JWT Integration
CoSec uses the Auth0 java-jwt library to create and verify JSON Web Tokens. The integration is encapsulated in the cosec-jwt module, which provides JwtTokenConverter (issue tokens) and JwtTokenVerifier (verify and extract principals). Spring Boot auto-configuration wires everything together.
Token Lifecycle
Token Validity Defaults
| Token Type | Default Validity | Configurable Via |
|---|---|---|
| Access Token | 10 minutes | cosec.jwt.token-validity.access |
| Refresh Token | 7 days | cosec.jwt.token-validity.refresh |
These defaults are defined in JwtProperties:
data class TokenValidity(
var access: Duration = Duration.ofMinutes(10),
var refresh: Duration = Duration.ofDays(7)
)Supported Algorithms
The auto-configuration supports three HMAC algorithms, selected via cosec.jwt.algorithm:
| Value | Algorithm | Javadoc |
|---|---|---|
HMAC256 (default) | HS256 | Algorithm.HMAC256(secret) |
HMAC384 | HS384 | Algorithm.HMAC384(secret) |
HMAC512 | HS512 | Algorithm.HMAC512(secret) |
JWT Claims Structure
JwtTokenConverter builds a JWT with the following claims structure for access tokens:
{
"jti": "<generated-unique-id>",
"sub": "<principal.id>",
"iat": 1684000000000,
"exp": 1684000600000,
"policies": ["policy-id-1", "policy-id-2"],
"roles": ["admin", "user"],
"attributes": {"key": "value"},
"tenantId": "tenant-123"
}Key mappings:
sub(Subject): Set toprincipal.id-- the unique user identifierjti(JWT ID): Generated by anIdGenerator(default: UUID). Used for token revocation and refresh token bindingpolicies:PolicyCapable.POLICY_KEYclaim -- list of policy IDs assigned to the principalroles:RoleCapable.ROLE_KEYclaim -- list of role IDsattributes:CoSecPrincipal::attributes.nameclaim -- arbitrary key-value metadatatenantId:Tenant.TENANT_ID_KEYclaim -- present only when the principal implementsTenantCapable
Refresh tokens have a simpler structure:
{
"jti": "<refresh-token-id>",
"sub": "<access-token-id>",
"iat": 1684000000000,
"exp": 1685209600000
}The refresh token's sub claim is set to the access token's jti, creating a binding between the two tokens.
Key Classes
JwtTokenConverter
JwtTokenConverter implements TokenConverter and converts a CoSecPrincipal into a CompositeToken:
class JwtTokenConverter(
private val idGenerator: IdGenerator,
private val algorithm: Algorithm,
private val accessTokenValidity: Duration = Duration.ofMinutes(10),
private val refreshTokenValidity: Duration = Duration.ofDays(7)
) : TokenConverterJwtTokenVerifier
JwtTokenVerifier implements TokenVerifier and provides:
verify(AccessToken): Validates signature, checks expiry, extractsTokenPrincipalrefresh(CompositeToken): Verifies the refresh token, ensures itssubmatches the access token'sjti, then extracts the principal from the (possibly expired) access token
Jwts Utility
Jwts provides helper functions:
decode(token): StripsBearerprefix and decodes the JWT without verificationtoPrincipal(decodedJWT): Extracts all claims and constructs aTokenPrincipal(orTokenTenantPrincipalwhentenantIdis present)removeBearerPrefix(): String extension that removes the"Bearer "prefix if present
Architecture Diagrams
Token Creation Flow
sequenceDiagram
autonumber
participant Auth as TokenCompositeAuthentication
participant CA as CompositeAuthentication
participant AP as AuthenticationProvider
participant Impl as Authentication Impl
participant Conv as JwtTokenConverter
participant JWT as JWT.create()
Auth->>CA: authenticate(credentials)
CA->>AP: get(credentialsType)
AP-->>CA: Authentication instance
CA->>Impl: authenticate(credentials)
Impl-->>Auth: CoSecPrincipal
Auth->>Conv: toToken(principal)
Conv->>JWT: create access token (sub=policies=roles=tenantId)
JWT-->>Conv: signed access token string
Conv->>JWT: create refresh token (sub=accessTokenId)
JWT-->>Conv: signed refresh token string
Conv-->>Auth: CompositeToken(accessToken, refreshToken)Token Verification Flow
flowchart TD
A["Incoming AccessToken"] --> B["removeBearerPrefix()"]
B --> C["jwtVerifier.verify(token)"]
C --> D{"Verification result"}
D -->|"TokenExpiredException"| E["throw TokenExpiredException"]
D -->|"Other Exception"| F["throw TokenVerificationException"]
D -->|"Valid DecodedJWT"| G["Jwts.toPrincipal(decodedJWT)"]
G --> H["Extract sub, policies, roles, attributes"]
H --> I{"tenantId claim present?"}
I -->|"yes"| J["return TokenTenantPrincipal"]
I -->|"no"| K["return TokenPrincipal"]
style A fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style B fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style C fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style D fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style E fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style F fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style G fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style H fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style I fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style J fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style K fill:#2d333b,stroke:#6d5dfc,color:#e6edf3Refresh Token Flow
sequenceDiagram
autonumber
participant Client
participant Verifier as JwtTokenVerifier
participant JWT as JWT.require()
participant Jwts as Jwts
Client->>Verifier: refresh(CompositeToken)
Verifier->>JWT: verify(refreshToken)
JWT-->>Verifier: DecodedJWT (refresh)
Verifier->>Jwts: decode(accessToken) -- no verification
Jwts-->>Verifier: DecodedJWT (access, possibly expired)
Verifier->>Verifier: require(refresh.sub == access.jti)
Verifier->>Jwts: toPrincipal(accessJWT)
Jwts-->>Verifier: TokenPrincipal
Verifier-->>Client: TokenPrincipalSpring Boot Auto-Configuration
CoSecJwtAutoConfiguration is activated when:
cosec.enabled=true(default)cosec.jwt.enabled=true(default)JwtTokenConverteris on the classpath
It registers three beans:
| Bean | Type | Purpose |
|---|---|---|
cosecTokenAlgorithm | Algorithm | HMAC algorithm from config |
cosecTokenConverter | TokenConverter | Creates JWT tokens |
cosecJwtTokenVerifier | TokenVerifier | Verifies JWT tokens |
When authentication is also enabled, it additionally registers TokenCompositeAuthentication which chains credential-based authentication with token issuance.
Configuration Example
cosec:
jwt:
enabled: true
algorithm: HMAC256
secret: your-secret-key-must-be-long-enough
token-validity:
access: 10m
refresh: 7dReferences
- JwtTokenConverter.kt:42 - JWT token creation with claims
- JwtTokenVerifier.kt:37 - JWT verification and principal extraction
- Jwts.kt:41 - JWT utility functions (decode, toPrincipal, removeBearerPrefix)
- CoSecJwtAutoConfiguration.kt:47 - Spring Boot auto-configuration
- JwtProperties.kt:28 - Configuration properties
Related Pages
- Authentication System - How JWT plugs into the provider registry
- Token Management - Token hierarchy and principal types
- Social Authentication - OAuth-based authentication alternative
- Authorization Flow - How token claims drive authorization decisions