Skip to content Skip to sidebar Skip to footer

How To Impement Spotify's Authorization Code With Pkce In Kotlin

So I want to use Spotify's Web API. I have read some documentation that you' ll need to implement an Authentication code with PKCE. I am not 100% sure on how to do that and can use

Solution 1:

One of the approaches would be to use the Spotify authorization library. Before starting add the following dependency to Your Android project:

// Spotify authorization
implementation 'com.spotify.android:auth:1.2.5'

And then start coding following the steps from the Authorization Guide:

1. Create the code verifier and challenge

This helpful article helps to handle the initial cryptographic part of the authorization flow. You can give it a quick read.

The first step in coding would be to create a companion object in which we will store things like the CLIENT_ID or the code verifier and code challenge:

companionobject {
        constval CLIENT_ID = "your_client_id"constval REDIRECT_URI = "https://com.company.app/callback"val CODE_VERIFIER = getCodeVerifier()

        privatefungetCodeVerifier(): String {
            val secureRandom = SecureRandom()
            val code = ByteArray(64)
            secureRandom.nextBytes(code)
            return Base64.encodeToString(
                code,
                Base64.URL_SAFE or Base64.NO_WRAP or Base64.NO_PADDING
            )
        }

        fungetCodeChallenge(verifier: String): String {
            val bytes = verifier.toByteArray()
            val messageDigest = MessageDigest.getInstance("SHA-256")
            messageDigest.update(bytes, 0, bytes.size)
            val digest = messageDigest.digest()
            return Base64.encodeToString(
                digest,
                Base64.URL_SAFE or Base64.NO_WRAP or Base64.NO_PADDING
            )
        }
    }

2. Construct the authorization URI

This step boils down to using the AuthorizationRequest.Builder and AuthorizationClient to create an intent for the Spotify Authentication activity.

There You will provide all the necessary parameters from the guide:

fungetLoginActivityCodeIntent(): Intent =
        AuthorizationClient.createLoginActivityIntent(
            activity,
            AuthorizationRequest.Builder(CLIENT_ID, AuthorizationResponse.Type.CODE, REDIRECT_URI)
                .setScopes(
                    arrayOf(
                        "user-library-read", "user-library-modify",
                        "app-remote-control", "user-read-currently-playing"
                    )
                )
                .setCustomParam("code_challenge_method", "S256")
                .setCustomParam("code_challenge", getCodeChallenge(CODE_VERIFIER))
                .build()
        )

3. Your app redirects the user to the authorization URI

Here you can register a callback for the result of the authorization activity that will use the intent that we have created in the previous step:

privateval showLoginActivityCode = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { result: ActivityResult ->

        val authorizationResponse = AuthorizationClient.getResponse(result.resultCode, result.data)

        when (authorizationResponse.type) {
            AuthorizationResponse.Type.CODE ->
                // Here You will get the authorization code which you// can get with authorizationResponse.code
            AuthorizationResponse.Type.ERROR ->
                // Handle the Errorelse ->
                // Probably interruption
        }
    }


// Usage:
showLoginActivityCode.launch(getLoginActivityCodeIntent())

In there You will have access to the authorization code - authorizationResponse.code. It will be used in the next step.

4. Your app exchanges the code for an access token

Here we are going to have to create another intent for the Spotify Authentication activity. This is very similar to the code from the step 2. Here in the getLoginActivityTokenIntent You will have to provide the code that You have retrieved from the previous step:

fungetLoginActivityTokenIntent(code: String): Intent =
        AuthorizationClient.createLoginActivityIntent(
            activity,
            AuthorizationRequest.Builder(CLIENT_ID, AuthorizationResponse.Type.TOKEN, REDIRECT_URI)
                .setCustomParam("grant_type", "authorization_code")
                .setCustomParam("code", code)
                .setCustomParam("code_verifier", CODE_VERIFIER)
                .build()
        )

Then create the callback:

privateval showLoginActivityToken = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { result: ActivityResult ->

        val authorizationResponse = AuthorizationClient.getResponse(result.resultCode, result.data)

        when (authorizationResponse.type) {
            AuthorizationResponse.Type.TOKEN -> {
                // Here You can get access to the authorization token// with authorizationResponse.token
            }
            AuthorizationResponse.Type.ERROR ->
                // Handle Errorelse ->
                // Probably interruption
        }
    }


// Usage:
showLoginActivityToken.launch(getLoginActivityTokenIntent(authorizationCode))

Now here the authorization part comes to an end - You have got the access to the authorization token - authorizationResponse.token. Save it, it will be used in creating requests to the Spotify Web API.

5. Use the access token to access the Spotify Web API

You can start using the API. Simple preview example using Retrofit:

interfaceSpotifyApi{

    companionobject {
        constval BASE_URL = "https://api.spotify.com/v1/"
    }

    @GET("me")suspendfungetMe(@Header("Authorization") bearerWithToken: String): User
}

Note that the bearerWithToken parameter should look like this: "Bearer {your_access_token}".

Solution 2:

I think that the accepted solution actually is not an Authorization Code Flow with PKCE. I believe what's happening in @sweak's proposed solution is that at first you start Authorization Code Flow but then you abandon it and initiate new Implicit Grant Flow. That is why it does not support the refresh token as @sweak himself mentioned here.

The thing is, in order to exchange code for token in Authorization Code Flow (with or without PKCE) you should request /api/token endpoint, however Spotify Android SDK's login activity requests /authorize endpoint, which I guess simply ignores custom params such as grant_type.

I'm currently struggling to make PKCE work with Android SDK as well. I'll update the answer if I manage to do this.

Post a Comment for "How To Impement Spotify's Authorization Code With Pkce In Kotlin"