Describe the bug
While authenticating a user using Google, I'm overriding the encode function with the following:
jwt: {
encode: async ({ secret, key, token, maxAge }) => {
const tokenContents = {
name: token.email,
"https://hasura.io/jwt/claims": {
"x-hasura-allowed-roles": ["admin", "user"],
"x-hasura-default-role": "user",
"x-hasura-user-id": "1",
},
iat: Date.now() / 1000,
exp: Math.floor(Date.now() / 1000) + 24 * 60 * 60,
sub: "1",
};
const signOptions = {
algorithm: "RS256",
};
const signedToken = jwt.sign(
tokenContents,
secret,
signOptions
);
return signedToken;
},
},
However, I'm getting the following error: [next-auth][error][jwt_session_error] JOSEAlgNotWhitelisted: alg not whitelisted. I tried figuring out if there is any way to whitelist the algorithm (RS256) that I'm using. But, I didn't find any such option.
To Reproduce
The process is defined above.
Include example code (or link to public repository) which can be used to reproduce the behaviour.
Expected behavior
A valid token should be returned.
Screenshots or error logs

Hi there!
Thanks for try out there features, I appreciate documentation for how to use them is quite minimal right now.
If you specify a custom encode function you also need a corresponding decode function (e.g. that calls jwt.verify() and returns either a token payload if the token valid, or null if there is no token or it has expired).
In v3, it is also possible to specify a custom signing algorithm so you can pass options instead of having to write the code to handle it, but this isn't documented yet. I'd be happy to write up a tutorial for this case though! We get quite a few related questions.
@iaincollins Thank you for your quick reply.
I was able to resolve this issue by following the steps that you had mentioned:
import jwt from "jsonwebtoken";
....
jwt: {
encode: async ({ token }) => {
const tokenContents = {
name: token.name,
email: token.email,
picture: token.picture,
"https://hasura.io/jwt/claims": {
"x-hasura-allowed-roles": ["admin", "user"],
"x-hasura-default-role": "user",
"x-hasura-user-id": "1",
},
iat: Date.now() / 1000,
exp: Math.floor(Date.now() / 1000) + 24 * 60 * 60,
sub: "1",
};
const signOptions = {
algorithm: "RS256",
};
const encodedToken = jwt.sign(
tokenContents,
secret,
signOptions
);
return encodedToken;
},
decode: async ({ token }) => {
const signOptions = {
algorithms: ["RS256"],
};
const decodedToken = jwt.verify(
token,
secret,
signOptions
);
return decodedToken;
},
},
However, while encoding the token, I need to know the id of the user which I'm unable to fetch. I was unable to fetch the userId by following this issue.
The session callback returns the following:
{
user: {
name: 'John Doe',
email: '[email protected]',
image: 'http://image.john.doe.com'
},
expires: '2020-09-25T12:27:51.776Z'
}
For now, I'm doing something like the following:
import jwt from "jsonwebtoken";
import { Client } from "pg";
....
const client = new Client({
connectionString: process.env.DATABASE_URL,
});
....
encode: async ({ token, secret }) => {
if (!client._connected) {
client.connect();
}
const result = await client.query(
"SELECT * FROM users WHERE email = $1",
[token.email]
);
const user = result.rows[0];
const tokenContents = {
name: token.name,
email: token.email,
picture: token.picture,
iat: Date.now() / 1000,
exp: Math.floor(Date.now() / 1000) + 24 * 60 * 60,
sub: user.id,
};
const encodedToken = jwt.sign(
tokenContents,
secret
);
return encodedToken;
},
This is working fine. However, I'm unsure if this is the correct way. There is an additional API call being done in this case to fetch the id of the user. Is it better if it's whitelisted after the session table is updating in the database?
Thanks! I think this highlights something missing from the token by default in v3.
The JWT callback back be used to add detail from a profile to a JWT, but I would agree the user ID should also be included in the token by default (in the sub claim, as you are doing here). I think this was overlooked when the JWT support was refactored.
I'll address that and push out an update in the next couple of days so that sub contains the user ID by default when a database is being used.
Any other feedback is much appreciated.
Was just running into this issue with the missing user ID (actually the complete user is missing on the token object in v3, i.e. token.user === undefined). I was relying on the code published in #366 which suddenly stopped working in v3:
callbacks: {
async session(session, token) {
// expose user id
return Promise.resolve({ ...session, user: { ...session.user, id: token.user.id } })
}
}
@iaincollins: very much looking forward to your fix and thanks for this awesome repo!
@styxlab Hi there!
To confirm the user object is not present in v3 by default, v3 uses JWT claims spec properties by default. You can still re-create a user object using the JWT callback if needed it, but the default is more compatible with third party software and helps save space, so has advantages.
The User ID was removed by default as the JWT is signed by default (with JWS) but is not longer encrypted by default and the aim is to not expose internal ID's by default.
Note: The token is wrapped with AES by default in v2. v3 still supports encryption with AES, but uses JWE standard. It only encrypted if encryption is explicitly enabled; this is a better default for most people.
We will provide a way to for the User ID to be in the token if using JWT with a database in v3.
Great. Just to clarify, I am using the MongoDB resolver and need a way to access the "_id" for the user object. I was using the method provided in #366, but if there is a better way to do this in v3, let me know.
This should be resolved with the new callbacks in version 3.0!
Most helpful comment
Thanks! I think this highlights something missing from the token by default in v3.
The JWT callback back be used to add detail from a profile to a JWT, but I would agree the user ID should also be included in the token by default (in the
subclaim, as you are doing here). I think this was overlooked when the JWT support was refactored.I'll address that and push out an update in the next couple of days so that
subcontains the user ID by default when a database is being used.Any other feedback is much appreciated.