Skip to content

feat: support jwt signing keys for local auth#3841

Merged
sweatybridge merged 22 commits into
developfrom
cemal/add-cmd-generate-jwt-signing-keys
Jul 21, 2025
Merged

feat: support jwt signing keys for local auth#3841
sweatybridge merged 22 commits into
developfrom
cemal/add-cmd-generate-jwt-signing-keys

Conversation

@cemalkilic

@cemalkilic cemalkilic commented Jul 10, 2025

Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce?

command to generate jwt signing keys & spin up auth server with the configured JWK keys

Steps

  1. Generate the jwt signing keys by supabase gen signing-key ES256
  2. Enable signing_keys_path line in the config.toml.
  3. Once the auth server is up and running, the configured JWK should be available in http://localhost:54321/auth/v1/.well-known/jwks.json
$ ./bin/supabase gen signing-key --help
Securely generate a private JWT signing key for use in the CLI or to import in the dashboard.

Supported algorithms:
	ES256 - ECDSA with P-256 curve and SHA-256 (recommended)
	RS256 - RSA with SHA-256

Usage:
  supabase gen signing-key [flags]

Examples:
  supabase gen signing-key --algorithm RS256
  supabase gen signing-key --algorithm ES256
  supabase gen signing-key --algorithm RS256 --append

Flags:
      --algorithm [ RS256 | ES256 ]   Algorithm for signing key generation. (default ES256)
      --append                        Append new key to existing keys file instead of overwriting.
  -h, --help                          help for signing-key

...

Command output

$ ./bin/supabase gen signing-key --algorithm RS256
/Users/cemalkilic/projects/cli/signing_keys.json
JWT signing keys saved to: /Users/cemalkilic/projects/cli/signing_keys.json
⚠️  IMPORTANT: Add this file to your .gitignore to prevent committing signing keys to version control

To enable JWT signing keys in your project:
1. Add the following to your config.toml file:
   signing_keys_path = "/Users/cemalkilic/projects/cli/signing_keys.json"
2. Restart your local development server:
   supabase start

Run with --append flag

$ ./bin/supabase gen signing-key --algorithm RS256 --append
/Users/user/projects/cli/signing_keys.json
JWT signing key appended to: /Users/user/projects/cli/signing_keys.json (now contains 2 keys)
⚠️  IMPORTANT: Add this file to your .gitignore to prevent committing signing keys to version control

To enable JWT signing keys in your project:
1. Add the following to your config.toml file:
   signing_keys_path = "/Users/cemalkilic/projects/cli/signing_keys.json"
2. Restart your local development server:
   supabase start

@cemalkilic cemalkilic requested a review from a team as a code owner July 10, 2025 19:44
@coveralls

coveralls commented Jul 10, 2025

Copy link
Copy Markdown

Pull Request Test Coverage Report for Build 16416756141

Details

  • 94 of 198 (47.47%) changed or added relevant lines in 4 files are covered.
  • 5 unchanged lines in 1 file lost coverage.
  • Overall coverage decreased (-0.2%) to 55.325%

Changes Missing Coverage Covered Lines Changed/Added Lines %
internal/start/start.go 3 5 60.0%
cmd/gen.go 0 7 0.0%
internal/utils/misc.go 0 16 0.0%
internal/gen/signingkeys/signingkeys.go 91 170 53.53%
Files with Coverage Reduction New Missed Lines %
internal/gen/keys/keys.go 5 12.9%
Totals Coverage Status
Change from base Build 16410144347: -0.2%
Covered Lines: 6171
Relevant Lines: 11154

💛 - Coveralls

@hf hf left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks OK to me, however you may want to explore a jwk library if you want.

@hf

hf commented Jul 11, 2025

Copy link
Copy Markdown
Contributor

Maybe the default output format should be the key, while passing another parameter to give you the config.toml setting?

Reason being this command can be used for people who want to import a private key they control.

@cemalkilic cemalkilic changed the title feat: add generate-key cmd for generating jwt signing keys feat: support jwt signing keys for local auth Jul 11, 2025
@cemalkilic cemalkilic force-pushed the cemal/add-cmd-generate-jwt-signing-keys branch from e23950b to edf8d7e Compare July 11, 2025 10:28
Comment thread cmd/gen.go Outdated
Comment thread pkg/config/templates/config.toml Outdated
Comment thread internal/start/start.go Outdated
@cemalkilic cemalkilic force-pushed the cemal/add-cmd-generate-jwt-signing-keys branch from f6a01d7 to 0cbcee0 Compare July 15, 2025 12:31

@hf hf left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Comment thread cmd/gen.go Outdated
Comment thread cmd/gen.go
Comment thread pkg/config/auth.go Outdated
Comment thread cmd/gen.go Outdated
Comment thread internal/gen/signingkeys/signingkeys.go Outdated
Comment thread cmd/gen.go Outdated
Comment thread internal/start/start.go Outdated
Comment thread internal/gen/signingkeys/signingkeys.go Outdated
Comment thread internal/gen/signingkeys/signingkeys.go Outdated
Comment thread internal/gen/signingkeys/signingkeys.go Outdated
Comment thread internal/gen/signingkeys/signingkeys.go Outdated
Comment thread internal/gen/signingkeys/signingkeys.go Outdated
Comment thread cmd/gen.go Outdated
Comment thread pkg/config/config.go Outdated
Comment thread internal/gen/signingkeys/signingkeys.go Outdated
}
out = f
}
jwkArray = append(jwkArray, keyPair.PrivateKey)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cemalkilic do we need to show or save the public key to user?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think at this point, no.

Comment thread internal/start/start.go Outdated

// Add JWT keys from file if configured
if keysData, err := utils.Config.Auth.GetSigningKeysData(fsys); err == nil && keysData != "" {
env = append(env, "GOTRUE_JWT_KEYS="+keysData)

@sweatybridge sweatybridge Jul 21, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cemalkilic can we add the jwks parsed from line 148 directly here?

			"GOTRUE_JWT_SECRET=" + utils.Config.Auth.JwtSecret.Value,
			"GOTRUE_JWT_KEYS=" + jwks,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that jwks is consumed by the other services (storage, realtime etc) to accept tokens from the third party auth (docs).
Supabase Auth server shouldn't serve the JWKS of the third party auth providers.

@sweatybridge sweatybridge force-pushed the cemal/add-cmd-generate-jwt-signing-keys branch from 62c58f7 to df6a75f Compare July 21, 2025 10:33
@sweatybridge sweatybridge merged commit 63a8891 into develop Jul 21, 2025
14 checks passed
@sweatybridge sweatybridge deleted the cemal/add-cmd-generate-jwt-signing-keys branch July 21, 2025 12:26
@logemann

Copy link
Copy Markdown

is there an easy way to track when this will be available as docker gotrue image? Happy to instantly use it ;-)

@github-actions github-actions Bot mentioned this pull request Jul 22, 2025
@traviswimer

Copy link
Copy Markdown

@logemann It seems to be working for me after installing the 2.33.8 beta version of the cli.

I'm now also getting an error when trying to use the js client with a "service role key", but JWTs are working well with the JWKs.

@marcfrankel

Copy link
Copy Markdown

@traviswimer Did you figure out a solution to the service role key not working? That's blocking me now. I keep getting "JWSError JWSInvalidSignature" errors when using it via the js-client

@traviswimer

Copy link
Copy Markdown

@marcfrankel yep, you can generate your own JWT and use it as the service role key, but hopefully Supabase eventually provides a more streamlined solution.

For now anyway, you can use supabase gen signing-key --algorithm ES256 to generate a JWK. Then you need to use a library to generate the JWT using that JWK.

In my case I used a node script with the jose library, so something like this:

import { importJWK, SignJWT } from 'jose';
import jwks from './signing_keys.json';

const jwk = jwks[0];

delete jwk.key_ops;
const privateKey = await importJWK(jwk);

const jwt = await new SignJWT({ role: "service_role" })
	.setProtectedHeader({ alg: "ES256", kid: jwks[0].kid })
	.setIssuedAt()
	.setIssuer("supabase")
	.setAudience("authenticated")
	.setExpirationTime("30d")
	.sign(privateKey);

I'm not sure why, but the key_ops property of the JWK caused an error for me, so I had to delete it to get it to work.

When signing the JWT, you can set the expiration to whatever you like, but the strings used for the other fields are what supabase expects in a service role key.


This is just the solution that worked for me. I have no idea if it is what the Supabase team would recommend, but since these are local JWTs that can't be used in production, I'm not too worried about it.

@cemalkilic

cemalkilic commented Aug 12, 2025

Copy link
Copy Markdown
Contributor Author

Hey folks, sorry for the inconvenience. Working on the fix now to make it work with secret role keys.

BTW @traviswimer that's a nice approach and the root cause! With current implementation, we sign the anon/service role keys with JWT secret and auth server is failing to validate them as it doesn't know about the JWT secret anymore.

edit: solved in #3969, will be available soon with a version release. thanks for bearing with us!

@cwooldridge1

cwooldridge1 commented Aug 26, 2025

Copy link
Copy Markdown

On cli v2.39.2 I am getting Error status 400: {"statusCode":"403","error":"Unauthorized","message":""alg" (Algorithm) Header Parameter value not allowed"}
Edit: Fixed by running supabase stop supabase start and then supabase db reset --local

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants