The authorization code flow with PKCE is the recommended authorization flow if you’re implementing authorization in a mobile app, single page web apps, or any other type of application where the client secret can’t be safely stored.
The implementation of the PKCE extension consists of the following steps:
This guide assumes that:
You can find an example app implementing Authorization Code flow with PKCE extension on GitHub in the web-api-examples repository.
Code VerifierThe PKCE authorization flow starts with the creation of a code verifier. According to the PKCE standard, a code verifier is a high-entropy cryptographic random string with a length between 43 and 128 characters (the longer the better). It can contain letters, digits, underscores, periods, hyphens, or tildes.
The code verifier could be implemented using the following JavaScript function:
_10
const generateRandomString = (length) => {
_10
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
_10
const values = crypto.getRandomValues(new Uint8Array(length));
_10
return values.reduce((acc, x) => acc + possible[x % possible.length], "");
_10
const codeVerifier = generateRandomString(64);
Code Challenge
Once the code verifier has been generated, we must transform (hash) it using the SHA256 algorithm. This is the value that will be sent within the user authorization request.
Let's use window.crypto.subtle.digest to generate the value using the SHA256 algorithm from the given data:
_10
const sha256 = async (plain) => {
_10
const encoder = new TextEncoder()
_10
const data = encoder.encode(plain)
_10
return window.crypto.subtle.digest('SHA-256', data)
Next, we will implement a function base64encode
that returns the base64
representation of the digest we just calculated with the sha256
function:
_10
const base64encode = (input) => {
_10
return btoa(String.fromCharCode(...new Uint8Array(input)))
_10
.replace(/\//g, '_');
Let's put all the pieces together to implement the code challenge generation:
_10
const hashed = await sha256(codeVerifier)
_10
const codeChallenge = base64encode(hashed);
To request authorization from the user, a GET
request must be made to the /authorize
endpoint. This request should include the same parameters as the authorization code flow, along with two additional parameters: code_challenge
and code_challenge_method
:
code
. redirect_uri Required The URI to redirect to after the user grants or denies permission. This URI needs to have been entered in the Redirect URI allowlist that you specified when you registered your application (See the app guide). The value of redirect_uri
here must exactly match one of the values you entered when you registered your application, including upper or lowercase, terminating slashes, and such. state Optional, but strongly recommended This provides protection against attacks such as cross-site request forgery. See RFC-6749. scope Optional A space-separated list of scopes. If no scopes are specified, authorization will be granted only to access publicly available information: that is, only information normally visible in the Spotify desktop, web, and mobile players. code_challenge_method Required Set to S256
. code_challenge Required Set to the code challenge that your app calculated in the previous step.
The code for requesting user authorization looks as follows:
_20
const clientId = 'YOUR_CLIENT_ID';
_20
const redirectUri = 'http://127.0.0.1:8080';
_20
const scope = 'user-read-private user-read-email';
_20
const authUrl = new URL("https://accounts.spotify.com/authorize")
_20
// generated in the previous step
_20
window.localStorage.setItem('code_verifier', codeVerifier);
_20
response_type: 'code',
_20
code_challenge_method: 'S256',
_20
code_challenge: codeChallenge,
_20
redirect_uri: redirectUri,
_20
authUrl.search = new URLSearchParams(params).toString();
_20
window.location.href = authUrl.toString();
The app generates a PKCE code challenge and redirects to the Spotify authorization server login page by updating the window.location
object value. This allows the user to grant permissions to our application
Please note that the code verifier value is stored locally using the localStorage
JavaScript property for use in the next step of the authorization flow.
If the user accepts the requested permissions, the OAuth service redirects the user back to the URL specified in the redirect_uri
field. This callback contains two query parameters within the URL:
state
parameter supplied in the request.
We must then parse the URL to retrieve the code
parameter:
_10
const urlParams = new URLSearchParams(window.location.search);
_10
let code = urlParams.get('code');
The code
will be necessary to request the access token in the next step.
If the user does not accept your request or if an error has occurred, the response query string contains the following parameters:
Query Parameter Value error The reason authorization failed, for example: "access_denied" state The value of thestate
parameter supplied in the request.
Request an access token
After the user accepts the authorization request of the previous step, we can exchange the authorization code for an access token. We must send a POST
request to the /api/token
endpoint with the following parameters:
authorization_code
. code Required The authorization code returned from the previous request. redirect_uri Required This parameter is used for validation only (there is no actual redirection). The value of this parameter must exactly match the value of redirect_uri
supplied when requesting the authorization code. client_id Required The client ID for your app, available from the developer dashboard. code_verifier Required The value of this parameter must match the value of the code_verifier
that your app generated in the previous step.
The request must include the following HTTP header:
Header Parameter Relevance Value Content-Type Required Set toapplication/x-www-form-urlencoded
.
The request of the token could be implemented with the following JavaScript function:
_25
const getToken = async code => {
_25
// stored in the previous step
_25
const codeVerifier = localStorage.getItem('code_verifier');
_25
const url = "https://accounts.spotify.com/api/token";
_25
'Content-Type': 'application/x-www-form-urlencoded',
_25
body: new URLSearchParams({
_25
grant_type: 'authorization_code',
_25
redirect_uri: redirectUri,
_25
code_verifier: codeVerifier,
_25
const body = await fetch(url, payload);
_25
const response = await body.json();
_25
localStorage.setItem('access_token', response.access_token);
Response
On success, the response will have a 200 OK
status and the following JSON data in the response body:
access_token
expires_in int The time period (in seconds) for which the access token is valid. refresh_token string See refreshing tokens.
What's next?
Great! We have the access token. Now you might be wondering: what do I do with it? Take a look at to the access token guide to learn how to make an API call using your new fresh access token.
If your access token has expired, you can learn how to issue a new one without requiring users to reauthorize your application by reading the refresh token guide.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4