Next-auth: How to resolve [next-auth][warn][jwt_auto_generated_signing_key]

Created on 26 Jul 2020  ·  22Comments  ·  Source: nextauthjs/next-auth

Your question
How can I resolve the [next-auth][warn][jwt_auto_generated_signing_key] warning?

I'm using the 3.0.0-beta.22

What are you trying to do
I'm using the credentials provider to try to allow for user/password logins. The login process works fine, but I'm getting this warning in the console.

I've added the options:

const options = {
  providers,
  adapter: Adapters.Prisma.Adapter({ prisma }),
  session: {
    jwt: true,
  },
  secret: process.env.SECRET,
  jwt: {
    secret: process.env.JWT_SECRET,
  },
};

How can I resolve this this warning?

Documentation feedback
The warning generates the link https://next-auth.js.org/warning#jwt_auto_generated_signing_key but this seems to generate an "Page not found" message.

  • [ ] Found the documentation helpful
  • [x] Found documentation but was incomplete
  • [x] Could not find relevant documentation
  • [ ] Found the example project helpful
  • [x] Did not find the example project helpful
documentation help wanted question

Most helpful comment

@timothymiller I couldn't find this documented, but I was able to add an encryptionKey prop in addition to signingKey.

The signingKey I generated per the docs like:

  • npx node-jose-tools newkey -s 256 -t oct -a HS512

I tried that for the encryptionKey, but got an error that it required the A256GCM algorithm, and so created it like so:

  • npx node-jose-tools newkey -s 256 -t oct -a A256GCM

which resulted in:

  jwt: {
    secret: jwtSecret,
    encryption: true,
    signingKey: '{"kty":"oct","kid":"<the-kid>","alg":"HS512","k":"<the-key>"}',
    encryptionKey: '{"kty":"oct","kid":"<the-kid>","alg":"A256GCM","k":"<the-key>"}',
  },

It appears to work as expected where changing the encryptionKey results in an error:

[next-auth][error][jwt_session_error] JWEDecryptionFailed: decryption operation failed

All 22 comments

Great question! This still needs documenting.

tl;dr version:

  • Option 1: Pass a pre-regenerated Private Key (and, optionally a Public Key) in the jwt options.
jwt: {
  signingKey: process.env.JWT_SIGNING_PRIVATE_KEY,

  // You can also specify a public key for verification if using public/private key (but private only is fine)
  // verificationKey: process.env.JWT_SIGNING_PUBLIC_KEY,

  // If you want to use some key format other than HS512 you can specify custom options to use
  // when verifying (note: verificationOptions should include a value for maxTokenAge as well).
  // verificationOptions = {
  //   maxTokenAge: `${maxAge}s`, // e.g. `${30 * 24 * 60 * 60}s` = 30 days
  //   algorithms: ['HS512']
  // },
}

You can use node-jose-tools to generate keys on the command line and set them as environment variables.

e.g. jose newkey -s 256 -t ec -a HS512

  • Option 2: Specify custom encode/decode functions on the jwt object.

This gives you complete control over signing / verification / etc.

EDIT: ^^ The command line example is wrong:

I think it should be key type oct for compatibility with the defaults in NextAuth.js

e.g. jose newkey -s 256 -t oct -a HS512

@iaincollins Thanks for your reply.

Sorry for the noob question, but I tried using node-jose-tools to generate the keys, but I'm really not sure how to interpret and use the resulting output.

I get a key in JWK format with values like "x", "y", and "d", but how do I get the public/private keys from this that are required by next-auth?

No problem - this definitely needs one or two tutorials and I'm sure this thread will be useful to other folks.

The entire response from node-jose-tools is actually a JSON Web Key (JWK).

_i.e. you should be able to copy and paste the entire thing as the key._

It's possible you might run into issues, if you do it should be reasonably safe to ignore the warning if you have a decent secret.

I'm thinking about ways we can make this easier and improve the security out of the box.

It's actually an interesting challenge that something like Auth0 handles well as it's easier to solve for a SaaS solution (because you can hide this complexity by generating and storing keys you manage on behalf of an end user). With a self hosted solution it's a bit tricker to make the process turnkey while keeping it secure.

i.e. you should be able to copy and paste the entire thing as the key.

So the output of the command jose newkey -s 256 -t ec -a HS512 can be used "as-is" for the value of the jwt.signingKey property?

But what about the jwt.verificationKey? I know you mentioned it's optional, but I'm still curious ;)

Great question! So it should work something like this:

Generate Public / Private key and save to JWK Key Set

$ jose newkey -t RSA -s 2048 -K -b > myKeySet.jwks 
$ cat mykey.jws 
{
    "keys": [
        {
            "kty": "RSA",
            "kid": "9wSFH_9gl4wB_ul1Oh7dio9vjaw0FrrvoJmX-OG4Y_Q",
            "e": "AQAB",
            "n": "7F5Ka8V9BWK3cAvtIwJjlx8FTdBz-ofzsDhfrd-eRee6mslj-n9eTXmevncouCAz_6GmjtkPFyi21VR0T5HwPOASCsaKUnZLnCgosfKruN54KN9nA1DxEjL4eF4QJDAy8epnJ4ICnHvVFfCWAKWJ2XGYzhsTDyKwuaov7CtIig5NvyFRE8t0hdCYbIU3j1EBUjDRvBixvQHPBifysHLxZdhDPvKH_azzYilSxwKteb1Z-roOJYi_HW4u5fAp8C7bLYp3KWiJABM-hlIYGuc726qxcWan91sJjbpXYldzelh1lazrXMgVSgKET6f5JaO6U2dnTGNM6_6hvdbtX3z8hw",
            "d": "wGviWenj5UZ_3w9WdXG0cYonXzmbsRCMKaE7AulQh8gl-WyJn5ugwaGIwbVvyLUun6SRWykJR-k1LmYaacx8rpIbHfqTEJ6M3wsyVesgMNerKlr0GZcDKbWk4RhyO4OhdZ9FarxEM-_7gGjjNJOOrR31L5SK539uLzeE29ZAr2F08TLPe0k0BBxNYZIEBdcRA2SaXJWYAvEnLK8BRgia0HlyowWBTHUs4dmrfyD62ejORRyzWAZpE3uhULXWgjTHEcG86Su0ihHVG-f2nWKOEs2PoB8AxSdb5YbHweOiE4BRlfg3qar_Kqw3iVtz8tbRcOXKXeKKYK04afARm9R0gQ",
            "p": "_LQCA0tD2Tz5qNtK4h3fC9hSfsjsaF34FzIWoQrNb5_X3N5x57d29ptt7Vi2VLgWttExT0N8fvcRziK31tPZkZKENHxlk5j7XCfi88XdeL666fGzI1_WRPWh_ybRfNquFLvfg5gP0M25GIKCVX_NMMD6QNyGVkN3rnY7C_gNAp8",
            "q": "73O6E0DAzlArfodxMyBRy-Q_woiwWkwRxOPkQ04AP6qmXlUtf5kOcVxofnR-0McB63RCMF6QwXcQse3o8epqM7t1DOuxoOLcAVG-A6O5mCvQLO1g1DlbEzmJQKjydpH9CeADKGvrPanzWQq-PSAf1c8ehM_YKfc4tpg356RxZRk",
            "dp": "drEd2Oo7HggTw4nsrDQXlXqvlLlOwN6SLmLqWg223F0ZiLY6jfmQONhcU4S9byFgGdI79Nzf1sq5ZDbOXmr23_cPfY-ILkFGnsxlPtaK5bHAHS3XVCAphg5RXoSfFKOp7DcqDqcXlp1p-OcjzIDQobuiT2DFADOEPT9vEHobskU",
            "dq": "vbUADNTWbB4T93Cy_IB0jYbOBP9qSH_P7B9o7vHTPJ2kpUPygg7u5F0D5HvDJQElwjcfYVbqdHwyW-VTEqZXcZjoiU4KMp7JErqCQjvqhGbhzVZbyoeOYlLJLNMtFUdgI_y4Q8QMYZRN2ZOrWJ4k2q33Td2lMspMwk0irYEY3bE",
            "qi": "SK7J6u9e48F-rHTR-KK-9tAdQxRwu7nmPDT2JqJnxzMl3EOHKcv8005iO-vLqRLWns2oWaQk0rpVVUcU4Y46Cad2NCOfa4mwKcrKyoyWlz4x4CpYSt1ggyZugzU0ncrwqLEakZhhHoz_6R1k5Z84fgDsm9CWDF7n4Hni7yxratw"
        }
    ]
}

Extract Private / Public Key from JWK Key Set

$ jose findkey -b -j myKeySet.jwks 
{
    "kty": "RSA",
    "kid": "9wSFH_9gl4wB_ul1Oh7dio9vjaw0FrrvoJmX-OG4Y_Q",
    "e": "AQAB",
    "n": "7F5Ka8V9BWK3cAvtIwJjlx8FTdBz-ofzsDhfrd-eRee6mslj-n9eTXmevncouCAz_6GmjtkPFyi21VR0T5HwPOASCsaKUnZLnCgosfKruN54KN9nA1DxEjL4eF4QJDAy8epnJ4ICnHvVFfCWAKWJ2XGYzhsTDyKwuaov7CtIig5NvyFRE8t0hdCYbIU3j1EBUjDRvBixvQHPBifysHLxZdhDPvKH_azzYilSxwKteb1Z-roOJYi_HW4u5fAp8C7bLYp3KWiJABM-hlIYGuc726qxcWan91sJjbpXYldzelh1lazrXMgVSgKET6f5JaO6U2dnTGNM6_6hvdbtX3z8hw",
    "d": "wGviWenj5UZ_3w9WdXG0cYonXzmbsRCMKaE7AulQh8gl-WyJn5ugwaGIwbVvyLUun6SRWykJR-k1LmYaacx8rpIbHfqTEJ6M3wsyVesgMNerKlr0GZcDKbWk4RhyO4OhdZ9FarxEM-_7gGjjNJOOrR31L5SK539uLzeE29ZAr2F08TLPe0k0BBxNYZIEBdcRA2SaXJWYAvEnLK8BRgia0HlyowWBTHUs4dmrfyD62ejORRyzWAZpE3uhULXWgjTHEcG86Su0ihHVG-f2nWKOEs2PoB8AxSdb5YbHweOiE4BRlfg3qar_Kqw3iVtz8tbRcOXKXeKKYK04afARm9R0gQ",
    "p": "_LQCA0tD2Tz5qNtK4h3fC9hSfsjsaF34FzIWoQrNb5_X3N5x57d29ptt7Vi2VLgWttExT0N8fvcRziK31tPZkZKENHxlk5j7XCfi88XdeL666fGzI1_WRPWh_ybRfNquFLvfg5gP0M25GIKCVX_NMMD6QNyGVkN3rnY7C_gNAp8",
    "q": "73O6E0DAzlArfodxMyBRy-Q_woiwWkwRxOPkQ04AP6qmXlUtf5kOcVxofnR-0McB63RCMF6QwXcQse3o8epqM7t1DOuxoOLcAVG-A6O5mCvQLO1g1DlbEzmJQKjydpH9CeADKGvrPanzWQq-PSAf1c8ehM_YKfc4tpg356RxZRk",
    "dp": "drEd2Oo7HggTw4nsrDQXlXqvlLlOwN6SLmLqWg223F0ZiLY6jfmQONhcU4S9byFgGdI79Nzf1sq5ZDbOXmr23_cPfY-ILkFGnsxlPtaK5bHAHS3XVCAphg5RXoSfFKOp7DcqDqcXlp1p-OcjzIDQobuiT2DFADOEPT9vEHobskU",
    "dq": "vbUADNTWbB4T93Cy_IB0jYbOBP9qSH_P7B9o7vHTPJ2kpUPygg7u5F0D5HvDJQElwjcfYVbqdHwyW-VTEqZXcZjoiU4KMp7JErqCQjvqhGbhzVZbyoeOYlLJLNMtFUdgI_y4Q8QMYZRN2ZOrWJ4k2q33Td2lMspMwk0irYEY3bE",
    "qi": "SK7J6u9e48F-rHTR-KK-9tAdQxRwu7nmPDT2JqJnxzMl3EOHKcv8005iO-vLqRLWns2oWaQk0rpVVUcU4Y46Cad2NCOfa4mwKcrKyoyWlz4x4CpYSt1ggyZugzU0ncrwqLEakZhhHoz_6R1k5Z84fgDsm9CWDF7n4Hni7yxratw"
}

Extract Public Key from JWK Key Set

_The advantage of public/private keys is that anyone can verify a JWT (and you can give it to third parties so they can validate your JWT) but you can keep the Private key secret so only you can create / update the JWT._

$ jose findkey -p -b -j myKeySet.jwks 
{
    "kty": "RSA",
    "kid": "9wSFH_9gl4wB_ul1Oh7dio9vjaw0FrrvoJmX-OG4Y_Q",
    "e": "AQAB",
    "n": "7F5Ka8V9BWK3cAvtIwJjlx8FTdBz-ofzsDhfrd-eRee6mslj-n9eTXmevncouCAz_6GmjtkPFyi21VR0T5HwPOASCsaKUnZLnCgosfKruN54KN9nA1DxEjL4eF4QJDAy8epnJ4ICnHvVFfCWAKWJ2XGYzhsTDyKwuaov7CtIig5NvyFRE8t0hdCYbIU3j1EBUjDRvBixvQHPBifysHLxZdhDPvKH_azzYilSxwKteb1Z-roOJYi_HW4u5fAp8C7bLYp3KWiJABM-hlIYGuc726qxcWan91sJjbpXYldzelh1lazrXMgVSgKET6f5JaO6U2dnTGNM6_6hvdbtX3z8hw"
}

There are some good docs for JOSE tools here (the inline help for the CLI doesn't work for me):
https://github.com/phish108/node-jose-tools/blob/HEAD/docs/00_INDEX.md

There are some examples of JSON Web Keys from the RFC I found helpful:
https://self-issued.info/docs/draft-ietf-jose-json-web-key.html#ExampleJWKSets

Oh and this section of the JSON Web Algorithms (JWA) RFC was helpful for me as it explains what all the bits of the object are:
https://tools.ietf.org/html/rfc7518#section-6.3

I also found the JWK RFC says there is an optional use parameter that can be used to indicate if a key is meant for signing or encryption (handy to avoid getting them mixed up), but I'm not sure if the jose tools command has a flag for that.

I've tagged this help wanted and if it's okay with you would like to leave it open until we have some sort of tutorial and/or better documentation for keys. It would be great if anyone fancies trying all this out, writing one up and submitting one. :-)

If anyone is inclined, it should be possible to work out the similar options for encryption / description from https://github.com/nextauthjs/next-auth/blob/main/src/lib/jwt.js

I'm also open to the idea of tweaking default options / default behaviour.

@iaincollins Thanks again for your quick reply.

I'm trying to automate this as much as possible. I already have a script that will generate the .env file for my project and I want to add the ability to generate the public/private keys for next-auth.

I found the node-jose package and I'm using it like this:

  // Generate a new key.
  const key = await jose.JWK.createKey("EC", "P-256", {
    alg: "HS512",
  });
  const jwk = key.toJSON(true);
  console.log(jwk);

This should generate a similar key to the one your provided as an example.

Obviously I'm just printing the key to the console for now, but the idea is to write the key to an entry in the .env file so it can be used to configure next-auth.

Does this look correct?

Sorry for the spam, but I'm totally at a loss...

I'm generating the signing key during configuration like this:

import jose from "node-jose";

  // Generate a new keys.
  const key = await jose.JWK.createKey("EC", "P-256", {
    alg: "HS512",
    use: "sig",
  });
  const jwk = key.toJSON(true);
  fs.writeFileSync("jwk.json", JSON.stringify(jwk));

And using the key at runtime like this:

import jwk from "./jwk.json";

const options = {
  // other options...
  session: {
    jwt: true,
  },
  jwt: {
    signingKey: JSON.stringify(jwk),
  },
};

But I keep getting the following error:

(node:12172) UnhandledPromiseRejectionWarning: JWKKeySupport: the key does not support HS512 sign algorithm
    at check (c:\app\node_modules\jose\lib\jwa\index.js:45:11)
    at check (c:\app\node_modules\jose\lib\jwa\index.js:58:12)
    at sign (c:\app\node_modules\jose\lib\jwa\index.js:65:5)
    at Sign.[PROCESS_RECIPIENT] (c:\app\node_modules\jose\lib\jws\sign.js:115:50)
    at Sign.sign (c:\app\node_modules\jose\lib\jws\sign.js:134:30)
    at single (c:\app\node_modules\jose\lib\jws\index.js:7:6)
    at Object.module.exports [as sign] (c:\app\node_modules\jose\lib\jwt\sign.js:91:14)
    at Object.<anonymous> (c:\app\node_modules\next-auth\dist\lib\jwt.js:52:41)
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (c:\app\node_modules\next-auth\dist\lib\jwt.js:22:103)
    at _next (c:\app\node_modules\next-auth\dist\lib\jwt.js:24:194)
    at c:\app\node_modules\next-auth\dist\lib\jwt.js:24:364
    at new Promise (<anonymous>)
    at Object.<anonymous> (c:\app\node_modules\next-auth\dist\lib\jwt.js:24:97)
    at Object.encode (c:\app\node_modules\next-auth\dist\lib\jwt.js:64:17)
    at c:\app\node_modules\next-auth\dist\server\routes\callback.js:278:38

I've tried to generate the key with both jose and node-jose, but I'm getting the same exception with both libraries.

Any ideas?

@jpvanoosten

Oops, sorry - the example I gave doesn't match up with the default key type NextAuth.js expects 🤦‍♂️

I think it should be key type oct for compatibility (i.e. octet - just a bytes sequence) rather than ec (Elliptic Curve) which is better but not the default (so you'd need to specify other options to use it).

I think this should work:

jose newkey -s 256 -t oct -a HS512

Alternatively, for createKey:

const key = await jose.JWK.createKey("oct", 256, {
 alg: "HS512",
 use: "sig",
})

I'd be interested to hear how you get on with this!

PS: If it doesn't work just let me know and I'll take a look; if it still doesn't work it's reasonable to assume there is a problem and that it's not with you if run into anything odd.

@iaincollins Changing the key type to "oct" fixes the issue.

Of course, now I'm curious about how I could use "EC"?

I suppose I have some reading to do. Cryptography has never been my favorite subject ;)

Thanks for your help on this. If I had a better grasp on these topics, then I'd feel more comfortable contributing to the project.

Any news about this issue and documentation about it? Getting the same warning.
By the way, great library!

I did not add the signingKey to the jwt

I just exported the enviroment variable NEXTAUTH_URL as _http://localhost:3000/_.
After this, I could see the page on http://localhost:3000/api/auth/signin

_Make sure you write /api/auth/signin instead of /api/auth/login as I had done :P__

By the way @iaincollins, can you tell why I am getting this

[next-auth][warn][jwt_auto_generated_signing_key]  
https://next-auth.js.org/warning#jwt_auto_generated_signing_key

Also, please provide also provide a fix for this

To resolve that warning you need to specify a signing key. If you want to just set a JWT secret instead, that's also fine - you will still get the warning but is fine to do as long as the secret is decently long.

For details, see:
https://next-auth.js.org/warnings#jwt_auto_generated_signing_key

The URL to point to the correct page has been fixed and will be updated in the next release.

Is it alright to not provide a signing key ? Are there any security vulnerabilities associated with the warning?
Also, if I use XXXX as my secret, and on the next deployment, change the secret to YYYY, would the current sessions be invalidated?

I wonder the same thing as @pbshgthm

@PaulKushch @pbshgthm

Yes, your sessions will be invalid if you change the secret for jwt.

You can use JSON web key as a signing key _(you can search it on the web for more)_. Any _random string_ cannot be used for JSON web key because it might crash the application giving you an error.

@iaincollins Is there any documentation about the config below:

jwt: {
  signingKey: process.env.JWT_SIGNING_PRIVATE_KEY,

  // You can also specify a public key for verification if using public/private key (but private only is fine)
  // verificationKey: process.env.JWT_SIGNING_PUBLIC_KEY,

  // If you want to use some key format other than HS512 you can specify custom options to use
  // when verifying (note: verificationOptions should include a value for maxTokenAge as well).
  // verificationOptions = {
  //   maxTokenAge: `${maxAge}s`, // e.g. `${30 * 24 * 60 * 60}s` = 30 days
  //   algorithms: ['HS512']
  // },
}

I only can find the documentation config below (i.e. no mentioning about signingKey and verificationKey):

jwt: {
  // A secret to use for key generation - you should set this explicitly
  // Defaults to NextAuth.js secret if not explicitly specified.
  // secret: 'INp8IvdIyeMcoGAgFGoA61DdBglwwSqnXJZkgz8PSnw',

  // Set to true to use encryption. Defaults to false (signing only).
  // encryption: true,

  // You can define your own encode/decode functions for signing and encryption
  // if you want to override the default behaviour.
  // encode: async ({ secret, token, maxAge }) => {},
  // decode: async ({ secret, token, maxAge }) => {},
}

I am still getting the following warning after adding a signingKey field to jwt

[next-auth][warn][jwt_auto_generated_encryption_key]
https://next-auth.js.org/warning#jwt_auto_generated_encryption_key

secret: process.env.COOKIE_SECRET,
  session: {
    // Use JSON Web Tokens for session instead of database sessions.
    // This option can be used with or without a database for users/accounts.
    // Note: `jwt` is automatically set to `true` if no database is specified.
    jwt: true,

    // Seconds - How long until an idle session expires and is no longer valid.
    maxAge: 30 * 24 * 60 * 60, // 30 days

    // Seconds - Throttle how frequently to write to database to extend a session.
    // Use it to limit write operations. Set to 0 to always update the database.
    // Note: This option is ignored if using JSON Web Tokens
    // updateAge: 24 * 60 * 60, // 24 hours
  },

  // JSON Web tokens are only used for sessions if the `jwt: true` session
  // option is set - or by default if no database is specified.
  // https://next-auth.js.org/configuration/options#jwt
  jwt: {
    // A secret to use for key generation (you should set this explicitly)
    // secret: process.env.JWT_SECRET,
    signingKey: process.env.JWT_SIGNING_PRIVATE_KEY,
    // Set to true to use encryption (default: false)
    encryption: true,
    // You can define your own encode/decode functions for signing and encryption
    // if you want to override the default behaviour.
    // encode: async ({ secret, token, maxAge }) => {},
    // decode: async ({ secret, token, maxAge }) => {},
  },

@timothymiller I couldn't find this documented, but I was able to add an encryptionKey prop in addition to signingKey.

The signingKey I generated per the docs like:

  • npx node-jose-tools newkey -s 256 -t oct -a HS512

I tried that for the encryptionKey, but got an error that it required the A256GCM algorithm, and so created it like so:

  • npx node-jose-tools newkey -s 256 -t oct -a A256GCM

which resulted in:

  jwt: {
    secret: jwtSecret,
    encryption: true,
    signingKey: '{"kty":"oct","kid":"<the-kid>","alg":"HS512","k":"<the-key>"}',
    encryptionKey: '{"kty":"oct","kid":"<the-kid>","alg":"A256GCM","k":"<the-key>"}',
  },

It appears to work as expected where changing the encryptionKey results in an error:

[next-auth][error][jwt_session_error] JWEDecryptionFailed: decryption operation failed

Hi there! It looks like this issue hasn't had any activity for a while. It will be closed if no further activity occurs. If you think your issue is still relevant, feel free to comment on it to keep it open. (Read more at #912) Thanks!

Was this page helpful?
3 / 5 - 1 ratings

Related issues

jimmiejackson414 picture jimmiejackson414  ·  3Comments

loonskai picture loonskai  ·  3Comments

MelMacaluso picture MelMacaluso  ·  3Comments

ryanditjia picture ryanditjia  ·  3Comments

simonbbyrne picture simonbbyrne  ·  3Comments