# Authorization Code Flow with PKCE

**URL:** <https://devforum.okta.com/t/authorization-code-flow-with-pkce/5725>\
**Category:** Questions\
**Tags:** angular\
**Created:** [July 21, 2019, 7:51am UTC](https://devforum.okta.com/t/authorization-code-flow-with-pkce/5725 "2019-07-21T07:51:35Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Robinyo](https://avatars.discourse-cdn.com/v4/letter/r/dec6dc/32.png) [@Robinyo](https://devforum.okta.com/u/Robinyo)\
**Post date:** [July 21, 2019, 7:51am UTC](https://devforum.okta.com/t/authorization-code-flow-with-pkce/5725/1 "2019-07-21T07:51:35Z")

</div>

I have created a [fork](https://github.com/Robinyo/serendipity/tree/master/projects/okta-angular) of the **okta-angular** library in order to implement support for the Authorization Code Flow with PKCE.

The redirect to the `/authorize` endpoint works as expected:

```auto
  async authorizationCodeRedirect() {

    const url = this.auth.issuer + '/v1/authorize'
      + '?response_type=' + encodeURIComponent(this.auth.responseType)
      + '&client_id=' + encodeURIComponent(this.auth.clientId)
      + '&state=' + encodeURIComponent(this.auth.state)
      + '&scope=' + encodeURIComponent(this.auth.scope)
      + '&redirect_uri=' + encodeURIComponent(this.auth.redirectUri)
      + '&code_challenge=' + encodeURIComponent(this.auth.code_challenge)
      + '&code_challenge_method=' + encodeURIComponent(this.auth.code_challenge_method);

    this.document.location.href = url;
  }

```

 ![07](https://global.discourse-cdn.com/oktadev/original/2X/a/aa7840c1a41e31af097a6b324861a7ca3a0a35cf.png)

As does the POST to the /token endpoint:

```auto
  async handleAuthorizationCodeFlow() {

    const params = new URLSearchParams(this.document.location.search.substring(1));
    const code = params.get('code');
    const state = params.get('state');

    const endpoint = this.auth.issuer + '/v1/token';

    const httpOptions = {
      headers: new HttpHeaders({
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
      })
    };

    const body = {
      grant_type: 'authorization_code',
      client_id: this.auth.clientId,
      redirect_uri: this.auth.redirectUri,
      code: code,
      code_verifier: 'M25iVXpKU3puUjFaYWg3T1NDTDQtcW1ROUY5YXlwalNoc0hhakxifmZHag'
    };

    const urlEncoded = Object.keys(body).map(key => key + '=' + body[key]).join('&');

    const response = await this.http.post<any>(endpoint, urlEncoded, httpOptions).toPromise();

    ...

  }

```

The response is as expected:

```auto
{
    "token_type": "Bearer",
    "expires_in": 3600,
    "access_token": "eyJraWQiOiJCMHcxTjV ...",
    "scope": "openid email groups profile address phone",
    "id_token": "eyJraWQiOiJCMHcx ..."
}

```

However, if I try to add the tokens to the token manager:

```auto
    ...

    if (response.id_token) {
      this.oktaAuth.tokenManager.add('idToken', response.id_token);
    }

    if (response.access_token) {
      this.oktaAuth.tokenManager.add('accessToken', response.access_token);
    }

```

I receive the following error:

```auto
    // ERROR Error: Uncaught (in promise): AuthSdkError: Token must be an Object with scopes, expiresAt, and an idToken or
    // accessToken properties

```

 ![44](https://global.discourse-cdn.com/oktadev/original/2X/d/d575867b3a88a81a98dd08932256014a33de1044.png)

---

<div class="post-metadata">

**Author:** ![micah.silverman](https://sea1.discourse-cdn.com/oktadev/user_avatar/devforum.okta.com/micah.silverman/32/1239_2.png) [@micah.silverman](https://devforum.okta.com/u/micah.silverman)\
**Post date:** [July 21, 2019, 8:47pm UTC](https://devforum.okta.com/t/authorization-code-flow-with-pkce/5725/2 "2019-07-21T20:47:14Z")

</div>

We’re working on building direct support for the auth code + PKCE flow into the okta-angular library.

In the meantime, the okta-auth-js library (which the angular library depends on) already has this support built in AND it does the heavy lifting for you.

Check out this repo: [https://github.com/dogeared/okta-auth-js-pkce-example](https://github.com/dogeared/okta-auth-js-pkce-example). In particular: [https://github.com/dogeared/okta-auth-js-pkce-example/blob/master/src/auth/index.js](https://github.com/dogeared/okta-auth-js-pkce-example/blob/master/src/auth/index.js)

The important bits are the setup:

```javascript
const oktaAuth = new OktaAuth({
    issuer: ISSUER,
    clientId: CLIENT_ID,
    redirectUri: REDIRECT_URL,
    grantType: 'authorization_code'
});

```

Notice the `grantType` is `authorization_code`

the login request:

```javascript
export async function loginOkta() {
    oktaAuth.token.getWithRedirect({
        responseType: 'code',
        scopes: ['openid', 'profile', 'email'],
    });
}

```

Notice the `responseType` is `code`

and the redirect handler:

```javascript
export async function redirect() {
    oktaAuth.token.parseFromUrl()
    .then((tokens) => {
        tokens.forEach((token) => {
            if (token.idToken) {
                oktaAuth.tokenManager.add('id_token', token);
            } else if (token.accessToken) {
                oktaAuth.tokenManager.add('access_token', token);
            }
        });
        router.push('/profile');
    })
    .catch(console.error);
}

```

the `parseFromUrl` function does two things:

1. detects that there’s a code in the url (as opposed to the implicit flow, where there would be tokens in the url)
2. creates the POST request to the /token endpoint and executes it

`parseFromUrl` returns a promise which is resolved in the `then()` function.

It iterates over the passed in `tokens` and makes the appropriate call to the `tokenManager.add` function:

```javascript
oktaAuth.tokenManager.add('id_token', token);

```

Notice that what’s stored in the `tokenManager` is NOT the raw jwt, but rather an object that includes the jwt.

That’s why you were getting the error you posted above.

HTH!

---

<div class="post-metadata">

**Author:** ![Robinyo](https://avatars.discourse-cdn.com/v4/letter/r/dec6dc/32.png) [@Robinyo](https://devforum.okta.com/u/Robinyo)\
**Post date:** [July 22, 2019, 2:57am UTC](https://devforum.okta.com/t/authorization-code-flow-with-pkce/5725/3 "2019-07-22T02:57:39Z")

</div>

I tried to follow the approach/style adopted by the **okta-angular** library.

My [fork](https://github.com/Robinyo/serendipity/tree/master/projects/okta-angular) of the okta-angular library is working now:

```auto
  async exchangeCodeForToken(): Promise<void> {

    const params = new URLSearchParams(this.document.location.search.substring(1));
    const code = params.get('code');
    const state = params.get('state');

    const endpoint = this.auth.issuer + '/v1/token';

    const httpOptions = {
      headers: new HttpHeaders({
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
      })
    };

    const body = {
      grant_type: 'authorization_code',
      client_id: this.auth.clientId,
      redirect_uri: this.auth.redirectUri,
      code: code,
      code_verifier: 'M25iVXpKU3puUjFaYWg3T1NDTDQtcW1ROUY5YXlwalNoc0hhakxifmZHag'
    };

    const urlEncoded = Object.keys(body).map(key => key + '=' + body[key]).join('&');

    return this.http.post<any>(endpoint, urlEncoded, httpOptions).toPromise();
  }

  async handleAuthorizationCodeFlow(): Promise<void> {

    const res = await this.exchangeCodeForToken();

    if (res['access_token']) {

      this.oktaAuth.tokenManager.add('accessToken', {
        accessToken: res['access_token'],
        expiresAt: Number(res['expires_in']) + Math.floor(Date.now() / 1000),
        tokenType: res['token_type'],
        scopes: res['scope']
      });

    }

    if (res['id_token']) {

      const jwt = this.oktaAuth.token.decode(res['id_token']);

      this.oktaAuth.tokenManager.add('idToken', {
        clientId: this.auth.clientId,
        idToken: res['id_token'],
        expiresAt: jwt.payload.exp,
        scopes: res['scope'],
        claims: jwt.payload
      });

    }

    if (await this.isAuthenticated()) {
      this.emitAuthenticationState(true);
    }

    const fromUri = this.getFromUri();
    this.router.navigate([fromUri.uri], fromUri.extras);
  }

```

I still need to add support for:

- create a random ‘state’ value
- create a PKCE code\_verifier (the plaintext random secret)
- create the code\_challenge
- verify the ‘state’ value

---

<div class="post-metadata">

**Author:** ![micah.silverman](https://sea1.discourse-cdn.com/oktadev/user_avatar/devforum.okta.com/micah.silverman/32/1239_2.png) [@micah.silverman](https://devforum.okta.com/u/micah.silverman)\
**Post date:** [July 22, 2019, 12:31pm UTC](https://devforum.okta.com/t/authorization-code-flow-with-pkce/5725/4 "2019-07-22T12:31:12Z")

</div>

You’re doing a lot of heavy lifting that’s already built into the okta-auth-js library, which the okta-angular library depends on.

If you really want to deal with the code\_verifier yourself, take a look at the spec. It just had to be between 43 and 128 characters.

---

<div class="post-metadata">

**Author:** ![Robinyo](https://avatars.discourse-cdn.com/v4/letter/r/dec6dc/32.png) [@Robinyo](https://devforum.okta.com/u/Robinyo)\
**Post date:** [July 23, 2019, 12:46am UTC](https://devforum.okta.com/t/authorization-code-flow-with-pkce/5725/5 "2019-07-23T00:46:47Z")

</div>

It was never my intention to do any heavy lifting 🙂

My first exposure to Okta was via the Angular Quick Start guide (see my [feedback](https://devforum.okta.com/t/angular-quick-start-guide/5570)).

Then I set up some groups (see my [feedback](https://devforum.okta.com/t/no-groups-scope-in-the-console-for-the-default-authorisation-server/55730)).

Then I got the Implicit Flow working using the **okta-angular** library (and everything that goes along with that e.g., an AuthService, an AuthGuard, a http-interceptor, etc.).

I read Aaron Parecki’s [post](https://developer.okta.com/blog/2019/05/01/is-the-oauth-implicit-flow-dead) and then compared his sample code to the code in the **okta-angular** library (which wraps the okta-auth-js library) that handles the Implicit Flow.

Aaron’s sample includes the source code for `base64urlencode`, `generateRandomString`, `pkceChallengeFromVerifier` and `sha256`.

I managed to create a working [implementation](https://github.com/Robinyo/serendipity/blob/master/projects/okta-angular/src/lib/services/auth/okta-auth.service.ts) for the Authorization Code flow (albeit with hard coded values for ‘code\_challenge’ and ‘code\_verifier’).

I’ve just had a quick look at the [token.js](https://github.com/okta/okta-auth-js/blob/master/lib/token.js) source and the [pkce](https://github.com/okta/okta-auth-js/blob/master/lib/pkce.js) utils library. And, I can see that you have added support for ‘state’, ‘codeChallenge’ and ‘codeVerifier’.

So based on the [okta-auth-js-pkce-example](https://github.com/dogeared/okta-auth-js-pkce-example) I added a call to [getWithRedirect()](https://github.com/Robinyo/serendipity/blob/6d5078c57113b5484445eb2e1c767bbfe55d17e0/projects/okta-angular/src/lib/services/auth/okta-auth.service.ts#L194):

```auto
  loginWithRedirect() {

    this.oktaAuth.token.getWithRedirect({
      responseType: 'code',
      scopes: ['openid', 'profile', 'email', 'phone', 'address', 'groups'],
    });

  }

```

But without any success as yet …

---

<div class="post-metadata">

**Author:** ![Robinyo](https://avatars.discourse-cdn.com/v4/letter/r/dec6dc/32.png) [@Robinyo](https://devforum.okta.com/u/Robinyo)\
**Post date:** [July 27, 2019, 8:19am UTC](https://devforum.okta.com/t/authorization-code-flow-with-pkce/5725/6 "2019-07-27T08:19:36Z")

</div>

Sorted 🙂

See: [Okta (Angular) Authentication (AuthN) library](https://github.com/Robinyo/serendipity/tree/master/projects/auth-okta)

---

<div class="post-metadata">

**Author:** ![system](https://global.discourse-cdn.com/oktadev/original/1X/589c4c5f80de422d01f47cee7185a7fb8227835c.png) [@system](https://devforum.okta.com/u/system)\
**Post date:** [January 17, 2024, 11:50pm UTC](https://devforum.okta.com/t/authorization-code-flow-with-pkce/5725/7 "2024-01-17T23:50:41Z")

</div>

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.
