Auth-module: Supprting PKCE based login

Created on 4 Apr 2019  路  20Comments  路  Source: nuxt-community/auth-module

What problem does this feature solve?

The current implementation of auth module uses the implicit flow to get the access token, as per the current best practices in oauth2 PKCE based auth is a better approach that implicit, because of various reasons, but the most important would be the ability to get refresh token, and hence can allow long-lived session.

Why don't we support the PKCE? Any thoughts?

What does the proposed changes look like?

Haven't looked into yet, but if there are any feedback and suggestion I would like to contribute.

This feature request is available on Nuxt community (#c307)
enhancement

Most helpful comment

In case anyone is interested, setting up PKCE flow with refresh tokens for Auth0 provider was pretty easy thanks to @nuxt/auth-next.
I used the following config to get it working:

strategies: {
  local: false,
  auth0: {
    domain: process.env.AUTH0_DOMAIN,
    clientId: process.env.AUTH0_CLIENT_ID,
    scope: ['openid', 'profile', 'offline_access'],
    responseType: 'code',
    accessType: 'offline',
    grantType: 'authorization_code',
    codeChallengeMethod: 'S256',
  }
}

All 20 comments

I believe it's important to support this, especially for nuxt in spa and generate modes since those sites are currently vulnerable to interception of access tokens and/or have no way to securely store the client secret many endpoints require.

The IETF Oauth working group recommends PKCE flow as current best practice over implicit flow for public web apps:

Public browser-based apps MUST implement the Proof Key for Code Exchange (PKCE [RFC7636]) extension to OAuth, and authorization servers MUST support PKCE for such clients.

This is a big deal. Currently none of the oauth2 providers support authorization token refresh using implicit flow. PKCE with authorization code flow is needed to obtain refresh tokens and new authorization tokens. In the case of Amazon Cognito, implicit flow authorization tokens are only valid for one hour. This forces users to re-login after an hour.

As far as I can tell if the authentication server requires PKCE (either plain or S256 currently) then it requires the code_challenge parameter in the url or the authentication request is rejected. Period.

What happens is described quite well here: https://developer.okta.com/blog/2019/05/01/is-the-oauth-implicit-flow-dead

For login, this is very similar to how the nonce currently works, adding itself to the url under certain conditions.

if (this.options.audience) {
      opts.audience = this.options.audience
    }
// Set Nonce Value if response_type contains id_token to mitigate Replay Attacks
    // More Info: https://openid.net/specs/openid-connect-core-1_0.html#NonceNotes
    // More Info: https://tools.ietf.org/html/draft-ietf-oauth-v2-threatmodel-06#section-4.6.2
    if (opts.response_type.includes('id_token')) {
      // nanoid auto-generates an URL Friendly, unique Cryptographic string
      // Recommended by Auth0 on https://auth0.com/docs/api-auth/tutorials/nonce
      opts.nonce = nonce || nanoid()
    }
...
if (this.options.pkce_flow=='S256') {
      opts.code_challenge_method= this.options.pkce_flow
      opts.code_challenge = encodeURIComponent(code_challenge)
    }
...
const url = this.options.authorization_endpoint + '?' + encodeQuery(opts)

Even if it was generated on every initial auth request - it's not a breaking change as the server would just ignore it.

I wouldn't mind contributing to this effort as well in any capacity.

https://www.oauth.com/oauth2-servers/pkce/

Edit: This would be the linked pull request: https://github.com/nuxt-community/auth-module/pull/507

I'm glad to see this feature is being added in v5.

In the meantime, I'd like my app to use PKCE login. What is the best workaround to use PKCE with the Nuxt Auth module?

I am using nuxt generate in universal mode.

@tomturton If you're generateing it anyway, I'd suggest going with a client-side library that supports PKCE flow, like vuex-oidc.

Thanks, but I plan to use the Nuxt server in the future, so I'd rather use this module if possible.

Hi @tomturton! If you want, you can use the dev version in the meantime :)
We published a new package for dev @nuxtjs/auth-next

Please, check the docs for dev version because we made a lot of changes https://dev.auth.nuxtjs.org/

Hi, just wanted to confirm that I could get the authorization code grant flow with PKCE to work with both Azure and Okta as authentication provider, using the current @nuxtjs/auth-next, so you guys are definitely on the right track there.

I also wanted to pointed out that there still seems to be an issue with the refresh token flow, the actual POST to refresh the token is never executed because there's a promise inside a promise being returned, changing (in oauth2.js) :

const response = await this.$auth.request(this.name, {
            ...
        })

into

const response = this.$auth.requestWith(this.name, {
            ...
        })

seems to resolve that issue for me.

Hi @studiocredo! Thank you for your report! :)

I'm gonna work on a fix for this issue.

After some re-testing (and upgrading to the latest dev version "@nuxtjs/auth-next": "^5.0.0-1589738535.90a214b"), the token refresh also seems to work. The issue was (as often) in my own code.

After some re-testing (and upgrading to the latest dev version "@nuxtjs/auth-next": "^5.0.0-1589738535.90a214b"), the token refresh also seems to work. The issue was (as often) in my own code.

Hi @studiocredo, were you able to get Okta PKCE working with auth-next module? If so (first off, awesome stuff), what settings did you use for strategy and scheme? I did ours outside of auth and running into the refresh issue, but I'd prefer to use auth module as much as possible. Thanks!

Hi @hahnzus, this is the config I used to get the Oauth2 authorization code flow with PKCE working for Okta. Mind that my remark about getting refresh tokens to work only applied to Azure. For Okta, I haven't figured out yet how to get their API to return a refresh token. Once you get that, I assume the client side part in nuxt-auth should work as well.

{
    "clientId": "****",
    "token": {
        "property": "access_token",
        "type": "Bearer",
        "name": "Authorization",
        "maxAge": 1800,
        "global": true,
        "required": true
    },
    "state": "UNIQUE_AND_NON_GUESSABLE",
    "responseMode": "",
    "acrValues": "",
    "autoLogout": true,
    "refreshToken": {
        "property": "refresh_token",
        "maxAge": 2592000
    },
    "responseType": "code",
    "codeChallengeMethod": "S256",
    "scheme": "oauth2",
    "endpoints": {
        "authorization": "https://****.okta.com/oauth2/v1/authorize",
        "token": "https://****.okta.com/oauth2/v1/token",
        "userInfo": "https://****.okta.com/oauth2/v1/userinfo"
    },
    "grantType": "authorization_code",
    "scope": "openid profile email"
}

Hi @studiocredo,

Thanks a lot for this. You are my only hope for days ;-)

Like you, I want to get the Oauth2 authorization code flow with PKCE working for Okta (locally at a first step, and then hosted on Azure for prod). I'm struggling for days and I'm about to drop nuxt and go back to Vue classic only because of this.

I configured my nuxt.config.js like you did (just add an additionnal "logout" endpoint ( 'https://**.okta.com/oauth2/default/v1/logout'

I will probably ask a dummy question but how did you manage the configuration of the redirect (for login and call back) ? Because I'm redirected to /login page each time I want to access a protected page.

Thanks a lot.

Hello @studiocredo,

I think in order to get the refresh token you have to provide the "offline_access" scope.

In case anyone is interested, setting up PKCE flow with refresh tokens for Auth0 provider was pretty easy thanks to @nuxt/auth-next.
I used the following config to get it working:

strategies: {
  local: false,
  auth0: {
    domain: process.env.AUTH0_DOMAIN,
    clientId: process.env.AUTH0_CLIENT_ID,
    scope: ['openid', 'profile', 'offline_access'],
    responseType: 'code',
    accessType: 'offline',
    grantType: 'authorization_code',
    codeChallengeMethod: 'S256',
  }
}

Hi,

Is it possible to add an option property 'clientSecret'. I'm using django oauth-toolkit and it is requiring client_secret to get the access_token.

For front-end usage, using client sercret should be avoided as much as possible.

For front-end usage, using client sercret should be avoided as much as possible.

Yes, I know but django oauth-toolkit is requiring the client_secret to be passed. Maybe I'll just check if there is a workaround in django oauth-toolkit to not require the client_secret. Thanks.

PKCE is supported in v5, as mentioned above. I've created https://github.com/nuxt-community/auth-module/issues/919 to improve the docs about that. Glad you all got it working :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

pi0 picture pi0  路  3Comments

yuwacker picture yuwacker  路  3Comments

roosht3 picture roosht3  路  3Comments

essamamdani picture essamamdani  路  3Comments

ishitatsuyuki picture ishitatsuyuki  路  4Comments