Le flux d'autorisation Spotify PKCE renvoie "code_verifier était incorrect"
J'ai suivi le guide d'authentification de l' API Spotify pour authentifier mon application à l'aide de PKCE.
À partir de maintenant, j'utilise un vérificateur de code factice avec un défi pré-calculé pour le débogage. Ces valeurs ont été calculées à l'aide de plusieurs outils en ligne ( SHA256 , SHA256 , base64url , base64url ) et correspondent aux valeurs renvoyées par les fonctions de hachage / codage que j'ai écrites en Swift. N'hésitez pas à utiliser les liens ci-dessus pour les vérifier.
let verifier = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
let challenge = "66d34fba71f8f450f7e45598853e53bfc23bbd129027cbb131a2f4ffd7878cd0"
let challengeBase64URL = "NjZkMzRmYmE3MWY4ZjQ1MGY3ZTQ1NTk4ODUzZTUzYmZjMjNiYmQxMjkwMjdjYmIxMzFhMmY0ZmZkNzg3OGNkMA"
J'utilise ASWebAuthenticationSession pour faire ma demande initiale à l'étape 2, comme ceci:
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()
Dans handleLoginResponse
, j'analyse la réponse à l'étape 3 et fais une demande réseau pour l'étape 4 à l'aide d'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 crée une interface pour faire des requêtes cURL à partir de Swift, et l'appel cURLDescription()
me permet de voir exactement ce que la commande cURL réelle finit par être:
$ 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"
C'est un peu difficile à lire, mais je suis sûr que la demande est faite correctement.
Cependant, à l'étape 4, je reçois toujours ce message d'erreur du serveur:
error = "invalid_grant";
"error_description" = "code_verifier was incorrect";
J'ai essayé beaucoup de choses au cours de plusieurs heures et je n'arrive toujours pas à comprendre. Tous les pointeurs seraient très appréciés. Merci!
Réponses
Votre problème est que les octets bruts du hachage SHA sont ce qui doit être base64. Je travaille également sur une application qui utilise Alamofire et Spotify PKCE et j'ai eu des problèmes avec le défi du code. Ce que j'ai fait, c'est d'utiliser du code de la documentation Auth0 qui a été écrit pour Swift 3 et je l'ai modifié pour fonctionner avec 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"))
J'espère que cela aide et bonne chance à vous!
Lorsque j'ai utilisé ce vérificateur PKCE en ligne, j'ai eu un défi différent pour le vérificateur, donc je réessayerais peut-être avec les valeurs que cet outil vous donne.
SÉCURITÉ GLOBALE
J'utiliserais une bibliothèque de sécurité respectée telle qu'AppAuth, afin que vous n'ayez pas besoin de coder la sécurité vous-même. Les bibliothèques nous aident à éviter les erreurs potentielles et nous obtenons de nouvelles fonctionnalités de sécurité gratuitement à l'avenir.
INTÉGRATION APPAUTH
Si vous êtes intéressé par cette approche, ces étapes et ressources pourraient s'avérer utiles:
- Exécution de l'exemple AppAuth
- Ensuite, mettez à jour votre configuration pour vous assurer qu'elle fonctionne avec Spotify
- L'exemple Advanced AppAuth couvre des domaines plus détaillés pour surmonter les problèmes OAuth courants sur les mobiles
CODE
Voici un de mes codes Swift qui intègre les bibliothèques, et aucune gestion PKCE de bas niveau n'est nécessaire.