Can you create your own JWT token from the server and not use the provided next-auth jwt? #2619
Replies: 3 comments 1 reply
-
Absolutely! This is how literally any of the OAuth providers work! 👍 They send their own access_token which is usually a JWT, accepted by their API endpoints. |
Beta Was this translation helpful? Give feedback.
-
Here is an example of how to encode and decode JWTs. There are probably other ways as well. Encode and decode import fs from 'fs';
import jwt from 'jsonwebtoken';
import { JWT, JWTDecodeParams } from 'next-auth/jwt';
import { join } from 'path';
import appRoot from 'app-root-path';
export type EncodeOptions = {
issuer?: string;
kid: string;
passphrase?: string;
};
export const jwtOptions: EncodeOptions = {
issuer: process.env.NEXTAUTH_URL,
kid: 'my-domain.v1'
};
const decode = async (
params?: JWTDecodeParams | undefined,
_encodeOptions?: EncodeOptions
): Promise<JWT> => {
if (!params?.token) return {};
const pubKey = fs.readFileSync(join(appRoot.path + '/server/certs', 'jwt.public.pem'), {
encoding: 'utf-8'
});
const decodedToken = jwt.verify(params.token, pubKey) as Record<string, never>;
return decodedToken;
};
const encode = async ({ token }: any, encodeOptions: EncodeOptions): Promise<string> => {
const tokenContents = {
user: token.user,
sub: token.user.id,
iat: Date.now() / 1000,
iss: encodeOptions.issuer
};
const signOptions: jwt.SignOptions = {
algorithm: 'RS256',
expiresIn: '3h',
keyid: encodeOptions.kid
};
const privKey = fs.readFileSync(join(appRoot.path + '/server/certs', 'jwt.private.key'), {
encoding: 'utf-8'
});
const encodedToken = jwt.sign(tokenContents, { key: privKey, passphrase: '' }, signOptions);
return encodedToken;
}; Generating private/public keys import { generateKeyPairSync } from 'crypto';
import { writeFileSync } from 'fs';
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
modulusLength: 4096,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
cipher: 'aes-256-cbc',
passphrase: ''
}
});
writeFileSync('jwt.private.key', privateKey);
writeFileSync('jwt.public.pem', publicKey); |
Beta Was this translation helpful? Give feedback.
-
I'm working on similar approach, custom credentials provider that delegates to backend-service. {
providers: [
Providers.Credentials({
....
authorize: async function (credentials, req) {
// POST backend-service/auth/token
return { accessToken, refreshToken };
}
})
],
jwt: {
async encode({token}) {
// return accessToken from backend-service as it is
return token.accessToken;
},
async decode({token}) {
// manually decode the accessToken or delegate to backend-service
const decodedJwt = await backend.profile(token.accessToken);
return {...decodedJwt, ...token}; // The trick here is to include `accessToken` for re-encoding.
}
},
callbacks: {
async jwt(token, user) {
return user || token; // user will be {accessToken, refreshToken} from `authorize` at signin, token will be decoded jwt from `jwt.decoded`, both should include accessToken information.
},
async session(session, jwt) {
// optionally, modify user session from
return {
...session,
user: { name: jwt.name, email: jwt.sub, scope: jwt.scope},
acessToken: jwt.accessToken // include accessToken if you ever need for calling backend-service
};
}
}
} However, I recommend to keep next-auth session-token as it is. just parse the accessToken into payload via {
providers: [
Providers.Credentials({
....
authorize: async function (credentials, req) {
// POST backend-service/auth/token
return { accessToken, refreshToken };
}
})
],
callbacks: {
async jwt(token, user) {
return user || token; // { accessToken, refreshToken }
},
async session(session, jwt /* { accessToken, refreshToken } */) {
// parse the accessToken into jwt payload
const decodedJwt = await backend.userProfile(jwt.accessToken);
return {
...session,
user: { name: decodedJwt.name, email: decodedJwt.sub, scope: decodedJwt.scope},
acessToken: jwt.accessToken // include accessToken if you ever need for calling backend-service
};
}
}
} // Though, I'm actually having issues with refresh token here |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Question 💬
I want to use my token that was created by the server instead of using the next-auth provided?
How to reproduce ☕️
None
Contributing 🙌🏽
Yes, I am willing to help answer this question in a PR
Beta Was this translation helpful? Give feedback.
All reactions