supabase / auth

A JWT based API for managing users and issuing JWT tokens
https://supabase.com/docs/guides/auth
MIT License
1.55k stars 374 forks source link

Some JWTs exceed Cloudflare header size limits & Nginx defaults #1754

Open yekta opened 2 months ago

yekta commented 2 months ago

Bug report

Describe the bug

Some JWTs are simply too big in size and they break reasonable header size defaults: Two of which are Cloudflare header size limits and Nginx default header size limits.

The example below is using Google OAuth to sign in to our self-hosted Supabase instance. We use anything but defaults, we are not pushing extra data to the user metadata object etc.

Screenshot 2024-08-31 at 16 25 46

I'm not sure about the reason for including these metadata in JWTs. There have been people having issues with exactly this over the years multiple times (starting with Netlify gotrue). Considering Cloudflare and Nginx are rather large and their limits are not unreasonable, I think gotrue should not break them by default.

A simple solution could be letting people skip the encoding of user metadata in JWTs. Here is a very rough example:

dontEncodeUserMetaData := true
var userMetaData map[string]interface{} = user.UserMetaData
if dontEncodeUserMetaData {
    userMetaData = map[string]interface{}{}
}

claims := &hooks.AccessTokenClaims{
    RegisteredClaims: jwt.RegisteredClaims{
        Subject:   user.ID.String(),
        Audience:  jwt.ClaimStrings{user.Aud},
        IssuedAt:  jwt.NewNumericDate(issuedAt),
        ExpiresAt: jwt.NewNumericDate(expiresAt),
        Issuer:    config.JWT.Issuer,
    },
    Email:                         user.GetEmail(),
    Phone:                         user.GetPhone(),
    AppMetaData:                   appMetaData,
    UserMetaData:                  userMetaData,
    Role:                          user.Role,
    SessionId:                     sid,
    AuthenticatorAssuranceLevel:   aal.String(),
    AuthenticationMethodReference: amr,
    IsAnonymous:                   user.IsAnonymous,
}

Here is the exact same user login with user metadata removed from the JWT. Significantly smaller, however still larger than "usual":

Screenshot 2024-08-31 at 16 33 04

I'm willing to create a PR with the fix, however, I'm not sure about the preferred way of handling this. I was thinking it could be a simple env variable which people can use to opt-out of encoding user metadata in the JWT. We're currently running our fork in production and nothing seems to be broken. However, I don't know if there are potential bad implications of simply omitting the user metadata.

Update: I've realized identities are also encoded in this cookie. So if someone is signed in with say 4 different providers and changed their email a couple of times, this guarantees exceeding the majority of all header size limits anywhere not just Cloudflare or Nginx.

To Reproduce

I'm not exactly sure exactly when this happens. It seems to be happening with some Google logins, and not others. Either way, the cookies on average are larger than any auth library I've seen.

Expected behavior

Not hitting header size limits by default.

danperks commented 1 month ago

Just to jump on the back of this, this has caused me a decent bit of pain today. I recently updated all my dependencies on a production project, all looked good so it was deployed. However, certain users were having issues logging in, getting 402's and "request header too big" errors.

This bug was the exact issue I was having, and it occurred only after upgrading my Nuxt Supabase version - I'd be more understanding if I was going to an older version of the package.

When a subset of our users logged in using a auth provider (namely the Azure provider), the cookies generated would account for ~7.5kb of size. With the rest of the usual request, this was breaching an 8kb request size, which caused both Cloudflare and the destination NGINX proxy to reject these requests.

It took long enough to figure out the cause, as non-Azure user's had no issues (Azure seems to account for a large chunk of the JWT), and I resolved the NGINX side with some config changes to override the defaults. However, Cloudflare has a hard limit of 8kb for it's request size, which I found no way of resolving, so I ended up having to remove Cloudflare altogether and point users direct to the NGINX server.

For clarity, I am using the nuxt-modules/supabase module as my app is a Nuxt one. A better way to configure what values should be included in a JWT would be ideal. as besides confirming a user is an Azure AD member, and linking a Supabase user to that account, I make no use of the output of Azure's authentication process and could happily strip it from the JWT.

Both Cloudflare and NGINX are production-ready, industry standard tools and consequently, Supabase would benefit by support this out the box, without complex token rewriting hooks or entirely custom JWT implementations.

yekta commented 3 weeks ago

Are there any plans to fix this? We started looking into moving to other auth options specifically because of this issue. I don't see using Supabase going forward unless the issue is fixed. We can't use Supabase Auth and put our site behind Cloudflare which is a deal breaker.