Skip to content

chore: use encrypted jwt secret#1988

Merged
rolznz merged 5 commits intomasterfrom
chore/encrypted-jwt-secret
Dec 16, 2025
Merged

chore: use encrypted jwt secret#1988
rolznz merged 5 commits intomasterfrom
chore/encrypted-jwt-secret

Conversation

@rolznz
Copy link
Copy Markdown
Contributor

@rolznz rolznz commented Dec 16, 2025

Fixes #1986

NOTES

  • JWT_SECRET env variable has been removed to reduce complexity. Now we always use a generated JWT secret.
  • The migration will log out any apps that use the hub API with a developer access token, so users will need to generate a new developer token after updating to 1.21.1.

TODOs:

  • if JWT secret exists but is not encrypted, do 1-time migration to encrypt it (actually generates a brand new secret, it doesn't encrypt the existing one)
  • if JWT secret does not exist, generate a new JWT secret and save it encrypted with the unlock password
  • Simplify change password to just delete the JWT secret

Tests

  • ensure all sessions are logged out after password change
  • cannot login with invalid password
  • check DB entry that newly-generated JWT secret is encrypted

@rolznz rolznz marked this pull request as ready for review December 16, 2025 15:39

newSecret, err := randomHex(32)
// delete the JWT secret so it will be re-generated on next unlock (to log all sessions out on password change)
err = tx.Where(&db.UserConfig{Key: "JWTSecret"}).Delete(&db.UserConfig{}).Error
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.

we currently create the secret in the Unlock function, don't we? so it's a bit of a side effect and not explicit and easy noticeable.

is the deletion here enough? or maybe could it be RegenerateJWTSecret() or something?

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 understand there is a side effect, but I think this is inevitable because now the JWT secret is stored decrypted in memory - and that is a side effect that we have to keep in mind. Also this code is inside a DB transaction, so the code for generating a new JWT secret had to be duplicated and slightly different than the other generation code.

I covered this quite well with tests I think. Do you think it's ok as-is?

// NOTE: the config is also unlocked as part of the start
// goroutine below. But since we execute start asynchronously it's hard to
// know when the config has been unlocked before being able to create the JWT token
err := httpSvc.cfg.Unlock(startRequest.UnlockPassword)
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.

so it runs twice potentially in parallel?

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, it's not in parallel - this first call here is synchronous

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.

Actually it doesn't make sense to call it in start. I can just call it here since it's only needed for the HTTP mode. So maybe the name of this function config.Unlock should also be renamed to be UnlockJWTSecret)

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.

Update: this doesn't work so well with auto-unlock. I will leave this as a follow-up improvement

@rolznz rolznz requested a review from bumi December 16, 2025 16:58
@rolznz rolznz merged commit 1f1aacf into master Dec 16, 2025
10 of 11 checks passed
@rolznz rolznz deleted the chore/encrypted-jwt-secret branch December 16, 2025 17:25
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.

Encrypt config JWTSecret with unlock password

2 participants