Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/CristianRR94/springCommunity/llms.txt

Use this file to discover all available pages before exploring further.

Every authenticated interaction with Spring Community is backed by a JSON Web Token signed with an HMAC-SHA algorithm. The platform issues two distinct token types on every login or registration event: an ACCESS token for short-lived API authorization and a REFRESH token for obtaining a new pair without re-entering credentials. Both token strings are persisted in the tokens database table, enabling the server to revoke individual tokens — or all tokens belonging to a user — at any time, regardless of their cryptographic expiry.

Token Types

ACCESS

Used to authorize requests to protected REST endpoints. Sent in the Authorization: Bearer header on every call.
  • Default expiry: 3600000 ms (1 hour)
  • Environment variable: JWT_EXPIRATION
  • TipoToken value: "ACCESS"

REFRESH

Used exclusively with POST /auth/refresh to obtain a new token pair. Must never be sent to regular API endpoints.
  • Default expiry: 604800000 ms (7 days)
  • Environment variable: JWT_REFRESH_EXPIRATION
  • TipoToken value: "REFRESH"
Both values fall back to their defaults when the corresponding environment variable is absent, as declared in application.properties:
jwt.expiration=${JWT_EXPIRATION:3600000}
jwt.refresh.expiration=${JWT_REFRESH_EXPIRATION:604800000}

JWT Claims

The following claims are embedded in every token by JwtProviderServiceImpl.buildToken(). Custom claim names come directly from the ClaimJwt enum string values.
Claim keyClaimJwt constantDescriptionExample value
subSubject — the user’s display name (usuario.getNombre())"alice"
usuarioIdUSUARIO_IDThe user’s numeric primary-key from the database42
nombreNOMBREUser’s display name (mirrors sub)"alice"
tipo_usoTIPO_USOToken type string — either "ACCESS" or "REFRESH""ACCESS"
rolesROLESList of Spring GrantedAuthority strings for the user["ROLE_USUARIO"]
jtiRandom UUID assigned per token via .id(UUID.randomUUID()...)"d4e8f1a2-…"
iatIssued-at timestamp (Unix epoch seconds)1731660000
expExpiration timestamp (Unix epoch seconds)1731663600
A decoded ACCESS token payload looks like this:
{
  "sub": "alice",
  "usuarioId": 42,
  "nombre": "alice",
  "tipo_uso": "ACCESS",
  "roles": ["ROLE_USUARIO"],
  "jti": "d4e8f1a2-3b5c-4d6e-9f0a-1b2c3d4e5f6a",
  "iat": 1731660000,
  "exp": 1731663600
}

Token Signing

Tokens are signed using HMAC-SHA via the JJWT library. The signing key is derived from a base64-encoded secret loaded from the JWT_SECRET environment variable:
// JwtProviderServiceImpl.getSigninKey()
byte[] keyBytes = Decoders.BASE64.decode(secretKey);
return Keys.hmacShaKeyFor(keyBytes);
Keys.hmacShaKeyFor() automatically selects the strongest HMAC-SHA variant (HS256, HS384, or HS512) that is appropriate for the key length. At verification time the same key is used by Jwts.parser().verifyWith(getSigninKey()).
JWT_SECRET must be a base64-encoded string long enough for the HMAC-SHA algorithm you intend to use (at least 32 bytes decoded for HS256, 48 for HS384, 64 for HS512). Never hard-code this value in application.properties or commit it to source control — always supply it through a secrets manager or environment variable at runtime.

Token Validation

Incoming HTTP requests pass through JwtAuthFilter, a OncePerRequestFilter that runs before Spring Security’s UsernamePasswordAuthenticationFilter. The filter performs the following checks in order:
1

Skip auth routes

Requests whose path contains /auth bypass the filter entirely — those endpoints are public.
2

Extract the Bearer token

The Authorization header is read. If it is absent or does not start with "Bearer ", the request continues unauthenticated (Spring Security will reject it if the route is protected).
3

Extract the username

jwtProviderService.extractUsername(jwtToken) parses the sub claim. A null result or an already-authenticated context short-circuits the filter.
4

Check the database record

tokenRepository.findByToken(jwtToken) is called. If the token is not found, or its isExpired or isRevoked flags are true, or its tipoUso is not TipoToken.ACCESS, the request continues without authentication.
5

Validate signature and expiry

jwtProviderService.isTokenValid(jwtToken, userDetails) confirms the username matches and the cryptographic expiry has not passed.
6

Set the Security context

A UsernamePasswordAuthenticationToken is built from the loaded UserDetails and placed in the SecurityContextHolder, granting the request its full authority set.

Refreshing a Token

When your ACCESS token expires, POST the current REFRESH token to /auth/refresh. The endpoint validates that the token is of type REFRESH, that it has not been revoked, and that its signature is valid. If all checks pass, all existing tokens for the user are revoked and a brand-new token pair is returned.
curl -X POST http://localhost:8080/auth/refresh \
  -H "Authorization: Bearer <refreshToken>"
A successful response returns a new TokenResponse:
{
  "access_token": "eyJhbGciOiJIUzI1NiJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiJ9..."
}
Replace both the stored access_token and refresh_token after a successful refresh call. The old refresh token is revoked immediately and cannot be used again.

Token Revocation

Token revocation is handled by TokenManagementServiceImpl.revokeAllUserTokens(). It fetches every non-expired, non-revoked token belonging to the user and sets both expired = true and revoked = true, then flushes all changes in a single saveAll() call. Revocation is triggered in two situations:
  1. Refresh — old tokens are revoked before the new pair is saved.
  2. Logout — the POST /auth/logout handler reads the Authorization header and calls revokeAllTokensByToken(token), which resolves the owning user from the token record and then calls revokeAllUserTokens().
curl -X POST http://localhost:8080/auth/logout \
  -H "Authorization: Bearer <accessToken>"
After logout, the SecurityContext is cleared by Spring’s logoutSuccessHandler and all subsequent requests with the old tokens will be rejected by JwtAuthFilter at the database-check step, even if the JWT signature itself is still cryptographically valid. The Token entity fields involved in revocation:
// com.project.community.entidades.Token
private boolean revoked;   // set to true on revocation
private boolean expired;   // set to true on revocation
private TipoToken tipoUso; // ACCESS | REFRESH

Build docs developers (and LLMs) love