Skip to content

bug: load JWT signing secret from JWT_SECRET env var instead of hard-coding it - #1062

Open
devin-ai-integration[bot] wants to merge 1 commit into
masterfrom
devin/1788253207-jwt-secret-env
Open

devin-ai-integration[bot] wants to merge 1 commit into
masterfrom
devin/1788253207-jwt-secret-env

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Sep 1, 2026

Copy link
Copy Markdown

Summary

The HS512 key that signs and verifies every auth token was committed in application.properties, so anyone with repo read access could forge a token for any user id and JwtTokenFilter would authenticate it. The literal is removed; the key now comes only from the environment.

  • application.properties: jwt.secret=${JWT_SECRET:} (no committed default).
  • DefaultJwtService: new buildSigningKey(secret, algorithm)
    • blank/unset -> Keys.secretKeyFor(HS512) (random per-process key) + log.warn, so a misconfigured deploy cannot fall back to a known key;
    • secret shorter than 64 bytes -> IllegalStateException. This matters because jjwt's single-arg signWith(key) silently downgrades to HS256/HS384 for short keys; signing is also pinned via signWith(signingKey, signatureAlgorithm).
  • Tests: cross-secret token rejection, short-secret rejection, and that two no-secret instances don't accept each other's tokens. The existing test secret was 60 chars -> bumped to 64.
  • README documents JWT_SECRET.

Rotation required at source: the previously committed key must be treated as compromised and rotated in every environment that used it (owner: whoever operates the deployed instances). No git history rewrite was performed.

Not verified locally: Maven Central and the Gradle plugin portal are outside this environment's network allowlist, so ./gradlew compileJava test cannot resolve dependencies here, and the repo has no GitHub Actions CI.


Devin session

Devin-Org: engineering


Devin Review

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

Devin Review

Comment on lines +39 to +45
if (secret == null || secret.trim().isEmpty()) {
log.warn(
"No JWT signing secret configured (set the JWT_SECRET environment variable to at least "
+ "{} bytes). Generating a random key: tokens will be invalidated on every restart "
+ "and will not be accepted by other instances.",
MINIMUM_SECRET_BYTES);
return Keys.secretKeyFor(algorithm);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🟨 Missing secret silently destabilizes authentication

When JWT_SECRET is blank, buildSigningKey starts with a process-local key. Restarts and replicas then reject valid user sessions.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

0 participants