Class SecurityContext
This class provides static methods to access and manage security tokens, certificates, and service plans on a per-thread basis. Each thread maintains its own isolated context, making this class safe for use in multi-threaded environments such as web servers handling concurrent requests.
Core Features:
- Token Management: Store and retrieve access tokens, ID tokens, and XSUAA tokens
- Automatic Token Resolution: Lazy-load tokens via registered extensions when needed
- Token Caching: Cache resolved tokens
- Certificate Storage: Store X.509 client certificates for mTLS scenarios
- Service Plan Tracking: Store Identity Authentication Service plan information
Thread-Local Storage: All security context data is stored in a ThreadLocal
instance, ensuring thread isolation. Each thread gets its own SecurityContext containing
all security-related state. This design:
- Eliminates the need for multiple
ThreadLocalfields (reduces memory overhead) - Improves cache locality by grouping related data in one object
- Simplifies cleanup with
clear()removing all data in one call
Token Extension System: The class supports pluggable token resolution via extensions:
IdTokenExtension— Resolves ID tokens (e.g., via OAuth2 user token flow)XsuaaTokenExtension— Resolves XSUAA tokens (e.g., via IAS-to-XSUAA exchange)
Usage Example:
// 1. Register extensions (once at application startup)
SecurityContext.registerIdTokenExtension(new DefaultIdTokenExtension(tokenService, iasConfig));
SecurityContext.registerXsuaaTokenExtension(new DefaultXsuaaTokenExtension(tokenService, xsuaaConfig));
// 2. Set the initial token (e.g., in authentication filter)
Token accessToken = parseFromAuthorizationHeader(request);
SecurityContext.setToken(accessToken);
// 3. Access tokens in business logic
Token idToken = SecurityContext.getIdToken(); // Automatically resolved if not cached
Token xsuaaToken = SecurityContext.getXsuaaToken(); // Automatically exchanged if needed
// 4. Clean up after request (e.g., in filter's finally block)
SecurityContext.clear(); // Clears all tokens, certificate, and service plans
Token Lifecycle:
- Initial Token: Set via
setToken(Token)(typically from HTTP Authorization header) - Caching:
getIdToken()andgetXsuaaToken()cache resolved tokens - Re-resolution: Expired tokens trigger automatic re-resolution via extensions
- Cleanup:
clear()orclearContext()removes all tokens
Memory Management: When using thread pools (e.g., servlet containers), always call
clear() or clearContext() at the end of request processing to prevent memory
leaks. Thread pools reuse threads, so stale context data can accumulate if not cleaned up
properly.
Cross-Thread Usage: For advanced scenarios like asynchronous token exchange, you can capture the context and pass it to other threads:
SecurityContext ctx = SecurityContext.get();
CompletableFuture.supplyAsync(() -> {
Token exchangedToken = tokenExchange.exchange(ctx.token);
ctx.updateToken(exchangedToken); // Preserves other context properties
return callApi(exchangedToken);
});
- See Also:
-
Method Summary
Modifier and TypeMethodDescriptionstatic voidclear()Clears all security context data for the current thread.static voidRemoves the entireSecurityContextinstance from thread-local storage.static voidRemoves the service plans from the security context.static voidRemoves the current access token from the security context.static AccessTokenReturns the current access token as anAccessToken, if applicable.static CertificateReturns the X.509 client certificate for the current thread.static TokenRetrieves the ID token associated with the current context.static TokenReturns the initial access token for the current thread.Returns the Identity Authentication Service plans associated with the current request.static TokengetToken()Returns the current access token for this thread.static TokenRetrieves the XSUAA token associated with the current context.static voidRegisters a globalIdTokenExtensionfor automatic ID token resolution.static voidRegisters a globalXsuaaTokenExtensionfor automatic XSUAA token resolution/exchange.static voidsetClientCertificate(Certificate certificate) Sets the X.509 client certificate for the current thread's security context.static voidsetServicePlans(String servicePlansHeader) Parses and stores Identity Authentication Service plans from a comma-separated header value.static voidSets the access token for the current thread and resets the security context.static voidsetXsuaaToken(Token token) Sets the XSUAA token for the current thread.static voidupdateToken(Token token) Updates only the token field without clearing derived tokens or resetting context.
-
Method Details
-
getToken
Returns the current access token for this thread.This returns the token set via
setToken(Token)or replaced viaupdateToken(Token). In hybrid IAS/XSUAA scenarios, this may be either:- IAS token (from the HTTP Authorization header)
- XSUAA token (if exchanged via
updateToken(Token))
To get the original token before any exchanges, use
getInitialToken().- Returns:
- the current access token, or
nullif none is set - See Also:
-
setToken
Sets the access token for the current thread and resets the security context.This method:
- Sets
tokento the provided value - Saves a copy in
initialToken(preserved even iftokenis later replaced) - Clears cached derived tokens (
idToken,xsuaaToken)
Why Derived Tokens Are Cleared: ID tokens and XSUAA tokens are derived from the access token. When the access token changes, cached derived tokens become invalid and must be re-resolved.
What Is NOT Cleared:
- Client certificate — Not token-specific, may persist across token rotations
- Service plans — Part of the request context, not tied to the token
- Extensions — Global configuration, not thread-specific
Usage:
// In authentication filter Token accessToken = parseAuthorizationHeader(request); SecurityContext.setToken(accessToken);- Parameters:
token- the access token to set (typically from the HTTP Authorization header)
- Sets
-
updateToken
Updates only the token field without clearing derived tokens or resetting context.For internal usage only. Use
setToken(Token)for normal authentication scenarios. This method is intended for advanced scenarios like cross-thread token exchange where you want to preserve other context properties.Difference from
setToken(Token):Method Comparison Action setToken(Token)updateToken(Token)Updates token✅ ✅ Updates initialToken✅ ❌ Clears idToken✅ ❌ Clears xsuaaToken✅ ❌ Use Case: Cross-Thread Token Exchange
SecurityContext ctx = SecurityContext.get(); CompletableFuture.supplyAsync(() -> { Token exchangedToken = tokenExchange.exchange(ctx.token); ctx.updateToken(exchangedToken); // Preserves idToken, extensions, etc. return callApi(exchangedToken); });- Parameters:
token- the token to set
-
getInitialToken
Returns the initial access token for the current thread.This returns the original token set via
setToken(Token), even iftokenwas later replaced viaupdateToken(Token). Use this to access the original authentication token after token exchanges.Example: Hybrid IAS/XSUAA Scenario
// 1. IAS token from HTTP Authorization header SecurityContext.setToken(iasToken); // 2. Exchange to XSUAA token Token xsuaaToken = tokenExchange.exchange(iasToken); SecurityContext.get().updateToken(xsuaaToken); // 3. Access tokens Token current = SecurityContext.getToken(); // → xsuaaToken Token original = SecurityContext.getInitialToken(); // → iasToken- Returns:
- the initial access token, or
nullif none was set - See Also:
-
getAccessToken
Returns the current access token as anAccessToken, if applicable.This is a convenience method that performs type checking and casting. It returns
nullif the current token is not anAccessTokeninstance (e.g., if it's an ID token or custom token implementation).Usage:
AccessToken accessToken = SecurityContext.getAccessToken(); if (accessToken != null) { String clientId = accessToken.getClientId(); List<String> scopes = accessToken.getScopes(); }- Returns:
- the current token as
AccessToken, ornullif:- No token is set (
getToken()returnsnull) - The token is not an instance of
AccessToken
- No token is set (
-
clearToken
public static void clearToken()Removes the current access token from the security context.This clears
tokenbut does NOT clear:initialToken,idToken,xsuaaToken— Useclear()to remove all tokens
Subsequent calls to
getToken()will returnnulluntil a new token is set. -
getIdToken
Retrieves the ID token associated with the current context.This method implements lazy-loading and caching:
- Returns cached ID token if it exists and is valid
- If
IdTokenExtensionis registered, callsIdTokenExtension.resolveIdToken(Token) - Caches the resolved token for subsequent calls
- Returns the resolved token (or
nullif resolution failed)
Extension Requirement: Automatic resolution requires registering an
IdTokenExtensionviaregisterIdTokenExtension(IdTokenExtension). Without an extension, this method only returns previously cached tokens.- Returns:
- the ID token, or
nullif:- No extension is registered
- Token resolution fails (network error, invalid token, etc.)
- No access token is available for token exchange
- See Also:
-
setXsuaaToken
Sets the XSUAA token for the current thread.- Parameters:
token- the XSUAA token to set
-
getXsuaaToken
Retrieves the XSUAA token associated with the current context.This method implements lazy-loading, caching, and automatic token exchange:
- Returns cached XSUAA token if it exists and is valid
- If
XsuaaTokenExtensionis registered, callsXsuaaTokenExtension.resolveXsuaaToken(Token) - Caches the resolved/exchanged token for subsequent calls
- Returns the resolved token (or
nullif resolution failed)
Hybrid Authentication (Level 0 Migration): In hybrid scenarios where IAS tokens are received but XSUAA authorization is still used, this method automatically exchanges IAS tokens to XSUAA format via the registered
XsuaaTokenExtension. The exchanged token is cached to avoid repeated exchanges.Extension Requirement: Automatic exchange requires:
- Registering a
XsuaaTokenExtensionviaregisterXsuaaTokenExtension(XsuaaTokenExtension) - Proper Cloud Foundry configuration (
xsuaa-cross-consumption: true)
- Returns:
- the XSUAA token, or
nullif:- No extension is registered
- Token exchange fails (network error, invalid configuration, etc.)
- No source token is available for exchange
- See Also:
-
getClientCertificate
Returns the X.509 client certificate for the current thread.This is used in mutual TLS (mTLS) scenarios where the client authenticates via certificate instead of (or in addition to) JWT tokens. The certificate is typically extracted from the HTTP request by the web server/servlet container.
Usage: Certificate-Based Authentication
Certificate cert = SecurityContext.getClientCertificate(); if (cert != null) { String subjectDN = cert.getSubjectDN(); // Perform certificate-based authorization }- Returns:
- the client certificate, or
nullif:- No certificate was set via
setClientCertificate(Certificate) - The current request does not use mTLS
- No certificate was set via
- See Also:
-
setClientCertificate
Sets the X.509 client certificate for the current thread's security context.This is typically called by authentication filters/interceptors that extract the certificate from the HTTP request. In servlet-based applications, the certificate is usually available via the
javax.servlet.request.X509Certificaterequest attribute.Example: Servlet Filter
X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate"); if (certs != null && certs.length > 0) { Certificate cert = Certificate.create(certs[0]); SecurityContext.setClientCertificate(cert); }- Parameters:
certificate- the client certificate to store (may benullto clear it)- See Also:
-
getServicePlans
Returns the Identity Authentication Service plans associated with the current request.Service plans indicate which IAS features/capabilities are available for the current user/tenant. This information is typically extracted from custom HTTP headers (e.g.,
X-Identity-Service-Plan) in multi-tenant scenarios.Example Plans:
"default"— Basic IAS features"application"— Full IAS application features"sso"— Single Sign-On enabled
- Returns:
- the list of service plans, or
nullif:- No plans were set via
setServicePlans(String) - The current request does not include service plan information
- No plans were set via
- See Also:
-
setServicePlans
Parses and stores Identity Authentication Service plans from a comma-separated header value.Expected Format:
"plan1", "plan2", "plan3"
Plans are extracted by splitting on commas and removing surrounding quotes.Example:
// HTTP header: X-Identity-Service-Plan: "default", "application", "sso" String header = request.getHeader("X-Identity-Service-Plan"); SecurityContext.setServicePlans(header); List<String> plans = SecurityContext.getServicePlans(); // → ["default", "application", "sso"]- Parameters:
servicePlansHeader- the comma-separated service plans header value (must not benull)- Throws:
NullPointerException- ifservicePlansHeaderisnull- See Also:
-
clearServicePlans
public static void clearServicePlans()Removes the service plans from the security context.Subsequent calls to
getServicePlans()will returnnulluntil new plans are set. -
registerIdTokenExtension
Registers a globalIdTokenExtensionfor automatic ID token resolution.The extension is called by
getIdToken(). Only one extension can be registered at a time; calling this method multiple times overwrites the previous extension.Registration Timing: Typically called once during application startup (e.g., in Spring Boot's
@PostConstructorApplicationRunner). However, per-request registration is also supported for scenarios where dependency injection causes circular bean initialization issues. Extensions are stateless, so overwriting the previous extension is safe.Example: Spring Boot Configuration
@Configuration public class SecurityConfig { @PostConstruct public void registerExtensions() { SecurityContext.registerIdTokenExtension( new DefaultIdTokenExtension(tokenService, iasConfig) ); } }- Parameters:
ext- the ID token extension to register (may benullto unregister)- See Also:
-
registerXsuaaTokenExtension
Registers a globalXsuaaTokenExtensionfor automatic XSUAA token resolution/exchange.The extension is called by
getXsuaaToken(). Only one extension can be registered at a time; calling this method multiple times overwrites the previous extension.Registration Timing: Typically called once during application startup (e.g., in Spring Boot's
@PostConstructorApplicationRunner). However, per-request registration is also supported for scenarios where dependency injection causes circular bean initialization issues. Extensions are stateless, so overwriting the previous extension is safe.Example: Spring Boot Configuration
@Configuration public class SecurityConfig { @PostConstruct public void registerExtensions() { SecurityContext.registerXsuaaTokenExtension( new DefaultXsuaaTokenExtension(tokenService, xsuaaConfig) ); } }- Parameters:
ext- the XSUAA token extension to register (may benullto unregister)- See Also:
-
clear
public static void clear()Clears all security context data for the current thread.This method removes:
- Access token (
token) - Initial token (
initialToken) - Cached ID token (
idToken) - Cached XSUAA token (
xsuaaToken) - Client certificate (
certificate) - Service plans (
servicePlans)
What Is NOT Cleared:
- Registered extensions (
idTokenExtension,xsuaaTokenExtension) — These are global and shared across all threads - The
SecurityContextinstance itself — Kept inThreadLocalfor potential reuse
Usage: Request Cleanup
try { SecurityContext.setToken(token); // ... process request ... } finally { SecurityContext.clear(); // Clean up thread-local state }Difference from
clearContext():clear()— Nulls out all fields but keeps theSecurityContextinstanceclearContext()— Removes the entireSecurityContextfromThreadLocal
- See Also:
- Access token (
-
clearContext
public static void clearContext()Removes the entireSecurityContextinstance from thread-local storage.This performs complete
ThreadLocalcleanup by removing theSecurityContextinstance. Use this to prevent memory leaks in thread pool environments where threads are reused (e.g., servlet containers, application servers).Difference from
clear():clear()— Nulls out all fields but keeps theSecurityContextinstance inThreadLocal(faster for subsequent requests on the same thread)clearContext()— Removes the entire instance fromThreadLocal(complete cleanup, prevents memory leaks)
When to Use Each:
Cleanup Method Comparison Scenario Recommended Method Servlet request cleanup clear()Thread pool shutdown clearContext()Test cleanup (after each test) clearContext()Custom thread lifecycle management clearContext()Best Practice: Request Handling
try { SecurityContext.setToken(token); // ... process request ... } finally { SecurityContext.clearContext(); // Complete cleanup }- See Also:
-