ขั้นตอนการให้สิทธิ์ Spotify PKCE ส่งคืน“ code_verifier ไม่ถูกต้อง”

Aug 19 2020

ฉันทำตามคู่มือการตรวจสอบสิทธิ์ของSpotify APIเพื่อตรวจสอบสิทธิ์แอปของฉันโดยใช้ PKCE

ณ ตอนนี้ฉันกำลังใช้โปรแกรมตรวจสอบโค้ดจำลองที่มีความท้าทายที่คำนวณไว้ล่วงหน้าสำหรับการดีบัก ค่าเหล่านี้คำนวณโดยใช้เครื่องมือออนไลน์หลายตัว ( SHA256 , SHA256 , base64url , base64url ) และจับคู่ค่าที่ส่งคืนจากฟังก์ชันการแฮช / การเข้ารหัสที่ฉันเขียนใน Swift อย่าลังเลที่จะใช้ลิงก์เหล่านี้ด้านบนเพื่อยืนยันสิ่งเหล่านี้

let verifier = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
let challenge = "66d34fba71f8f450f7e45598853e53bfc23bbd129027cbb131a2f4ffd7878cd0"
let challengeBase64URL = "NjZkMzRmYmE3MWY4ZjQ1MGY3ZTQ1NTk4ODUzZTUzYmZjMjNiYmQxMjkwMjdjYmIxMzFhMmY0ZmZkNzg3OGNkMA"

ฉันใช้ ASWebAuthenticationSession เพื่อสร้างคำขอเริ่มต้นของฉันในขั้นตอนที่ 2 ดังนี้:

var components = URLComponents()
components.scheme = "https"
components.host = "accounts.spotify.com"
components.path = "/authorize"
components.queryItems = [
    URLQueryItem(name: "client_id", value: SpotifyClientID),
    URLQueryItem(name: "response_type", value: "code"),
    URLQueryItem(name: "redirect_uri", value: SpotifyRedirectURL.absoluteString),
    URLQueryItem(name: "code_challenge_method", value: "S256"),
    URLQueryItem(name: "code_challenge", value: challenge),
    URLQueryItem(name: "state", value: "testing-state"),
    URLQueryItem(name: "scope", value: "user-follow-read")
]
let urlString = components.url!.absoluteString

guard let authURL = URL(string: urlString) else { return }
print(authURL)
let authSession = ASWebAuthenticationSession(url: authURL, callbackURLScheme: callbackScheme, completionHandler: handleLoginResponse)
authSession.presentationContextProvider = self
authSession.prefersEphemeralWebBrowserSession = true
authSession.start()

ในhandleLoginResponseฉันแยกวิเคราะห์การตอบสนองในขั้นตอนที่ 3 และทำการร้องขอเครือข่ายสำหรับขั้นตอนที่ 4 โดยใช้ Alamofire:

guard let items = URLComponents(string: callbackURL?.absoluteString ?? "").queryItems else { return }
let authCode = items[0].value!
let endpoint = "https://accounts.spotify.com/api/token"

let headers = HTTPHeaders(["Content-Type": "application/x-www-form-urlencoded"])
let parameters: [String: String] = [
    "client_id": SpotifyClientID,
    "grant_type": "authorization_code",
    "code": authCode,
    "redirect_uri": SpotifyRedirectURL.absoluteString,
    "code_verifier": verifier!
]
AF.request(endpoint,
           method: .post,
           parameters: parameters,
           encoder: URLEncodedFormParameterEncoder.default,
           headers: headers
).cURLDescription() { description in
    print(description)
}
.responseJSON() { (json) in
    print(json)
}

Alamofire สร้างอินเทอร์เฟซเพื่อสร้างคำขอ cURL จากภายใน Swift และการโทรcURLDescription()ช่วยให้ฉันเห็นว่าคำสั่ง cURL ที่แท้จริงคืออะไร:

$ curl -v \
    -X POST \
    -b "__Host-device_id=AQBHyRKdulrPJU6vY5xlua1xKOZBtBZVcrW9IK-X0LQ_MPj5x3N4mZkF4OzgLMdQwviWUxJ2dY6d49d0QpjG0ayFtCfrhwzG5-g" \
    -H "User-Agent: SpotifyUserGraph/1.0 (hl999.SpotifyUserGraph; build:1; iOS 14.0.0) Alamofire/5.1.0" \
    -H "Accept-Encoding: br;q=1.0, gzip;q=0.9, deflate;q=0.8" \
    -H "Accept-Language: en-US;q=1.0, zh-Hans-US;q=0.9, ko-US;q=0.8" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "client_id=e11fb810282946569aab8f89e52f78d5&code=AQC3Lm3KDPFCg3mBjSAiXMyvjdn5GvUJCjjCTQzPhAFe5mLntAHcAeiEufXcCv3Jne2qn345MZxBNiCggO-35mn6AAFsjRlm5lPynyC6clWABSzBK1OdWIynTlf0CiyR8vWYeO54GHHEXBSzj6URKWnAiXuxTUV6n1Axra6Oet8FY6-0jwU0CNGMaB91q1JFXlyl5J9JvrRtrP3s2Ef8Xb5A7gcCzqW6RHRzO0--BKiPHFnprK0SitiLxi-md2aaMnS2aHsRTqvc_NfFcuRpFR05WmSm6Gvkk_9trSBqRvVZYuGs-Ap3-ydVGk7BCqNc3lpbh4Jku6W_930fOg9kI__zRA&code_verifier=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&grant_type=authorization_code&redirect_uri=hl999-spotifyusergraph%3A//spotify-login-callback" \
    "https://accounts.spotify.com/api/token"

มันอ่านยากนิดหน่อย แต่ฉันค่อนข้างมั่นใจว่าคำขอนั้นถูกต้อง

อย่างไรก็ตามในขั้นตอนที่ 4 ฉันมักจะได้รับข้อความแสดงข้อผิดพลาดนี้จากเซิร์ฟเวอร์:

error = "invalid_grant";
"error_description" = "code_verifier was incorrect";

ฉันได้ลองทำหลาย ๆ อย่างเป็นเวลาหลายชั่วโมงแล้ว แต่ก็ยังคิดไม่ออก คำแนะนำใด ๆ จะได้รับการชื่นชมเป็นอย่างมาก ขอบคุณ!

คำตอบ

2 Sn0w0dDev Sep 17 2020 at 01:55

ปัญหาของคุณคือไบต์ดิบของแฮช SHA เป็นสิ่งที่ต้องใช้ base64ed ฉันกำลังทำงานกับแอพที่ใช้ Alamofire และ Spotify PKCE และมีปัญหากับการท้าทายโค้ด สิ่งที่ฉันทำคือใช้รหัสบางส่วนจากเอกสาร Auth0ที่เขียนขึ้นสำหรับ Swift 3 และแก้ไขให้ทำงานกับ Swift 5:

import Foundation
import CommonCrypto
 
func challenge(verifier: String) -> String {
    
    guard let verifierData = verifier.data(using: String.Encoding.utf8) else { return "error" }
        var buffer = [UInt8](repeating: 0, count:Int(CC_SHA256_DIGEST_LENGTH))
 
        verifierData.withUnsafeBytes {
            CC_SHA256($0.baseAddress, CC_LONG(verifierData.count), &buffer)
        }
    let hash = Data(_: buffer)
    print(hash)
    let challenge = hash.base64EncodedData()
    return String(decoding: challenge, as: UTF8.self)
        .replacingOccurrences(of: "+", with: "-")
        .replacingOccurrences(of: "/", with: "_")
        .replacingOccurrences(of: "=", with: "")
        .trimmingCharacters(in: .whitespaces)
}
print(challenge(verifier: "ExampleVerifier"))

หวังว่านี่จะช่วยและโชคดีสำหรับคุณ!

GaryArcher Aug 19 2020 at 18:27

เมื่อฉันใช้โปรแกรมยืนยัน PKCE ออนไลน์นี้ฉันมีความท้าทายที่แตกต่างออกไปสำหรับผู้ยืนยันดังนั้นฉันอาจลองใช้ค่าที่เครื่องมือให้คุณอีกครั้ง

ความปลอดภัยโดยรวม

ฉันจะใช้ไลบรารีความปลอดภัยที่ได้รับการยอมรับเช่น AppAuth เพื่อที่คุณจะได้ไม่ต้องเขียนรหัสความปลอดภัยด้วยตัวเอง ไลบรารีช่วยให้เราหลีกเลี่ยงข้อผิดพลาดที่อาจเกิดขึ้นและเราจะได้รับคุณลักษณะด้านความปลอดภัยใหม่ ๆ ฟรีในอนาคต

การรวมแอปพลิเคชัน

หากสนใจแนวทางนี้ขั้นตอนและทรัพยากรเหล่านี้ของฉันอาจมีประโยชน์:

  • เรียกใช้ตัวอย่าง AppAuth
  • จากนั้นอัปเดตการกำหนดค่าของคุณเพื่อให้แน่ใจว่าใช้งานได้กับ Spotify
  • ตัวอย่าง AppAuth ขั้นสูงครอบคลุมพื้นที่ที่มีรายละเอียดเพิ่มเติมเพื่อกำจัดปัญหา OAuth ทั่วไปบนอุปกรณ์เคลื่อนที่

รหัส

นี่คือรหัส Swift ของฉันที่รวมไลบรารีเข้าด้วยกันและไม่จำเป็นต้องมีการจัดการ PKCE ระดับต่ำ