CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-quarkus--quarkus-oidc-client

Get and refresh access tokens from OpenID Connect providers

Pending
Overview
Eval results
Files

token-management.mddocs/

Token Management

Token container and management functionality providing access to tokens, expiration tracking, and automatic refresh capabilities. The Tokens class encapsulates access and refresh tokens along with metadata for intelligent token lifecycle management.

Capabilities

Tokens Class

Container class for access and refresh tokens returned from OIDC token grant requests. Provides comprehensive token metadata and expiration tracking capabilities.

/**
 * Container for access and refresh tokens with metadata
 */
public class Tokens {
    /**
     * Construct a Tokens instance with full token information
     * @param accessToken The access token string
     * @param accessTokenExpiresAt Timestamp when access token expires (epoch seconds)
     * @param refreshTokenTimeSkewDuration Time skew duration for refresh token operations
     * @param refreshToken The refresh token string (may be null)
     * @param refreshTokenExpiresAt Timestamp when refresh token expires (epoch seconds, may be null)
     * @param grantResponse The complete JSON response from the token grant
     * @param clientId The client ID used for the token request
     */
    public Tokens(String accessToken, Long accessTokenExpiresAt, Duration refreshTokenTimeSkewDuration, 
                  String refreshToken, Long refreshTokenExpiresAt, JsonObject grantResponse, String clientId);
    
    /**
     * Get the access token string
     * @return The access token
     */
    public String getAccessToken();
    
    /**
     * Get the refresh token string
     * @return The refresh token, or null if not available
     */
    public String getRefreshToken();
    
    /**
     * Get the client ID used for token acquisition
     * @return The client ID
     */
    public String getClientId();
    
    /**
     * Get a property from the original grant response
     * @param propertyName Name of the property to retrieve
     * @return The property value as a String, or null if not found
     */
    public String get(String propertyName);
    
    /**
     * Get the access token expiration timestamp
     * @return Epoch seconds when access token expires, or null if not specified
     */
    public Long getAccessTokenExpiresAt();
    
    /**
     * Get the refresh token time skew in seconds
     * @return Time skew in seconds, or null if not specified
     */
    public Long getRefreshTokenTimeSkew();
    
    /**
     * Check if the access token has expired
     * @return true if access token is expired, false otherwise
     */
    public boolean isAccessTokenExpired();
    
    /**
     * Check if the refresh token has expired
     * @return true if refresh token is expired, false otherwise
     */
    public boolean isRefreshTokenExpired();
    
    /**
     * Check if access token should be proactively refreshed
     * Considers the refresh interval and time skew to determine if token
     * should be refreshed before it actually expires
     * @return true if token should be refreshed, false otherwise
     */
    public boolean isAccessTokenWithinRefreshInterval();
}

Usage Examples:

import io.quarkus.oidc.client.Tokens;
import io.quarkus.oidc.client.OidcClient;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.time.Instant;

@ApplicationScoped
public class TokenManager {
    
    @Inject
    OidcClient oidcClient;
    
    public void manageTokens() {
        // Get initial tokens
        Uni<Tokens> tokensUni = oidcClient.getTokens();
        
        tokensUni.subscribe().with(tokens -> {
            // Access token information
            String accessToken = tokens.getAccessToken();
            Long expiresAt = tokens.getAccessTokenExpiresAt();
            
            System.out.println("Access token: " + accessToken);
            System.out.println("Expires at: " + 
                (expiresAt != null ? Instant.ofEpochSecond(expiresAt) : "Never"));
            
            // Check token status
            if (tokens.isAccessTokenExpired()) {
                System.out.println("Access token has expired");
            } else if (tokens.isAccessTokenWithinRefreshInterval()) {
                System.out.println("Access token should be refreshed soon");
                
                // Refresh token if available
                String refreshToken = tokens.getRefreshToken();
                if (refreshToken != null && !tokens.isRefreshTokenExpired()) {
                    Uni<Tokens> refreshedTokens = oidcClient.refreshTokens(refreshToken);
                    // Handle refreshed tokens...
                }
            }
            
            // Access additional properties from grant response
            String scope = tokens.get("scope");
            String tokenType = tokens.get("token_type");
            System.out.println("Scope: " + scope);
            System.out.println("Token type: " + tokenType);
        });
    }
}

Token Lifecycle Management

Comprehensive example showing token lifecycle management with automatic refresh and error handling.

import io.quarkus.oidc.client.Tokens;
import io.quarkus.oidc.client.OidcClient;
import io.quarkus.oidc.client.OidcClientException;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.time.Instant;
import java.util.concurrent.atomic.AtomicReference;

@ApplicationScoped
public class TokenLifecycleManager {
    
    @Inject
    OidcClient oidcClient;
    
    private final AtomicReference<Tokens> currentTokens = new AtomicReference<>();
    
    /**
     * Get a valid access token, refreshing if necessary
     */
    public Uni<String> getValidAccessToken() {
        Tokens tokens = currentTokens.get();
        
        if (tokens == null) {
            // No tokens yet, get initial tokens
            return obtainInitialTokens();
        } else if (tokens.isAccessTokenExpired()) {
            // Token expired, must refresh
            return refreshTokens(tokens);
        } else if (tokens.isAccessTokenWithinRefreshInterval()) {
            // Token should be refreshed proactively
            return refreshTokensProactively(tokens);
        } else {
            // Token is still valid
            return Uni.createFrom().item(tokens.getAccessToken());
        }
    }
    
    private Uni<String> obtainInitialTokens() {
        return oidcClient.getTokens()
            .onItem().invoke(tokens -> {
                currentTokens.set(tokens);
                logTokenInfo("Obtained initial tokens", tokens);
            })
            .map(Tokens::getAccessToken)
            .onFailure().invoke(throwable -> 
                System.err.println("Failed to obtain initial tokens: " + throwable.getMessage())
            );
    }
    
    private Uni<String> refreshTokens(Tokens expiredTokens) {
        String refreshToken = expiredTokens.getRefreshToken();
        
        if (refreshToken == null || expiredTokens.isRefreshTokenExpired()) {
            // No refresh token or refresh token expired, get new tokens
            return obtainInitialTokens();
        }
        
        return oidcClient.refreshTokens(refreshToken)
            .onItem().invoke(tokens -> {
                currentTokens.set(tokens);
                logTokenInfo("Refreshed expired tokens", tokens);
            })
            .map(Tokens::getAccessToken)
            .onFailure().recoverWithUni(throwable -> {
                System.err.println("Token refresh failed: " + throwable.getMessage());
                // Fall back to getting new tokens
                return obtainInitialTokens();
            });
    }
    
    private Uni<String> refreshTokensProactively(Tokens currentTokens) {
        String refreshToken = currentTokens.getRefreshToken();
        
        if (refreshToken != null && !currentTokens.isRefreshTokenExpired()) {
            return oidcClient.refreshTokens(refreshToken)
                .onItem().invoke(tokens -> {
                    this.currentTokens.set(tokens);
                    logTokenInfo("Proactively refreshed tokens", tokens);
                })
                .map(Tokens::getAccessToken)
                .onFailure().recoverWithItem(throwable -> {
                    System.err.println("Proactive refresh failed, using current token: " + 
                        throwable.getMessage());
                    // Fall back to current token if proactive refresh fails
                    return currentTokens.getAccessToken();
                });
        } else {
            // No refresh token available, use current access token
            return Uni.createFrom().item(currentTokens.getAccessToken());
        }
    }
    
    private void logTokenInfo(String action, Tokens tokens) {
        Long expiresAt = tokens.getAccessTokenExpiresAt();
        String expirationInfo = expiresAt != null ? 
            Instant.ofEpochSecond(expiresAt).toString() : "Never";
            
        System.out.println(String.format(
            "%s - Client: %s, Expires: %s, Has refresh: %s",
            action,
            tokens.getClientId(),
            expirationInfo,
            tokens.getRefreshToken() != null
        ));
    }
    
    /**
     * Revoke current tokens and clear cache
     */
    public Uni<Void> revokeAndClearTokens() {
        Tokens tokens = currentTokens.getAndSet(null);
        
        if (tokens != null) {
            return oidcClient.revokeAccessToken(tokens.getAccessToken())
                .onItem().invoke(revoked -> {
                    if (revoked) {
                        System.out.println("Tokens revoked and cleared");
                    } else {
                        System.out.println("Token revocation may need to be retried");
                    }
                })
                .onFailure().invoke(throwable -> 
                    System.err.println("Failed to revoke tokens: " + throwable.getMessage())
                )
                .replaceWithVoid();
        } else {
            return Uni.createFrom().voidItem();
        }
    }
}

Token Expiration Strategies

Different strategies for handling token expiration based on application requirements.

/**
 * Conservative strategy - refresh tokens well before expiration
 */
public boolean shouldRefreshConservative(Tokens tokens) {
    if (tokens.isAccessTokenExpired()) {
        return true; // Must refresh
    }
    
    Long expiresAt = tokens.getAccessTokenExpiresAt();
    if (expiresAt != null) {
        long now = Instant.now().getEpochSecond();
        long timeToExpiry = expiresAt - now;
        
        // Refresh if less than 5 minutes remaining
        return timeToExpiry < 300;
    }
    
    return false;
}

/**
 * Aggressive strategy - refresh tokens just before expiration
 */
public boolean shouldRefreshAggressive(Tokens tokens) {
    if (tokens.isAccessTokenExpired()) {
        return true; // Must refresh
    }
    
    Long expiresAt = tokens.getAccessTokenExpiresAt();
    if (expiresAt != null) {
        long now = Instant.now().getEpochSecond();
        long timeToExpiry = expiresAt - now;
        
        // Refresh if less than 30 seconds remaining
        return timeToExpiry < 30;
    }
    
    return false;
}

/**
 * Use built-in strategy with configured time skew
 */
public boolean shouldRefreshBuiltIn(Tokens tokens) {
    return tokens.isAccessTokenWithinRefreshInterval();
}

Install with Tessl CLI

npx tessl i tessl/maven-io-quarkus--quarkus-oidc-client

docs

client-interface.md

configuration.md

index.md

integration.md

token-management.md

tile.json