CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-apereo-cas--cas-server-core-webflow-mfa-api

Core API for multifactor authentication webflow configuration in Apereo CAS providing interfaces and base classes for MFA provider integration

Pending
Overview
Eval results
Files

webflow-utilities.mddocs/

Webflow Utilities

Static utility methods for managing MFA-related data in webflow scopes, device registration, provider selection, and token management. The MultifactorAuthenticationWebflowUtils class provides a comprehensive set of utilities for storing and retrieving MFA-related information across webflow transitions.

Capabilities

MultifactorAuthenticationWebflowUtils

Utility class providing static methods for managing MFA-related data in webflow scopes.

/**
 * Utility class for managing MFA-related data in webflow scopes
 */
@UtilityClass
public class MultifactorAuthenticationWebflowUtils {
    
    // Webflow Customizer Management
    
    /**
     * Get multifactor authentication webflow customizers from application context
     * @param applicationContext Spring application context
     * @return List of sorted webflow customizers
     */
    public static List<CasMultifactorWebflowCustomizer> getMultifactorAuthenticationWebflowCustomizers(
        ConfigurableApplicationContext applicationContext);
    
    // Device Registration Management
    
    /**
     * Check if multifactor device registration is enabled
     * @param requestContext The webflow request context
     * @return true if device registration is enabled
     */
    public static boolean isMultifactorDeviceRegistrationEnabled(RequestContext requestContext);
    
    /**
     * Set multifactor device registration enabled flag
     * @param requestContext The webflow request context
     * @param enabled Whether device registration is enabled
     */
    public static void putMultifactorDeviceRegistrationEnabled(RequestContext requestContext, boolean enabled);
    
    // Provider Management
    
    /**
     * Store resolved multifactor authentication providers in conversation scope
     * @param context The webflow request context
     * @param value Collection of resolved MFA providers
     */
    public static void putResolvedMultifactorAuthenticationProviders(
        RequestContext context,
        Collection<MultifactorAuthenticationProvider> value);
    
    /**
     * Get resolved multifactor authentication providers from conversation scope
     * @param context The webflow request context
     * @return Collection of resolved provider IDs
     */
    public static Collection<String> getResolvedMultifactorAuthenticationProviders(RequestContext context);
    
    /**
     * Store MFA provider ID in flow scope
     * @param context The webflow request context
     * @param provider The MFA provider
     */
    public static void putMultifactorAuthenticationProvider(RequestContext context, MultifactorAuthenticationProvider provider);
    
    /**
     * Get MFA provider ID from flow scope
     * @param context The webflow request context
     * @return Provider ID string
     */
    public static String getMultifactorAuthenticationProvider(RequestContext context);
    
    // Provider Selection Management
    
    /**
     * Store selectable multifactor authentication providers in view scope
     * @param requestContext The webflow request context
     * @param mfaProviders List of selectable provider IDs
     */
    public static void putSelectableMultifactorAuthenticationProviders(RequestContext requestContext, List<String> mfaProviders);
    
    /**
     * Get selectable multifactor authentication providers from view scope
     * @param requestContext The webflow request context
     * @return List of selectable provider IDs
     */
    public static List<String> getSelectableMultifactorAuthenticationProviders(RequestContext requestContext);
    
    // Google Authenticator Specific
    
    /**
     * Set Google Authenticator multiple device registration enabled flag
     * @param requestContext The webflow request context
     * @param enabled Whether multiple device registration is enabled
     */
    public static void putGoogleAuthenticatorMultipleDeviceRegistrationEnabled(RequestContext requestContext, boolean enabled);
    
    /**
     * Check if Google Authenticator multiple device registration is enabled
     * @param requestContext The webflow request context
     * @return Boolean indicating if multiple device registration is enabled
     */
    public static Boolean isGoogleAuthenticatorMultipleDeviceRegistrationEnabled(RequestContext requestContext);
    
    // YubiKey Specific
    
    /**
     * Set YubiKey multiple device registration enabled flag
     * @param requestContext The webflow request context
     * @param enabled Whether multiple device registration is enabled
     */
    public static void putYubiKeyMultipleDeviceRegistrationEnabled(RequestContext requestContext, boolean enabled);
    
    // Simple MFA Token Management
    
    /**
     * Store simple multifactor authentication token in flow scope
     * @param requestContext The webflow request context
     * @param token The ticket token
     */
    public static void putSimpleMultifactorAuthenticationToken(RequestContext requestContext, Ticket token);
    
    /**
     * Remove simple multifactor authentication token from flow scope
     * @param requestContext The webflow request context
     */
    public static void removeSimpleMultifactorAuthenticationToken(RequestContext requestContext);
    
    /**
     * Get simple multifactor authentication token from flow scope
     * @param <T> Type of ticket extending Ticket
     * @param requestContext The webflow request context
     * @param clazz Class type of the ticket
     * @return Typed ticket instance or null
     */
    public static <T extends Ticket> T getSimpleMultifactorAuthenticationToken(RequestContext requestContext, Class<T> clazz);
    
    // Parent Credential Management
    
    /**
     * Get multifactor authentication parent credential from flow scope
     * @param requestContext The webflow request context
     * @return Parent credential or null
     */
    public static Credential getMultifactorAuthenticationParentCredential(RequestContext requestContext);
    
    // Registered Device Management
    
    /**
     * Store multifactor authentication registered devices in flow scope
     * @param requestContext The webflow request context
     * @param accounts Set of registered devices/accounts
     */
    public static void putMultifactorAuthenticationRegisteredDevices(RequestContext requestContext, Set accounts);
    
    /**
     * Get multifactor authentication registered devices from flow scope
     * Note: This method is NOT static in the source, which appears to be an inconsistency
     * @param requestContext The webflow request context
     * @return Set of registered devices
     */
    public Set<MultifactorAuthenticationRegisteredDevice> getMultifactorAuthenticationRegisteredDevices(RequestContext requestContext);
    
    // One-Time Token Account Management
    
    /**
     * Store one-time token account in flow scope
     * @param requestContext The webflow request context
     * @param account The one-time token account
     */
    public static void putOneTimeTokenAccount(RequestContext requestContext, OneTimeTokenAccount account);
    
    /**
     * Store collection of one-time token accounts in flow scope
     * @param requestContext The webflow request context
     * @param accounts Collection of one-time token accounts
     */
    public static void putOneTimeTokenAccounts(RequestContext requestContext, Collection accounts);
    
    /**
     * Get one-time token accounts from flow scope
     * @param requestContext The webflow request context
     * @return Collection of one-time token accounts
     */
    public static Collection getOneTimeTokenAccounts(RequestContext requestContext);
    
    /**
     * Get typed one-time token account from flow scope
     * @param <T> Type extending OneTimeTokenAccount
     * @param requestContext The webflow request context
     * @param clazz Class type of the account
     * @return Typed account instance or null
     */
    public static <T extends OneTimeTokenAccount> T getOneTimeTokenAccount(RequestContext requestContext, Class<T> clazz);
}

Usage Examples

Basic Provider Management

// In a webflow action
public class MyMfaAction extends BaseCasWebflowAction {
    
    @Override
    protected Event doExecuteInternal(RequestContext requestContext) throws Exception {
        // Get current MFA provider
        val providerId = MultifactorAuthenticationWebflowUtils
            .getMultifactorAuthenticationProvider(requestContext);
        
        if (providerId == null) {
            // No provider set, determine and store one
            val selectedProvider = determineAppropriateProvider(requestContext);
            MultifactorAuthenticationWebflowUtils
                .putMultifactorAuthenticationProvider(requestContext, selectedProvider);
        }
        
        return success();
    }
}

Device Registration Management

// Enable/disable device registration based on policy
public class DeviceRegistrationPolicyAction extends BaseCasWebflowAction {
    
    @Override
    protected Event doExecuteInternal(RequestContext requestContext) throws Exception {
        val authentication = WebUtils.getAuthentication(requestContext);
        val service = WebUtils.getService(requestContext);
        
        // Check policy for device registration
        val deviceRegistrationEnabled = shouldAllowDeviceRegistration(authentication, service);
        
        // Store the decision in webflow scope
        MultifactorAuthenticationWebflowUtils
            .putMultifactorDeviceRegistrationEnabled(requestContext, deviceRegistrationEnabled);
        
        // Provider-specific settings
        if (isGoogleAuthenticatorProvider()) {
            MultifactorAuthenticationWebflowUtils
                .putGoogleAuthenticatorMultipleDeviceRegistrationEnabled(requestContext, 
                    shouldAllowMultipleDevices(authentication));
        }
        
        return success();
    }
}

Provider Selection Workflow

// Prepare providers for user selection
public class PrepareProviderSelectionAction extends BaseCasWebflowAction {
    
    @Override
    protected Event doExecuteInternal(RequestContext requestContext) throws Exception {
        val authentication = WebUtils.getAuthentication(requestContext);
        val availableProviders = getAvailableProvidersForUser(authentication);
        
        // Filter providers based on availability and bypass rules
        val selectableProviders = availableProviders.stream()
            .filter(provider -> isProviderAvailable(provider, authentication))
            .filter(provider -> !shouldBypassProvider(provider, authentication))
            .map(MultifactorAuthenticationProvider::getId)
            .collect(Collectors.toList());
        
        // Store selectable providers for the view
        MultifactorAuthenticationWebflowUtils
            .putSelectableMultifactorAuthenticationProviders(requestContext, selectableProviders);
        
        if (selectableProviders.isEmpty()) {
            return error();
        } else if (selectableProviders.size() == 1) {
            // Only one provider available, set it directly
            val singleProvider = getProviderById(selectableProviders.get(0));
            MultifactorAuthenticationWebflowUtils
                .putMultifactorAuthenticationProvider(requestContext, singleProvider);
            return new EventFactorySupport().event(this, "single");
        } else {
            // Multiple providers available, proceed to selection
            return success();
        }
    }
}

Token and Credential Management

// Handle simple MFA token lifecycle
public class SimpleMfaTokenAction extends BaseCasWebflowAction {
    
    private final TicketRegistry ticketRegistry;
    
    @Override
    protected Event doExecuteInternal(RequestContext requestContext) throws Exception {
        val authentication = WebUtils.getAuthentication(requestContext);
        
        // Generate and store token
        val token = createSimpleMfaToken(authentication);
        ticketRegistry.addTicket(token);
        
        MultifactorAuthenticationWebflowUtils
            .putSimpleMultifactorAuthenticationToken(requestContext, token);
        
        // Send token to user (SMS, email, etc.)
        sendTokenToUser(token, authentication.getPrincipal());
        
        return success();
    }
    
    // In verification action
    protected Event verifyToken(RequestContext requestContext) throws Exception {
        val submittedToken = requestContext.getRequestParameters().get("token");
        val storedToken = MultifactorAuthenticationWebflowUtils
            .getSimpleMultifactorAuthenticationToken(requestContext, SimpleMfaToken.class);
        
        if (storedToken != null && storedToken.getId().equals(submittedToken)) {
            // Token verified, clean up
            MultifactorAuthenticationWebflowUtils
                .removeSimpleMultifactorAuthenticationToken(requestContext);
            ticketRegistry.deleteTicket(storedToken.getId());
            return success();
        }
        
        return error();
    }
}

Registered Device Management

// Load and manage registered devices
public class LoadRegisteredDevicesAction extends BaseCasWebflowAction {
    
    private final MultifactorAuthenticationDeviceManager deviceManager;
    
    @Override
    protected Event doExecuteInternal(RequestContext requestContext) throws Exception {
        val authentication = WebUtils.getAuthentication(requestContext);
        val principal = authentication.getPrincipal();
        
        // Load registered devices for the user
        val registeredDevices = deviceManager.findRegisteredDevices(principal);
        
        // Store in webflow scope for view access
        MultifactorAuthenticationWebflowUtils
            .putMultifactorAuthenticationRegisteredDevices(requestContext, registeredDevices);
        
        // Also store as one-time token accounts if applicable
        val tokenAccounts = registeredDevices.stream()
            .filter(device -> device instanceof OneTimeTokenAccount)
            .map(device -> (OneTimeTokenAccount) device)
            .collect(Collectors.toSet());
        
        if (!tokenAccounts.isEmpty()) {
            MultifactorAuthenticationWebflowUtils
                .putOneTimeTokenAccounts(requestContext, tokenAccounts);
        }
        
        return success();
    }
}

Custom Webflow Scope Integration

// Custom utility methods extending the base utilities
@UtilityClass
public class CustomMfaWebflowUtils {
    
    /**
     * Store custom MFA context data
     */
    public static void putCustomMfaContext(RequestContext context, String key, Object value) {
        context.getFlowScope().put("customMfa_" + key, value);
    }
    
    /**
     * Get custom MFA context data
     */
    public static <T> T getCustomMfaContext(RequestContext context, String key, Class<T> type) {
        return context.getFlowScope().get("customMfa_" + key, type);
    }
    
    /**
     * Check if user has completed MFA recently (using conversation scope for session-wide storage)
     */
    public static boolean hasRecentMfaCompletion(RequestContext context, String providerId) {
        val completions = context.getConversationScope()
            .get("recentMfaCompletions", Map.class);
        if (completions != null) {
            val lastCompletion = (Long) completions.get(providerId);
            return lastCompletion != null && 
                   (System.currentTimeMillis() - lastCompletion) < Duration.ofMinutes(5).toMillis();
        }
        return false;
    }
    
    /**
     * Record MFA completion timestamp
     */
    public static void recordMfaCompletion(RequestContext context, String providerId) {
        val completions = context.getConversationScope()
            .get("recentMfaCompletions", Map.class);
        val completionMap = completions != null ? completions : new HashMap<String, Long>();
        completionMap.put(providerId, System.currentTimeMillis());
        context.getConversationScope().put("recentMfaCompletions", completionMap);
    }
}

Scope Usage Guidelines

  • Flow Scope: Use for data that needs to persist across the entire webflow execution (e.g., selected provider, tokens)
  • Conversation Scope: Use for data that should persist across multiple webflow executions in the same session (e.g., resolved providers)
  • View Scope: Use for data that's only needed for rendering the current view (e.g., selectable providers list)

The utility class automatically manages the appropriate scopes for different types of MFA data, ensuring optimal performance and proper cleanup.

Install with Tessl CLI

npx tessl i tessl/maven-org-apereo-cas--cas-server-core-webflow-mfa-api

docs

authentication-components.md

event-resolution.md

index.md

provider-selection.md

webflow-actions.md

webflow-configuration.md

webflow-utilities.md

tile.json