Getting a Refresh Token from NextAuth Okta Provider
Asked Answered
S

2

5

We have an existing application use NextAuth to communicate with Okta. This is all working fine, we've just added an API which needs the Okta Access Token, we've used the JWT callback in NextAuth to grab the access token from the account object, and add that to the NextAuth session.

This is all working fine, we can grab the access token from the session and pass that to the API and is all good, the API validates the access token and all is fine. But, we sometimes come back to the session and the call to the API fails because it can not validate the access token as it has expired.

I'm looking at the NextAuth documentation for rotating a refresh token and I see that Okta has an end-point for getting a new refresh token. That all seems to make sense, the problem is, looking at what the JWT callback in NextAuth returns, I don't get a refresh token, these are the props returned by the Okta provider. We get an access token, and an ID token (they do contain different values) but we don't get a refresh token returned.

I see reference to offline_access scope with regards to refresh tokens, do we need to set this in our call for the Okta provider? If so, how? Otherwise, has anyone managed to use the Okta Provider in NextAuth and fixed the expired access token issue?

{
  token: {
    name: ...,
    email: ...,
    picture: .,
    sub: ...
  },
  user: {
    id: ..,
    name: ...,
    email: ...,
    image: undefined
  },
  account: {
    provider: 'okta',
    type: 'oauth',
    providerAccountId: ...,
    token_type: 'Bearer',
    expires_at: ...,
    access_token: ...,
    scope: ...,
    id_token: ...
  },
  profile: {
    sub: ...,
    name: ...,
    email: ...,
    ver: ...,
    iss: ...,
    aud: ...,
    iat: ...,
    exp: ...,
    jti: ...,
    amr: [ ... ],
    idp: ...,
    preferred_username: ...,
    auth_time: ...,
    at_hash: ...
  },
  isNewUser: undefined
}
Sussex answered 4/8, 2022 at 14:27 Comment(1)
I’m facing the same issue. The refresh token is not a part of account objectAdaxial
A
6

You may add the scope like below ‘

OktaProvider({
  clientId: process.env.OKTA_CLIENT_ID,
  clientSecret: process.env.OKTA_CLIENT_SECRET,
  issuer: process.env.OKTA_ISSUER,
  idToken: true,
  exp: true,
  authorization: { 
    params: { 
      scope: "openid offline_access" 
    } 
  }
})
Adaxial answered 15/8, 2022 at 18:37 Comment(1)
Thank you, that seems to make sense - hopefully I'll get chance to try this later this week and will mark it as accepted answer if it works, thanks once again.Sussex
R
1

First off, thanks! I got this working with the snippet provided by Appu Rajendran but I wanted to add the logic that I also had to write into the jwt callback to refresh the access token once I had the refresh token.

async jwt({ token, account }: any) {
if (account) {
    token.accessToken = account.access_token;
    token.idToken = account.id_token;
    token.oktaId = account.providerAccountId;
    token.refreshToken = account.refresh_token;
}

var tokenParsed = JSON.parse(Buffer.from(token.idToken.split('.')[1], 'base64').toString());
const dateNowInSeconds = new Date().getTime() / 1000
const tokenIsNotExpired = dateNowInSeconds < tokenParsed.exp;
if (tokenIsNotExpired) {
    return token;
} else {
    const url = `${process.env.OKTA_DOMAIN}/v1/token`;
    const body = `grant_type=refresh_token&client_id=${process.env.OKTA_CLIENTID}&client_secret=${process.env.OKTA_CLIENTSECRET}&refresh_token=${token.refreshToken}`;
    const headers = {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Accept': 'application/json'
    };
    const response = await fetch(url, { method: 'POST', body, headers });
    const data = await response.json();
    if (data.error) {
        throw new Error("Unable to refresh token");
    }
    token.accessToken = data.access_token;
    token.idToken = data.id_token;
    token.oktaId = data.providerAccountId;
    token.refreshToken = data.refresh_token;

    return token;
}

},

I did a full writeup explaining the steps here: https://thetombomb.com/posts/next-okta-refresh-token. The article also includes more information on the scope property.

Remorseless answered 23/6, 2023 at 18:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.