Skip to content

Commit 8411e7d

Browse files
committed
refactor(jwt): implement ADR-T-007 Phase 2 — per-purpose signing keys and RFC 7519 claims
Split the single shared JWT signing secret into two separate keys: `session_signing_key` and `email_verification_signing_key`, so a compromise of one key does not affect the other token type. Session tokens (`SessionClaims`, née `UserClaims`) now carry proper RFC 7519 registered claims: `sub` (user ID), `iss` ("torrust-index"), `aud` ("session"), `iat`, and `exp`. The `role` and `username` fields are advisory only — the authoritative role is always re-checked from the database on each authenticated request. Email-verification tokens (`VerifyClaims`) gain an `aud` ("email-verification") claim and `iat`, and are now validated with audience + issuer checks by the `jsonwebtoken` library instead of a manual `iss` string comparison. Other changes: - Enforce a 32-byte minimum length on `JwtSigningSecret`. - Accept `jwt_signing_secret` and `user_claim_token_pepper` as legacy serde aliases for `session_signing_key` for backward compatibility. - Provide `UserClaims` as a type alias so existing imports keep compiling. - Update all config files, doc examples, and tests. Ref: ADR-T-007
1 parent b6f51c3 commit 8411e7d

34 files changed

Lines changed: 275 additions & 110 deletions

.env.local

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
DATABASE_URL=sqlite://storage/database/data.db?mode=rwc
22
TORRUST_INDEX_CONFIG_TOML=
3-
TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__JWT_SIGNING_SECRET=MaxVerstappenWC2021
3+
TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__SESSION_SIGNING_KEY=MaxVerstappenWC2021-session-key!
4+
TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__EMAIL_VERIFICATION_SIGNING_KEY=MaxVerstappenWC2021-emailverify!
45
USER_ID=1000
56
TORRUST_TRACKER_CONFIG_TOML=
67
TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER=Sqlite3

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,15 @@ TORRUST_INDEX_CONFIG_TOML=$(cat "./storage/index/etc/index.toml") cargo run
9595

9696
_For deployment, you __should__ override:
9797

98-
- The `tracker_api_token` and the `index_auth_jwt_signing_secret` by using environmental variables:_
98+
- The `tracker_api_token` and the JWT signing keys by using environmental variables:_
9999

100100
```sh
101101
# Please use the secret that you generated for the torrust-tracker configuration.
102-
# Override secret in configuration using an environmental variable
102+
# Override secrets in configuration using environmental variables
103103
TORRUST_INDEX_CONFIG_TOML=$(cat "./storage/index/etc/index.toml") \
104104
TORRUST_INDEX_CONFIG_OVERRIDE_TRACKER__TOKEN=$(cat "./storage/tracker/lib/tracker_api_admin_token.secret") \
105-
TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__JWT_SIGNING_SECRET="MaxVerstappenWC2021" \
105+
TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__SESSION_SIGNING_KEY="your-session-signing-secret-here!" \
106+
TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__EMAIL_VERIFICATION_SIGNING_KEY="your-email-verify-secret-here!!" \
106107
cargo run
107108
```
108109

@@ -127,7 +128,7 @@ The following services are provided by the default configuration:
127128
- [ADR-T-004: Remove `located-error` Package](adr/004-remove-located-error.md) — Replace the `torrust-index-located-error` wrapper with `tracing` for error context.
128129
- [ADR-T-005: Migrate to Rust Edition 2024](adr/005-edition-2024.md) — Migrate the entire workspace to `edition = "2024"` and raise the MSRV to 1.85.
129130
- [ADR-T-006: Refactor the Error System](adr/006-error-system-refactor.md) — Replace the 41-variant `ServiceError` god enum with domain-scoped error enums (`AuthError`, `UserError`, `TorrentError`, `CategoryTagError`) and a thin `ApiError` wrapper.
130-
- [ADR-T-007: Refactor the JWT System](adr/007-jwt-system-refactor.md) — Centralise JWT handling into `src/jwt.rs`, rename `ClaimTokenPepper``JwtSigningSecret`, make token lifetimes configurable, and fix panics in token parsing.
131+
- [ADR-T-007: Refactor the JWT System](adr/007-jwt-system-refactor.md) — Centralise JWT handling into `src/jwt.rs`, redesign claims to RFC 7519, split into per-purpose signing keys, and enforce minimum secret length.
131132

132133
## Contributing
133134

adr/007-jwt-system-refactor.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ADR-T-007: Refactor the JWT System
22

3-
**Status:** Phase 1 implemented
3+
**Status:** Phase 2 implemented
44
**Date:** 2026-04-14
55

66
## Context
@@ -362,9 +362,9 @@ phased rollout that subsumes Options A and B.
362362
`session_token_lifetime_secs`,
363363
`email_verification_token_lifetime_secs`.
364364

365-
#### Phase 2 — Claim Redesign + Per-Purpose Keys (Option B scope)
365+
#### Phase 2 — Claim Redesign + Per-Purpose Keys (Option B scope) ✅ Implemented
366366

367-
- Redesign `UserClaims``SessionClaims`:
367+
- Redesign `UserClaims``SessionClaims`:
368368
```rust
369369
struct SessionClaims {
370370
sub: UserId, // subject = user ID
@@ -376,14 +376,15 @@ phased rollout that subsumes Options A and B.
376376
username: String, // convenience, non-authoritative
377377
}
378378
```
379-
- Redesign `VerifyClaims` with `aud: "email-verification"`.
380-
- Split config into two independent keys:
379+
- Redesign `VerifyClaims` with `aud: "email-verification"`.
380+
- Split config into two independent keys:
381381
`auth.session_signing_key` and
382382
`auth.email_verification_signing_key`.
383-
- Re-validate the user's role from the database on every
384-
authenticated request (with a short-lived cache) so the token
385-
role is advisory only.
386-
- Enforce a minimum secret length at config validation time.
383+
- ✅ Re-validate the user's role from the database on every
384+
authenticated request (the authorization service already does
385+
this via `get_role`) so the token role is advisory only.
386+
- ✅ Enforce a minimum secret length (32 bytes) at config
387+
validation time.
387388
- **Breaking change:** existing HS256 tokens are invalidated;
388389
users must re-login.
389390

compose.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ services:
1313
- TORRUST_INDEX_DATABASE=${TORRUST_INDEX_DATABASE:-e2e_testing_sqlite3}
1414
- TORRUST_INDEX_DATABASE_DRIVER=${TORRUST_INDEX_DATABASE_DRIVER:-sqlite3}
1515
- TORRUST_INDEX_CONFIG_OVERRIDE_TRACKER__TOKEN=${TORRUST_INDEX_CONFIG_OVERRIDE_TRACKER__TOKEN:-MyAccessToken}
16-
- TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__JWT_SIGNING_SECRET=${TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__JWT_SIGNING_SECRET:-MaxVerstappenWC2021}
16+
- TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__SESSION_SIGNING_KEY=${TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__SESSION_SIGNING_KEY:-MaxVerstappenWC2021-session-key!}
17+
- TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__EMAIL_VERIFICATION_SIGNING_KEY=${TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__EMAIL_VERIFICATION_SIGNING_KEY:-MaxVerstappenWC2021-emailverify!}
1718
networks:
1819
- server_side
1920
ports:

contrib/dev-tools/container/e2e/sqlite/mode/private/e2e-env-up.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ USER_ID=${USER_ID:-1000} \
88
TORRUST_INDEX_DATABASE="e2e_testing_sqlite3" \
99
TORRUST_INDEX_DATABASE_DRIVER="sqlite3" \
1010
TORRUST_INDEX_CONFIG_OVERRIDE_TRACKER__TOKEN="MyAccessToken" \
11-
TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__JWT_SIGNING_SECRET="MaxVerstappenWC2021" \
11+
TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__SESSION_SIGNING_KEY="MaxVerstappenWC2021-session-key!" \
1212
TORRUST_TRACKER_CONFIG_TOML=$(cat ./share/default/config/tracker.private.e2e.container.sqlite3.toml) \
1313
TORRUST_TRACKER_DATABASE="e2e_testing_sqlite3" \
1414
TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER="sqlite3" \

contrib/dev-tools/container/e2e/sqlite/mode/public/e2e-env-up.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ USER_ID=${USER_ID:-1000} \
88
TORRUST_INDEX_DATABASE="e2e_testing_sqlite3" \
99
TORRUST_INDEX_DATABASE_DRIVER="sqlite3" \
1010
TORRUST_INDEX_CONFIG_OVERRIDE_TRACKER__TOKEN="MyAccessToken" \
11-
TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__JWT_SIGNING_SECRET="MaxVerstappenWC2021" \
11+
TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__SESSION_SIGNING_KEY="MaxVerstappenWC2021-session-key!" \
1212
TORRUST_TRACKER_CONFIG_TOML=$(cat ./share/default/config/tracker.public.e2e.container.sqlite3.toml) \
1313
TORRUST_TRACKER_DATABASE="e2e_testing_sqlite3" \
1414
TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER="sqlite3" \

docs/containers.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,8 @@ The following environmental variables can be set:
149149

150150
- `TORRUST_INDEX_CONFIG_TOML_PATH` - The in-container path to the index configuration file, (default: `"/etc/torrust/index/index.toml"`).
151151
- `TORRUST_INDEX_CONFIG_OVERRIDE_TRACKER__TOKEN` - Override of the admin token. If set, this value overrides any value set in the config.
152-
- `TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__JWT_SIGNING_SECRET` - Override of the auth JWT signing secret. If set, this value overrides any value set in the config.
152+
- `TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__SESSION_SIGNING_KEY` - Override of the auth session signing key. If set, this value overrides any value set in the config.
153+
- `TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__EMAIL_VERIFICATION_SIGNING_KEY` - Override of the auth email-verification signing key. If set, this value overrides any value set in the config.
153154
- `TORRUST_INDEX_DATABASE_DRIVER` - The database type used for the container, (options: `sqlite3`, `mysql`, default `sqlite3`). Please Note: This dose not override the database configuration within the `.toml` config file.
154155
- `TORRUST_INDEX_CONFIG_TOML` - Load config from this environmental variable instead from a file, (i.e: `TORRUST_INDEX_CONFIG_TOML=$(cat index-index.toml)`).
155156
- `USER_ID` - The user id for the runtime crated `torrust` user. Please Note: This user id should match the ownership of the host-mapped volumes, (default `1000`).
@@ -202,7 +203,7 @@ mkdir -p ./storage/index/lib/ ./storage/index/log/ ./storage/index/etc/
202203
## Run Torrust Index Container Image
203204
docker run -it \
204205
--env TORRUST_INDEX_CONFIG_OVERRIDE_TRACKER__TOKEN="MySecretToken" \
205-
--env TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__JWT_SIGNING_SECRET="MaxVerstappenWC2021" \
206+
--env TORRUST_INDEX_CONFIG_OVERRIDE_AUTH__SESSION_SIGNING_KEY="MaxVerstappenWC2021-session-key!" \
206207
--env USER_ID="$(id -u)" \
207208
--publish 0.0.0.0:3001:3001/tcp \
208209
--volume ./storage/index/lib:/var/lib/torrust/index:Z \

share/default/config/index.container.mysql.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ threshold = "info"
1515
token = "MyAccessToken"
1616

1717
[auth]
18-
jwt_signing_secret = "MaxVerstappenWC2021"
18+
session_signing_key = "MaxVerstappenWC2021-session-key!"
19+
email_verification_signing_key = "MaxVerstappenWC2021-emailverify!"
1920

2021
[database]
2122
connect_url = "mysql://root:root_secret_password@mysql:3306/torrust_index"

share/default/config/index.container.sqlite3.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ threshold = "info"
1515
token = "MyAccessToken"
1616

1717
[auth]
18-
jwt_signing_secret = "MaxVerstappenWC2021"
18+
session_signing_key = "MaxVerstappenWC2021-session-key!"
19+
email_verification_signing_key = "MaxVerstappenWC2021-emailverify!"
1920

2021
[database]
2122
connect_url = "sqlite:///var/lib/torrust/index/database/sqlite3.db?mode=rwc"

share/default/config/index.development.sqlite3.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ threshold = "info"
1717
token = "MyAccessToken"
1818

1919
[auth]
20-
jwt_signing_secret = "MaxVerstappenWC2021"
20+
session_signing_key = "MaxVerstappenWC2021-session-key!"
21+
email_verification_signing_key = "MaxVerstappenWC2021-emailverify!"
2122

2223
# Uncomment if you want to enable TSL for development
2324
#[net.tsl]

0 commit comments

Comments
 (0)