v1.0.3

Key Management

Last updated


go-jwt signs Access Tokens with ES256 (ECDSA P-256 + SHA-256); this page covers key loading order, format requirements, and multi-instance deployment.

Loading Order


PriorityConditionBehavior
1File.PrivateKeyPath / File.PublicKeyPath setRead the files and overwrite Option.PrivateKey / Option.PublicKey; a read failure returns an error
2Both Option.PrivateKey and Option.PublicKey setUse them directly
3Both empty, and ./keys/private-key.pem and ./keys/public-key.pem both existRead those files
4Both empty, and either file is missingGenerate a P-256 key pair into ./keys/ (directory 0755, private 0600, public 0644); when only one file is missing, the existing one is overwritten too
—Only one key providedReturn the error Both private key and public key are required

./keys/ is relative to the process working directory, not the binary's location.

Format Requirements


KeyPEM blockEncoding
PrivatePRIVATE KEYPKCS#8
PublicPUBLIC KEYPKIX (SubjectPublicKeyInfo)

After loading, both keys must be ECDSA and form a matching pair, or an error is returned.
The curve must be P-256. New() accepts any ECDSA curve, but signing is fixed to ES256; with P-384 or another curve New() succeeds and Create() later returns 500 failed_to_sign (key is invalid).

Generating with OpenSSL


  1. openssl ecparam -name prime256v1 -genkey -noout \
  2. | openssl pkcs8 -topk8 -nocrypt -out private-key.pem
  3. openssl ec -in private-key.pem -pubout -out public-key.pem
  4. chmod 600 private-key.pem

openssl ecparam -genkey alone outputs SEC1 (EC PRIVATE KEY); it must go through pkcs8 -topk8 to become PKCS#8.

Loading from the Environment or a Secret Manager


The library reads no environment variables; the application reads them and passes them in Option:
  1. auth, err := goJwt.New(goJwt.Config{
  2. Redis: goJwt.Redis{Host: "localhost", Port: 6379},
  3. Option: &goJwt.Option{
  4. PrivateKey: os.Getenv("JWT_PRIVATE_KEY"),
  5. PublicKey: os.Getenv("JWT_PUBLIC_KEY"),
  6. },
  7. })
  8. if err != nil {
  9. log.Fatalf("failed to init go-jwt: %v", err)
  10. }

JWT_PRIVATE_KEY / JWT_PUBLIC_KEY are names chosen by the application, not library settings.

Multi-Instance Deployment


CaseResult
Each instance auto-generates keysKeys differ per instance; a token issued by A fails verification on B
Container without a mounted ./keys/A new key pair on every restart invalidates every existing token
All instances share one key pair and one RedisCorrect setup

Rotating Keys


An instance holds one key pair at a time. After switching keys:
RequestResult
Carries an Access Token signed with the old key (expired or not)400 data_invalid: signature verification runs before the expiry check, so no refresh happens
Carries only the Refresh IDRefresh succeeds and signs with the new key

The browser access_token cookie expires together with AccessTokenExpires; after that, requests carry only the Refresh ID and refresh automatically, while earlier requests get 400. To recover immediately, the client clears the Access Token on 400 and retries.
中文