Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update claims that stored in jwt #2366

Closed
dkleber89 opened this issue Jul 13, 2021 · 1 comment
Closed

Update claims that stored in jwt #2366

dkleber89 opened this issue Jul 13, 2021 · 1 comment
Labels
question Ask how to do something or how something works

Comments

@dkleber89
Copy link

Question 💬

Hi,

im storing some customernumbers that i receive from idp claims (identity server 4) in the jwt session cookie.

The user can change from the actual app to a "MyAccount" App to add more customernumbers ... these claims ar added to the claims from the idp (identity server 4)

Then he comes back to the actual app and there i cant find a way to update the customernumbers in the session cookie.

Have anyone a idea how i achive this?

How to reproduce ☕️

My actual [...nextauth].ts code:

import axios from 'axios';
import NextAuth from 'next-auth';
import { JWT } from 'next-auth/jwt';
import Providers from 'next-auth/providers';

const SCOPE = 'openid email offline_access profile user.sapdata user.portal_emobility';

const refreshToken = async (token: JWT): Promise<JWT> => {
  const params = new URLSearchParams();

  params.append('grant_type', 'refresh_token');
  params.append('refresh_token', token.refreshToken);
  params.append('client_id', process.env.CLIENT_ID);
  params.append('client_secret', process.env.CLIENT_SECRET);
  params.append('scope', SCOPE);

  try {
    const axiosResponse = await axios.post(`${process.env.IDENTITY_URL}connect/token`, params, {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
    });

    if (axiosResponse.status !== 200) {
      throw new Error(JSON.stringify(axiosResponse));
    }

    return {
      ...token,
      idToken: axiosResponse.data.id_token,
      accessToken: axiosResponse.data.access_token,
      accessTokenExpires: Date.now() + axiosResponse.data.expires_in * 1000,
      refreshToken: axiosResponse.data.refresh_token ?? token.refreshToken, // Fallback to old refresh token
    };
  } catch (error) {
    console.error(error);

    return {
      ...token,
      error: 'RefreshAccessTokenError',
    };
  }
};

export default NextAuth({
  providers: [
    Providers.IdentityServer4({
      id: 'idp',
      name: 'IdentityProvider',
      scope: SCOPE,
      domain: process.env.IDENTITY_URL,
      clientId: process.env.CLIENT_ID,
      clientSecret: process.env.CLIENT_SECRET,
      accessTokenUrl: `${process.env.IDENTITY_URL}connect/token`,
      authorizationUrl: `${process.env.IDENTITY_URL}connect/authorize?response_type=code`,
      profileUrl: `${process.env.IDENTITY_URL}connect/userinfo`,
      protection: ['pkce', 'state'],
    }),
  ],
  jwt: {
    secret: process.env.COOKIE_SECRET,
    signingKey: process.env.COOKIE_SIGNIN_KEY,
    encryption: true,
    encryptionKey: process.env.COOKIE_ENCRYPTION_KEY,
  },
  session: {
    maxAge: 1800,
  },
  cookies: {
    sessionToken: {
      name: `${process.env.PROJECT}SC`,
      options: {
        httpOnly: true,
        sameSite: 'lax',
        secure: process.env.NEXTAUTH_URL.startsWith('https://'),
        domain: process.env.COOKIE_DOMAIN,
      },
    },
  },
  callbacks: {
    jwt: async (token, user, account) => {
      const tempToken = token;

      if (account && user) {
        tempToken.idToken = account.idToken ?? '';
        tempToken.accessToken = account.accessToken ?? '';
        tempToken.refreshToken = account.refresh_token ?? '';

        tempToken.accessTokenExpires = account.expires_in ? Date.now() + account.expires_in * 1000 : 0;

        tempToken.customernumber = user.customernumber;

        return tempToken;
      }

      if (Date.now() < tempToken.accessTokenExpires) {
        return tempToken;
      }

      return refreshToken(tempToken);
    },
    session: async (session, token) => {
      const tempToken = token as JWT;
      const tempSession = session;

      tempSession.accessToken = tempToken.accessToken;
      tempSession.customernumber = tempToken.customernumber;

      return tempSession;
    },
  },
  debug: process.env.NODE_ENV === 'development',
});

Contributing 🙌🏽

Yes, I am willing to help answer this question in a PR

@dkleber89 dkleber89 added the question Ask how to do something or how something works label Jul 13, 2021
@balazsorban44
Copy link
Member

balazsorban44 commented Jul 13, 2021

no, in case of jwt persisted sessions, I think currently the only way is to reauthenticate the user.

see #2269 for a discussion on that topic.

I can think of ways though. Maybe the jwt callback could be extended to periodically check the provider's userinfo endpoint. but it still wouldn't be instant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Ask how to do something or how something works
Projects
None yet
Development

No branches or pull requests

2 participants