Next-auth: Cookie Contents & Usage Documentation for Audit/Security-review Support

Created on 20 Jul 2020  路  5Comments  路  Source: nextauthjs/next-auth

Your question
I've been looking through the documentation to see if there were specifics around what the three next-auth cookies were used for and what details were stored in each. I didn't find answers to everything I was curious about.

I know (or have made the assumptions) that:

  • The cookies are signed with the secret I provide
  • Any tokens in the cookies are hashed using the secret I provide
  • The JWT is stored in the session-token cookie
  • The csrf-token is a relatively standard implementation of this functionality, preventing attacks/supporting scenarios as detailed here https://stackoverflow.com/q/20504846/1763258

I'm still wondering/would like to confirm:

  • Are my assumptions above are correct?
  • Although they are signed, the cookies are not encrypted, and anything not hashed can be read?

    • Is there anything not hashed stored in the cookies?

  • What else (besides token), if anything, is stored in the session-token cookie?
  • What else (besides token), if anything, is stored on the csrf-token cookie?
  • How is the callback-url cookie used, and what is stored in it?
  • Will any of the above assumptions or answers change between current v2 implementation and upcoming v3 implementation?

What are you trying to do
I'd like to be able to provide as much comprehensive detail as possible to security teams/auditors.

Documentation feedback
Documentation refers to searching through online documentation, code comments and issue history. The example project refers to next-auth-example.

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

Most helpful comment

This is a fantastic set of questions and deserves to be written up somewhere more permanently, but here is a quick reply from memory:

I know (or have made the assumptions) that:

  • The cookies are signed with the secret I provide
  • Any tokens in the cookies are hashed using the secret I provide

I'll answer these together.

In v3 there are currently only 3 cookies (less than in v2):

  • sessionToken - Either JWT signed with JWS or a random Session ID token if using database sessions (if latter, not signed)
  • csrfToken - Uses hash for signing and is salted with secret
  • callbackUrl - Not signed (see below)

The value in callbackUrl is always checked before it is used so there is limited value in signing it, but it's not a terrible idea, now that in v3 it can only be set via POST (using request with CSRF Token).

The JWT is signed by the random Session ID token is not. There might be some benefit in also signing it as it would avoid an extra database call to look up a token that is fake (reducing a DDoS vector).

  • The JWT is stored in the session-token cookie

Yes.

Note: Version 3 uses a better JWS implementation and signing only by default

  • By default Version 3 tokens are signed with JWS using asymmetric key derived from the secret.
  • By default Version 3 not use AES by default, but does now use JWE if you do enable encryption (jwt: { encryption: true }). If keys are not configured, it uses a different AES asymmetric key, also ultimately derived from the secret.

Yes - specifically the 'Request Body Approach' detailed there.

Note: Not all routes were protected in v2, but this has been extended in v3 to cover sign in as well (often with OAuth this a GET request as it's often just in the form of a link, but is now an HTTP POST with CSRF Token required for all flows). The built-in sign in pages and the client signIn() method hide this complexity.

I'm still wondering/would like to confirm:

  • Are my assumptions above are correct?
  • Although they are signed, the cookies are not encrypted, and anything not hashed can be read?

    • Is there anything not hashed stored in the cookies?

  • In v2 JWT were encrypted with AES by default (wrapped, but not using the JWE spec) as well as signed with JWS.
  • In v3 JWT are still signed by default (with JWS) but not encrypted by default (but use AES with JWE spec if encryption enabled). Encrypted JWT by default is less compatible and introduces some problems, so it's an option now.
  • Cookies are sent over SSL and HTTP Only. Encryption of the payload of the CSRF Token and Callback URL in this context doesn't seem to offer additional protection (anything that can read them can also see what URLs you are accessing / perform an MITM).
    > * What else (besides token), if anything, is stored in the session-token cookie?

Session Token cookie is either a random Session ID (if using database sessions) or a JWT (if using JWT sessions)

The JWT in v3 includes JWT claims for name, email and avatar; but the contents is customisable.

  • What else (besides token), if anything, is stored on the csrf-token cookie?

Only the token and a signature for the token (hashed with the secret).

  • How is the callback-url cookie used, and what is stored in it?

The callback URL set before the user started their sign in journey so that they are returned to the same page after signing in.

The value is checked to see if it is allowed before it is stored AND AGAIN before it is used. By default any same site URL is valid, but a custom redirect() callback can be specified.

It is used because not all providers support keeping track of a state parameter passed to them; and there are security issues that can arise with those that do support this.

  • Will any of the above assumptions or answers change between current v2 implementation and upcoming v3 implementation?

tl;dr

  • The site name is now an environment variable in v3, so is one less cookie to worry about.
  • The JWT is signed but not encrypted by default. The payload is simpler (standards compliant).
  • More use POST and CSRF Tokens are enforced on all of them (including custom credentials providers).

Happy to answer any followup questions.

If a team are evaluating this, I would suggest against the v3 beta or - if they don't want to do that - hold off for a week or two until it's released (I don't think it will be long now).

All 5 comments

This is a fantastic set of questions and deserves to be written up somewhere more permanently, but here is a quick reply from memory:

I know (or have made the assumptions) that:

  • The cookies are signed with the secret I provide
  • Any tokens in the cookies are hashed using the secret I provide

I'll answer these together.

In v3 there are currently only 3 cookies (less than in v2):

  • sessionToken - Either JWT signed with JWS or a random Session ID token if using database sessions (if latter, not signed)
  • csrfToken - Uses hash for signing and is salted with secret
  • callbackUrl - Not signed (see below)

The value in callbackUrl is always checked before it is used so there is limited value in signing it, but it's not a terrible idea, now that in v3 it can only be set via POST (using request with CSRF Token).

The JWT is signed by the random Session ID token is not. There might be some benefit in also signing it as it would avoid an extra database call to look up a token that is fake (reducing a DDoS vector).

  • The JWT is stored in the session-token cookie

Yes.

Note: Version 3 uses a better JWS implementation and signing only by default

  • By default Version 3 tokens are signed with JWS using asymmetric key derived from the secret.
  • By default Version 3 not use AES by default, but does now use JWE if you do enable encryption (jwt: { encryption: true }). If keys are not configured, it uses a different AES asymmetric key, also ultimately derived from the secret.

Yes - specifically the 'Request Body Approach' detailed there.

Note: Not all routes were protected in v2, but this has been extended in v3 to cover sign in as well (often with OAuth this a GET request as it's often just in the form of a link, but is now an HTTP POST with CSRF Token required for all flows). The built-in sign in pages and the client signIn() method hide this complexity.

I'm still wondering/would like to confirm:

  • Are my assumptions above are correct?
  • Although they are signed, the cookies are not encrypted, and anything not hashed can be read?

    • Is there anything not hashed stored in the cookies?

  • In v2 JWT were encrypted with AES by default (wrapped, but not using the JWE spec) as well as signed with JWS.
  • In v3 JWT are still signed by default (with JWS) but not encrypted by default (but use AES with JWE spec if encryption enabled). Encrypted JWT by default is less compatible and introduces some problems, so it's an option now.
  • Cookies are sent over SSL and HTTP Only. Encryption of the payload of the CSRF Token and Callback URL in this context doesn't seem to offer additional protection (anything that can read them can also see what URLs you are accessing / perform an MITM).
    > * What else (besides token), if anything, is stored in the session-token cookie?

Session Token cookie is either a random Session ID (if using database sessions) or a JWT (if using JWT sessions)

The JWT in v3 includes JWT claims for name, email and avatar; but the contents is customisable.

  • What else (besides token), if anything, is stored on the csrf-token cookie?

Only the token and a signature for the token (hashed with the secret).

  • How is the callback-url cookie used, and what is stored in it?

The callback URL set before the user started their sign in journey so that they are returned to the same page after signing in.

The value is checked to see if it is allowed before it is stored AND AGAIN before it is used. By default any same site URL is valid, but a custom redirect() callback can be specified.

It is used because not all providers support keeping track of a state parameter passed to them; and there are security issues that can arise with those that do support this.

  • Will any of the above assumptions or answers change between current v2 implementation and upcoming v3 implementation?

tl;dr

  • The site name is now an environment variable in v3, so is one less cookie to worry about.
  • The JWT is signed but not encrypted by default. The payload is simpler (standards compliant).
  • More use POST and CSRF Tokens are enforced on all of them (including custom credentials providers).

Happy to answer any followup questions.

If a team are evaluating this, I would suggest against the v3 beta or - if they don't want to do that - hold off for a week or two until it's released (I don't think it will be long now).

This is amazing, thanks for the detailed response!

I'll take your advice & look into the v3 beta and hold off on any formal internal review until after that is released.

I'm curious about setting the domain in the cookie. It sounds the domain in the cookie is generated from the environmental variable. I'm looking to generate a cookie that can be shared between this site and a subdomain (like .mydomain.com) but I'm having trouble setting this. I've tried generating my own cookies and it has been troublesome as the page mentions (https://next-auth.js.org/configuration/options#usesecurecookies). If it is an environmental variable I can't use .domain.com since that will break everything else.

What is the best approach to expanding this? My goal is to get a cookie which can be passed to api.domain.com as well. Is it to use custom cookies? Just using the code from the cookie page doesn't seem to work (they aren't setting). Thanks.

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 ot open. Thanks!

Hi there! It looks like this issue hasn't had any activity for a while. To keep things tidy, I am going to close this issue for now. If you think your issue is still relevant, just leave a comment and I will reopen it. (Read more at #912) Thanks!

Was this page helpful?
0 / 5 - 0 ratings