feat: implement JWT utility class with configurable properties and to…#293
feat: implement JWT utility class with configurable properties and to…#293lhpqaq merged 3 commits intoapache:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a configurable JWT utility (secret/issuer/audience/expiration) and wires it into login/auth flows, with accompanying config and test updates.
Changes:
- Add
JwtPropertiesconfiguration and enable it in the Spring Boot application. - Refactor
JWTUtilsfrom static helpers to a Spring component using configurable signing/verification parameters (plus stricter verification). - Update login/auth code paths and tests; add dev-support container wiring for JWT secret env var.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| dev-support/docker/containers/build.sh | Sets/passes JWT secret into dev Docker containers and attempts to inject it into server config. |
| bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/utils/JWTUtils.java | New Spring-managed JWT utility using JwtProperties, stricter verification, configurable secret. |
| bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/config/JwtProperties.java | Adds bindable JWT configuration properties. |
| bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/BigtopManagerServer.java | Enables configuration properties binding for JwtProperties. |
| bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/service/impl/LoginServiceImpl.java | Switches token generation to injected JWTUtils. |
| bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/interceptor/AuthInterceptor.java | Switches token verification to injected JWTUtils. |
| bigtop-manager-server/src/main/resources/application.yml | Adds JWT configuration section and env-var secret placeholder. |
| bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/utils/JWTUtilsTest.java | Updates tests for instance-based JWTUtils and new verification rules. |
| bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/service/LoginServiceTest.java | Mocks JWTUtils to keep login unit test isolated from signing config. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| docker exec ${container} bash -c "grep -q '^\s*secret:' /opt/bigtop-manager-server/conf/application.yml || true" | ||
| docker exec ${container} bash -c "sed -i 's/^\(\s*secret:\).*/\1 \${BIGTOP_MANAGER_JWT_SECRET}/' /opt/bigtop-manager-server/conf/application.yml || true" |
There was a problem hiding this comment.
The grep/sed patterns use \s to match whitespace, but POSIX grep/sed don’t treat \s as whitespace. As a result, these commands won’t match secret: lines and the “inject JWT secret” step is effectively a no-op. Use a portable whitespace class (e.g., [[:space:]]) and make the logic actually update/ensure the intended YAML key.
| docker exec ${container} bash -c "grep -q '^\s*secret:' /opt/bigtop-manager-server/conf/application.yml || true" | |
| docker exec ${container} bash -c "sed -i 's/^\(\s*secret:\).*/\1 \${BIGTOP_MANAGER_JWT_SECRET}/' /opt/bigtop-manager-server/conf/application.yml || true" | |
| docker exec ${container} bash -c "if grep -q '^[[:space:]]*secret:' /opt/bigtop-manager-server/conf/application.yml; then sed -i 's/^\([[:space:]]*secret:\).*/\1 \${BIGTOP_MANAGER_JWT_SECRET}/' /opt/bigtop-manager-server/conf/application.yml; else printf '\nsecret: %s\n' \"\${BIGTOP_MANAGER_JWT_SECRET}\" >> /opt/bigtop-manager-server/conf/application.yml; fi || true" |
| throw new IllegalStateException( | ||
| "JWT secret is not configured. Please set bigtop-manager.security.jwt.secret (or enable allowDefaultSecret for dev only)."); | ||
| } |
There was a problem hiding this comment.
getSigningSecret() throws IllegalStateException when the secret is missing, which will be handled by the global exception handler as a generic 500. That makes a common misconfiguration hard to diagnose and can surface as an opaque runtime failure on login/auth. Consider failing fast at startup (validation) or throwing/handling a domain-specific exception that returns a clearer configuration error response.
| throw new IllegalStateException( | |
| "JWT secret is not configured. Please set bigtop-manager.security.jwt.secret (or enable allowDefaultSecret for dev only)."); | |
| } | |
| throw new JwtConfigurationException( | |
| "JWT secret is not configured. Please set bigtop-manager.security.jwt.secret (or enable allowDefaultSecret for dev only)."); | |
| } | |
| /** | |
| * Exception indicating that the JWT subsystem is misconfigured, for example | |
| * when the signing secret is missing in a non-development environment. | |
| */ | |
| static class JwtConfigurationException extends RuntimeException { | |
| JwtConfigurationException(String message) { | |
| super(message); | |
| } | |
| } |
| public DecodedJWT resolveToken(String token) throws JWTVerificationException { | ||
| Algorithm algorithm = Algorithm.HMAC256(getSigningSecret()); | ||
| JWTVerifier verifier = JWT.require(algorithm) | ||
| .withIssuer(jwtProperties.getIssuer()) | ||
| .withAudience(jwtProperties.getAudience()) | ||
| .build(); | ||
|
|
||
| DecodedJWT decodedJWT = verifier.verify(token); | ||
|
|
||
| // Enforce issued-at to mitigate tokens without freshness metadata. | ||
| Date issuedAt = decodedJWT.getIssuedAt(); | ||
| if (issuedAt == null) { | ||
| throw new JWTVerificationException("Missing iat"); | ||
| } |
There was a problem hiding this comment.
resolveToken() now requires issuer/audience and enforces presence of iat. Tokens minted by the previous implementation (no issuer/audience/iat) will start failing verification immediately after upgrade, forcing all clients to re-authenticate. If that’s intended, it should be explicitly documented as a breaking change; otherwise consider a compatibility mode/migration window (e.g., optionally accept missing fields for a period).
| # IMPORTANT: Set a strong, random secret in production (e.g. via env var BIGTOP_MANAGER_JWT_SECRET) | ||
| secret: ${BIGTOP_MANAGER_JWT_SECRET:} | ||
| issuer: bigtop-manager | ||
| audience: bigtop-manager | ||
| expiration-days: 7 | ||
| # Dev-only compatibility switch. Keep false in production. | ||
| allow-default-secret: false |
There was a problem hiding this comment.
With secret: ${BIGTOP_MANAGER_JWT_SECRET:} and allow-default-secret: false, a default install where the env var isn’t set will allow the app to start but will later fail token generation/verification at runtime (typically as 500s). If the intention is to require an explicit secret everywhere, consider failing fast during startup or providing a clear dev/default profile so contributors don’t hit opaque runtime errors.
| # Generate a random JWT secret for local dev if user didn't provide one. | ||
| init_jwt_secret() { | ||
| if [ -z "${BIGTOP_MANAGER_JWT_SECRET}" ]; then | ||
| # Dev-support default. Override by exporting BIGTOP_MANAGER_JWT_SECRET before running this script. | ||
| BIGTOP_MANAGER_JWT_SECRET="r0PGVyvjKOxUBwGt" | ||
| export BIGTOP_MANAGER_JWT_SECRET | ||
| log "Defaulted BIGTOP_MANAGER_JWT_SECRET for dev-support" |
There was a problem hiding this comment.
init_jwt_secret says it "Generate[s] a random JWT secret", but it actually sets a hard-coded, well-known value. This is misleading and also makes local environments share the same signing key, which weakens token integrity even in dev. Either generate a per-run/per-dev random secret (and optionally persist it) or update the comment/behavior to clearly indicate it’s a fixed dev default.
| # Generate a random JWT secret for local dev if user didn't provide one. | |
| init_jwt_secret() { | |
| if [ -z "${BIGTOP_MANAGER_JWT_SECRET}" ]; then | |
| # Dev-support default. Override by exporting BIGTOP_MANAGER_JWT_SECRET before running this script. | |
| BIGTOP_MANAGER_JWT_SECRET="r0PGVyvjKOxUBwGt" | |
| export BIGTOP_MANAGER_JWT_SECRET | |
| log "Defaulted BIGTOP_MANAGER_JWT_SECRET for dev-support" | |
| # Set a fixed default JWT secret for local dev if user didn't provide one. | |
| init_jwt_secret() { | |
| if [ -z "${BIGTOP_MANAGER_JWT_SECRET}" ]; then | |
| # Fixed dev-support default. Override by exporting BIGTOP_MANAGER_JWT_SECRET before running this script. | |
| BIGTOP_MANAGER_JWT_SECRET="r0PGVyvjKOxUBwGt" | |
| export BIGTOP_MANAGER_JWT_SECRET | |
| log "Using fixed default BIGTOP_MANAGER_JWT_SECRET for dev-support" |
…ken management