or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

aot-native-support.mdauthentication-core.mdauthentication-events.mdauthentication-management.mdauthentication-tokens.mdauthorities.mdauthorization.mdcompromised-password.mdconcurrent-async.mddao-authentication.mdexpression-access-control.mdindex.mdjaas-authentication.mdjackson-serialization.mdmethod-security.mdobservation-metrics.mdone-time-tokens.mdprovisioning.mdsecurity-context.mdsession-management.mduser-details.md
tile.json

authentication-management.mddocs/

Authentication Management

Authentication management services that process authentication requests through chains of authentication providers. Includes AuthenticationManager, ProviderManager, reactive alternatives, and authentication provider implementations.

Key Information for Agents

Core Capabilities:

  • AuthenticationManager processes authentication requests and returns fully authenticated objects
  • ProviderManager iterates through AuthenticationProvider chain until one succeeds
  • Supports parent authentication manager for fallback
  • Event publishing via AuthenticationEventPublisher (success/failure events)
  • Observation support via ObservationAuthenticationManager for metrics/tracing
  • Reactive support via ReactiveAuthenticationManager and DelegatingReactiveAuthenticationManager

Key Interfaces and Classes:

  • AuthenticationManager - Functional interface: authenticate(Authentication) returns authenticated token or throws exception
  • AuthenticationProvider - Processes specific authentication types (supports(Class), authenticate(Authentication))
  • ProviderManager - Iterates through provider list, supports parent manager fallback
  • ReactiveAuthenticationManager - Returns Mono<Authentication> for non-blocking authentication
  • DelegatingReactiveAuthenticationManager - Iterates through reactive managers
  • ReactiveAuthenticationManagerAdapter - Adapts synchronous manager to reactive API
  • AuthenticationManagerResolver<C> - Resolves manager from context (e.g., HTTP request)
  • ObservationAuthenticationManager - Wraps manager with Micrometer observation support

Default Behaviors:

  • ProviderManager tries providers in order until one authenticates or all fail
  • If no provider authenticates and parent manager exists, tries parent
  • Credentials erased after authentication by default (setEraseCredentialsAfterAuthentication(true))
  • Events published only for denied results by default (configurable via setShouldPublishResult())
  • Parent manager can only be set via constructor in Spring Security 7.0.0

Threading Model:

  • Synchronous managers execute in calling thread
  • Reactive managers return Mono<Authentication> for non-blocking execution
  • Thread-safe: Managers are stateless, providers should be thread-safe

Lifecycle:

  • ProviderManager implements InitializingBean (validates configuration)
  • Event publisher set via setAuthenticationEventPublisher() (optional)
  • Observation manager wraps delegate (decorator pattern)

Exceptions:

  • AuthenticationException - Base exception for authentication failures
  • BadCredentialsException - Invalid credentials
  • DisabledException - Account disabled
  • LockedException - Account locked
  • AccountExpiredException - Account expired
  • CredentialsExpiredException - Credentials expired
  • ProviderNotFoundException - No provider supports authentication type
  • AuthenticationServiceException - Service error during authentication

Edge Cases:

  • Null authentication: Providers may return null if they cannot authenticate (not an error)
  • Multiple providers: First successful provider wins, remaining providers not called
  • Parent manager: Only used if no provider authenticates (fallback mechanism)
  • Event publishing: Only denied events published by default (since 7.0, configurable)
  • Credential erasure: Happens after successful authentication (default: true)
  • Reactive errors: Return Mono.error(AuthenticationException) instead of throwing
  • Provider order: Providers checked in list order (order matters)
  • Abstain behavior: Provider returning null is not an error (next provider tried)
  • Concurrent authentication: Multiple threads can authenticate simultaneously (stateless design)
  • Provider chain short-circuit: First successful provider stops iteration
  • Parent manager fallback: Only invoked if all providers return null or throw exceptions
  • Event publisher null: No events published if publisher not set (silent failure)
  • Observation registry null: Observation manager throws IllegalArgumentException if registry is null

Core Interfaces

AuthenticationManager

Processes an authentication request and returns a fully authenticated object including credentials.

package org.springframework.security.authentication;

@FunctionalInterface
interface AuthenticationManager {
    Authentication authenticate(Authentication authentication)
        throws AuthenticationException;
}

Method Details:

  • authenticate(Authentication) - Attempts to authenticate the passed Authentication object. Returns a fully authenticated Authentication with isAuthenticated() == true on success. Throws AuthenticationException if authentication fails.

Return Behavior:

  • Returns fully populated Authentication (including granted authorities) if successful
  • Throws DisabledException if account is disabled
  • Throws LockedException if account is locked
  • Throws BadCredentialsException if credentials are invalid
  • Never returns null (throws exception instead)

Usage Pattern:

AuthenticationManager authManager = // ... configured manager

UsernamePasswordAuthenticationToken authRequest =
    UsernamePasswordAuthenticationToken.unauthenticated("user", "password");

try {
    Authentication result = authManager.authenticate(authRequest);
    // Result is fully authenticated: result.isAuthenticated() == true
    SecurityContextHolder.getContext().setAuthentication(result);
    log.info("Authentication successful for user: {}", result.getName());
} catch (BadCredentialsException e) {
    log.error("Invalid credentials: {}", e.getMessage());
} catch (DisabledException e) {
    log.error("Account disabled: {}", e.getMessage());
} catch (LockedException e) {
    log.error("Account locked: {}", e.getMessage());
} catch (AuthenticationException e) {
    log.error("Authentication failed: {}", e.getMessage());
}

Critical Implementation Notes:

  • Must never return null (always throw exception on failure)
  • Returned authentication must have isAuthenticated() == true
  • Returned authentication should include all granted authorities
  • Credentials may be cleared after authentication (depends on manager configuration)

AuthenticationProvider

Processes specific Authentication implementation types.

package org.springframework.security.authentication;

interface AuthenticationProvider {
    Authentication authenticate(Authentication authentication)
        throws AuthenticationException;
    boolean supports(Class<?> authentication);
}

Method Details:

  • authenticate(Authentication) - Performs authentication with the same contract as AuthenticationManager.authenticate(). May return null if this provider cannot authenticate (abstain behavior).
  • supports(Class) - Returns true if this provider supports the indicated Authentication class.

Return Behavior:

  • Returns fully authenticated Authentication if this provider can authenticate
  • Returns null if this provider cannot authenticate (abstain - not an error)
  • Throws AuthenticationException if authentication fails (e.g., bad credentials)

Usage Pattern:

public class CustomAuthenticationProvider implements AuthenticationProvider {

    private final CustomAuthenticationService authService;
    private final UserDetailsService userDetailsService;

    public CustomAuthenticationProvider(
            CustomAuthenticationService authService,
            UserDetailsService userDetailsService) {
        this.authService = authService;
        this.userDetailsService = userDetailsService;
    }

    @Override
    public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        // Check if this provider can handle this authentication type
        if (!supports(authentication.getClass())) {
            return null; // Abstain - let other providers try
        }

        String username = authentication.getName();
        String password = authentication.getCredentials().toString();

        // Custom authentication logic
        if (!authService.authenticate(username, password)) {
            throw new BadCredentialsException("Invalid credentials");
        }

        // Load user details
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);

        // Return fully authenticated token
        return UsernamePasswordAuthenticationToken.authenticated(
            userDetails, password, userDetails.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class
            .isAssignableFrom(authentication);
    }
}

Critical Implementation Notes:

  • supports() should be fast (no I/O operations)
  • Returning null from authenticate() is valid (abstain behavior)
  • Throwing exception indicates authentication failure (not abstain)
  • Must return fully authenticated token (with authorities) on success

ReactiveAuthenticationManager

Reactive authentication manager for non-blocking authentication.

package org.springframework.security.authentication;

interface ReactiveAuthenticationManager {
    Mono<Authentication> authenticate(Authentication authentication);
}

Method Details:

  • authenticate(Authentication) - Attempts to authenticate the passed Authentication object reactively. Returns Mono<Authentication> that emits authenticated token on success or error signal on failure.

Return Behavior:

  • Returns Mono<Authentication> that emits fully authenticated token on success
  • Returns Mono.error(AuthenticationException) on failure
  • Never emits null (always emits value or error)

Usage Pattern:

ReactiveAuthenticationManager authManager = // ... configured manager

UsernamePasswordAuthenticationToken authRequest =
    UsernamePasswordAuthenticationToken.unauthenticated("user", "password");

authManager.authenticate(authRequest)
    .doOnSuccess(auth -> {
        log.info("Authentication successful: {}", auth.getName());
        // Set in reactive security context
        ReactiveSecurityContextHolder.withAuthentication(auth)
            .subscribe();
    })
    .doOnError(BadCredentialsException.class, e -> {
        log.error("Invalid credentials: {}", e.getMessage());
    })
    .doOnError(DisabledException.class, e -> {
        log.error("Account disabled: {}", e.getMessage());
    })
    .doOnError(AuthenticationException.class, e -> {
        log.error("Authentication failed: {}", e.getMessage());
    })
    .subscribe();

Critical Implementation Notes:

  • Must return Mono (never null)
  • Errors must be wrapped in Mono.error()
  • Should not block the calling thread
  • Returned authentication must be fully authenticated

AuthenticationManagerResolver

Resolves an AuthenticationManager from a context object.

package org.springframework.security.authentication;

interface AuthenticationManagerResolver<C> {
    AuthenticationManager resolve(C context);
}

Method Details:

  • resolve(C) - Returns an AuthenticationManager based on the provided context. May return null if no manager can be resolved.

Usage Pattern:

// Resolve different authentication managers based on request path
AuthenticationManagerResolver<HttpServletRequest> resolver = request -> {
    String path = request.getRequestURI();
    
    if (path.startsWith("/api/admin")) {
        return adminAuthenticationManager;
    } else if (path.startsWith("/api/internal")) {
        return internalAuthenticationManager;
    } else if (path.startsWith("/api/public")) {
        return publicAuthenticationManager;
    }
    
    return defaultAuthenticationManager;
};

// Usage in filter
AuthenticationManager manager = resolver.resolve(request);
if (manager != null) {
    Authentication result = manager.authenticate(authRequest);
}

Critical Implementation Notes:

  • May return null if no manager can be resolved
  • Should be fast (no blocking I/O)
  • Context type is generic (can be any type)

ReactiveAuthenticationManagerResolver

Reactive version of AuthenticationManagerResolver.

package org.springframework.security.authentication;

interface ReactiveAuthenticationManagerResolver<C> {
    Mono<ReactiveAuthenticationManager> resolve(C context);
}

Method Details:

  • resolve(C) - Returns a Mono that emits an appropriate ReactiveAuthenticationManager for the context. May emit null if no manager can be resolved.

Usage Pattern:

ReactiveAuthenticationManagerResolver<ServerWebExchange> resolver = exchange -> {
    String path = exchange.getRequest().getPath().value();
    
    if (path.startsWith("/api/admin")) {
        return Mono.just(adminReactiveAuthManager);
    } else if (path.startsWith("/api/public")) {
        return Mono.just(publicReactiveAuthManager);
    }
    
    return Mono.just(defaultReactiveAuthManager);
};

// Usage in reactive filter
resolver.resolve(exchange)
    .flatMap(manager -> manager.authenticate(authRequest))
    .subscribe();

Provider Manager

ProviderManager

Iterates through a list of AuthenticationProvider instances to perform authentication.

package org.springframework.security.authentication;

class ProviderManager implements AuthenticationManager, MessageSourceAware,
        InitializingBean {

    ProviderManager(List<AuthenticationProvider> providers);
    ProviderManager(List<AuthenticationProvider> providers,
        AuthenticationManager parent);
    ProviderManager(AuthenticationProvider... providers);

    Authentication authenticate(Authentication authentication)
        throws AuthenticationException;

    List<AuthenticationProvider> getProviders();
    void setMessageSource(MessageSource messageSource);
    void setAuthenticationEventPublisher(
        AuthenticationEventPublisher eventPublisher);
    void setEraseCredentialsAfterAuthentication(boolean eraseCredentials);
    void setParent(AuthenticationManager parent);
    AuthenticationManager getParent();
    boolean isEraseCredentialsAfterAuthentication();
}

Constructor Details:

  • ProviderManager(List<AuthenticationProvider>) - Creates a manager with the specified providers.
  • ProviderManager(List<AuthenticationProvider>, AuthenticationManager) - Creates a manager with providers and a parent manager to try if no provider authenticates.
  • ProviderManager(AuthenticationProvider...) - Varargs constructor for convenience.

Method Details:

  • authenticate(Authentication) - Iterates through providers until one successfully authenticates or all fail. If no provider authenticates and parent manager exists, tries parent. Throws ProviderNotFoundException if no provider supports the authentication type and no parent exists.
  • getProviders() - Returns the list of configured providers (unmodifiable).
  • setAuthenticationEventPublisher(AuthenticationEventPublisher) - Sets the event publisher for authentication events (optional).
  • setEraseCredentialsAfterAuthentication(boolean) - Controls whether credentials are erased after successful authentication (default: true).
  • setParent(AuthenticationManager) - Sets a parent authentication manager to try if no providers can authenticate (Spring Security 7.0.0: can only be set via constructor).
  • getParent() - Returns the parent manager (may be null).

Authentication Flow:

  1. Iterate through providers in order
  2. For each provider:
    • Check if provider.supports(authentication.getClass())
    • If supported, call provider.authenticate(authentication)
    • If returns non-null authentication, use it (short-circuit)
    • If throws exception, propagate exception
    • If returns null, continue to next provider
  3. If no provider authenticates:
    • If parent manager exists, try parent
    • If no parent, throw ProviderNotFoundException

Usage Pattern:

// Configure provider manager
List<AuthenticationProvider> providers = Arrays.asList(
    new DaoAuthenticationProvider(), // Try first
    new RememberMeAuthenticationProvider("key"), // Try second
    new AnonymousAuthenticationProvider("key") // Try third
);

ProviderManager providerManager = new ProviderManager(providers);
providerManager.setEraseCredentialsAfterAuthentication(true);
providerManager.setAuthenticationEventPublisher(
    new DefaultAuthenticationEventPublisher(applicationEventPublisher));

// Use the provider manager
try {
    Authentication result = providerManager.authenticate(authRequest);
    // Success - result is fully authenticated
} catch (ProviderNotFoundException e) {
    // No provider could handle this authentication type
} catch (AuthenticationException e) {
    // Authentication failed
}

Complete Configuration Example:

@Configuration
public class AuthenticationManagerConfiguration {

    @Bean
    public AuthenticationManager authenticationManager(
            UserDetailsService userDetailsService,
            PasswordEncoder passwordEncoder,
            ApplicationEventPublisher eventPublisher) {
        
        // Create DAO authentication provider
        DaoAuthenticationProvider daoProvider = new DaoAuthenticationProvider();
        daoProvider.setUserDetailsService(userDetailsService);
        daoProvider.setPasswordEncoder(passwordEncoder);
        
        // Create remember-me provider
        RememberMeAuthenticationProvider rememberMeProvider =
            new RememberMeAuthenticationProvider("remember-me-secret-key");
        
        // Create anonymous provider
        AnonymousAuthenticationProvider anonymousProvider =
            new AnonymousAuthenticationProvider("anonymous-key");
        
        // Build provider list (order matters - first matching provider wins)
        List<AuthenticationProvider> providers = Arrays.asList(
            daoProvider,           // Primary authentication
            rememberMeProvider,    // Remember-me authentication
            anonymousProvider     // Anonymous authentication
        );
        
        // Create provider manager with parent (optional)
        AuthenticationManager parentManager = createParentManager();
        ProviderManager providerManager = new ProviderManager(providers, parentManager);
        
        // Configure credential erasure
        providerManager.setEraseCredentialsAfterAuthentication(true);
        
        // Configure event publishing
        DefaultAuthenticationEventPublisher eventPublisherImpl =
            new DefaultAuthenticationEventPublisher(eventPublisher);
        providerManager.setAuthenticationEventPublisher(eventPublisherImpl);
        
        return providerManager;
    }
    
    private AuthenticationManager createParentManager() {
        // Optional parent manager for fallback
        return new ProviderManager(/* fallback providers */);
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}

Critical Implementation Notes:

  • Provider order matters (first matching provider wins)
  • Parent manager only used if all providers abstain (return null)
  • Events published only for denied results by default (since 7.0)
  • Credentials erased after successful authentication by default
  • Throws ProviderNotFoundException if no provider supports authentication type

DelegatingReactiveAuthenticationManager

Iterates through ReactiveAuthenticationManager instances reactively.

package org.springframework.security.authentication;

class DelegatingReactiveAuthenticationManager
        implements ReactiveAuthenticationManager {

    DelegatingReactiveAuthenticationManager(
        List<ReactiveAuthenticationManager> delegates);
    DelegatingReactiveAuthenticationManager(
        ReactiveAuthenticationManager... delegates);

    Mono<Authentication> authenticate(Authentication authentication);
}

Constructor Details:

  • DelegatingReactiveAuthenticationManager(List) - Creates a manager with the specified delegates.
  • DelegatingReactiveAuthenticationManager(ReactiveAuthenticationManager...) - Varargs constructor.

Authentication Flow:

  1. Iterate through delegates in order
  2. For each delegate:
    • Call delegate.authenticate(authentication)
    • If emits authentication, use it (short-circuit)
    • If emits error, propagate error
    • If emits empty, continue to next delegate
  3. If no delegate authenticates, return Mono.empty()

Usage Pattern:

List<ReactiveAuthenticationManager> managers = Arrays.asList(
    new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService),
    new ReactiveAuthenticationManagerAdapter(rememberMeAuthenticationManager)
);

DelegatingReactiveAuthenticationManager authManager =
    new DelegatingReactiveAuthenticationManager(managers);

authManager.authenticate(authRequest)
    .switchIfEmpty(Mono.error(new ProviderNotFoundException("No manager could authenticate")))
    .subscribe(auth -> log.info("Authenticated: {}", auth.getName()));

Critical Implementation Notes:

  • Delegate order matters (first successful delegate wins)
  • Returns Mono.empty() if no delegate authenticates (not an error)
  • Errors are propagated immediately (no fallback)

ReactiveAuthenticationManagerAdapter

Adapts a synchronous AuthenticationManager to reactive API.

package org.springframework.security.authentication;

class ReactiveAuthenticationManagerAdapter
        implements ReactiveAuthenticationManager {

    ReactiveAuthenticationManagerAdapter(AuthenticationManager authenticationManager);

    Mono<Authentication> authenticate(Authentication authentication);
}

Usage Pattern:

AuthenticationManager syncManager = new ProviderManager(providers);
ReactiveAuthenticationManager reactiveManager =
    new ReactiveAuthenticationManagerAdapter(syncManager);

reactiveManager.authenticate(authRequest)
    .subscribe(auth -> log.info("Authenticated: {}", auth.getName()));

Critical Implementation Notes:

  • Wraps synchronous manager in Mono.fromCallable()
  • Executes on calling thread (not truly reactive)
  • Use only when synchronous manager is required

Observation Support

ObservationAuthenticationManager

Wraps an AuthenticationManager to add Micrometer observation/metrics support.

package org.springframework.security.authentication;

class ObservationAuthenticationManager implements AuthenticationManager {
    ObservationAuthenticationManager(ObservationRegistry observationRegistry,
        AuthenticationManager delegate);

    Authentication authenticate(Authentication authentication)
        throws AuthenticationException;
}

Usage Pattern:

ObservationRegistry observationRegistry = ObservationRegistry.create();
AuthenticationManager delegate = new ProviderManager(providers);

ObservationAuthenticationManager authManager =
    new ObservationAuthenticationManager(observationRegistry, delegate);

// All authentication attempts will be observed
try {
    Authentication result = authManager.authenticate(authRequest);
    // Observation includes success metrics
} catch (AuthenticationException e) {
    // Observation includes failure metrics
}

Critical Implementation Notes:

  • Observation registry must not be null
  • Wraps delegate (decorator pattern)
  • Observations include request, result, and error information

ObservationReactiveAuthenticationManager

Reactive version with observation support.

package org.springframework.security.authentication;

class ObservationReactiveAuthenticationManager
        implements ReactiveAuthenticationManager {

    ObservationReactiveAuthenticationManager(ObservationRegistry observationRegistry,
        ReactiveAuthenticationManager delegate);

    Mono<Authentication> authenticate(Authentication authentication);
}

AuthenticationObservationContext

Context object for authentication observations containing request, result, and error information.

package org.springframework.security.authentication;

class AuthenticationObservationContext extends Observation.Context {
    AuthenticationObservationContext();

    Authentication getAuthenticationRequest();
    void setAuthenticationRequest(Authentication authenticationRequest);
    Authentication getAuthenticationResult();
    void setAuthenticationResult(Authentication authenticationResult);
    Throwable getError();
    void setError(Throwable error);
}

AuthenticationObservationConvention

Naming convention for authentication observations.

package org.springframework.security.authentication;

interface AuthenticationObservationConvention
        extends ObservationConvention<AuthenticationObservationContext> {

    @Override
    default boolean supportsContext(Observation.Context context);
}

User Details Authentication

AbstractUserDetailsReactiveAuthenticationManager

Abstract base class for reactive authentication using user details.

package org.springframework.security.authentication;

abstract class AbstractUserDetailsReactiveAuthenticationManager
        implements ReactiveAuthenticationManager, InitializingBean {

    AbstractUserDetailsReactiveAuthenticationManager();

    Mono<Authentication> authenticate(Authentication authentication);
    void setUserDetailsChecker(UserDetailsChecker userDetailsChecker);
    void setPostAuthenticationChecks(UserDetailsChecker postAuthenticationChecks);

    protected abstract Mono<UserDetails> retrieveUser(String username);
}

Method Details:

  • authenticate(Authentication) - Performs authentication using user details retrieved reactively.
  • setUserDetailsChecker(UserDetailsChecker) - Sets pre-authentication checks (account status).
  • setPostAuthenticationChecks(UserDetailsChecker) - Sets post-authentication checks.
  • retrieveUser(String) - Abstract method to retrieve user details reactively.

Usage Pattern:

public class CustomReactiveAuthenticationManager
        extends AbstractUserDetailsReactiveAuthenticationManager {

    private final ReactiveUserDetailsService userDetailsService;

    public CustomReactiveAuthenticationManager(
            ReactiveUserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected Mono<UserDetails> retrieveUser(String username) {
        return userDetailsService.findByUsername(username)
            .switchIfEmpty(Mono.error(new UsernameNotFoundException(username)));
    }
}

UserDetailsRepositoryReactiveAuthenticationManager

Reactive authentication manager using ReactiveUserDetailsService.

package org.springframework.security.authentication;

class UserDetailsRepositoryReactiveAuthenticationManager
        extends AbstractUserDetailsReactiveAuthenticationManager {

    UserDetailsRepositoryReactiveAuthenticationManager(
        ReactiveUserDetailsService userDetailsService);

    void setPasswordEncoder(PasswordEncoder passwordEncoder);
    void setUserDetailsPasswordService(
        ReactiveUserDetailsPasswordService userDetailsPasswordService);
    void setCompromisedPasswordChecker(
        ReactiveCompromisedPasswordChecker compromisedPasswordChecker);
    void setHideUserNotFoundExceptions(boolean hideUserNotFoundExceptions);
}

Method Details:

  • Constructor takes a ReactiveUserDetailsService for loading users.
  • setPasswordEncoder(PasswordEncoder) - Sets the password encoder (default: delegates to password in database).
  • setUserDetailsPasswordService(ReactiveUserDetailsPasswordService) - Service for updating passwords after authentication.
  • setCompromisedPasswordChecker(ReactiveCompromisedPasswordChecker) - Checker for compromised passwords.
  • setHideUserNotFoundExceptions(boolean) - Controls whether to hide user not found exceptions (default: true).

Usage Pattern:

ReactiveUserDetailsService userDetailsService =
    new MapReactiveUserDetailsService(user1, user2);

UserDetailsRepositoryReactiveAuthenticationManager authManager =
    new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService);

authManager.setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());
authManager.setHideUserNotFoundExceptions(true);

authManager.authenticate(authRequest)
    .subscribe(auth -> log.info("User authenticated: {}", auth.getName()));

Authentication Event Publishing

AuthenticationEventPublisher

Publishes authentication success and failure events.

package org.springframework.security.authentication;

interface AuthenticationEventPublisher {
    void publishAuthenticationSuccess(Authentication authentication);
    void publishAuthenticationFailure(AuthenticationException exception,
        Authentication authentication);
}

Method Details:

  • publishAuthenticationSuccess(Authentication) - Publishes a success event.
  • publishAuthenticationFailure(AuthenticationException, Authentication) - Publishes a failure event.

Usage Pattern:

public class CustomAuthenticationEventPublisher implements AuthenticationEventPublisher {

    private final ApplicationEventPublisher eventPublisher;

    public CustomAuthenticationEventPublisher(
            ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    @Override
    public void publishAuthenticationSuccess(Authentication authentication) {
        eventPublisher.publishEvent(
            new AuthenticationSuccessEvent(authentication));
    }

    @Override
    public void publishAuthenticationFailure(
            AuthenticationException exception,
            Authentication authentication) {
        eventPublisher.publishEvent(
            new AbstractAuthenticationFailureEvent(authentication, exception) {});
    }
}

DefaultAuthenticationEventPublisher

Default implementation publishing Spring application events.

package org.springframework.security.authentication;

class DefaultAuthenticationEventPublisher
        implements AuthenticationEventPublisher, ApplicationEventPublisherAware {

    DefaultAuthenticationEventPublisher();
    DefaultAuthenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher);

    void publishAuthenticationSuccess(Authentication authentication);
    void publishAuthenticationFailure(AuthenticationException exception,
        Authentication authentication);
    void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher);
    void setDefaultAuthenticationFailureEvent(
        Class<? extends AbstractAuthenticationFailureEvent> defaultAuthenticationFailureEvent);
    void setAdditionalExceptionMappings(
        Map<Class<? extends AuthenticationException>,
            Class<? extends AbstractAuthenticationFailureEvent>> additionalExceptionMappings);
}

Method Details:

  • setDefaultAuthenticationFailureEvent(Class) - Sets the default failure event class for unmapped exceptions.
  • setAdditionalExceptionMappings(Map) - Maps specific exception types to event types.

Usage Pattern:

DefaultAuthenticationEventPublisher eventPublisher =
    new DefaultAuthenticationEventPublisher(applicationEventPublisher);

// Map custom exceptions to events
Map<Class<? extends AuthenticationException>,
    Class<? extends AbstractAuthenticationFailureEvent>> mappings = new HashMap<>();
mappings.put(CustomAuthException.class, CustomAuthFailureEvent.class);
mappings.put(BadCredentialsException.class, AuthenticationFailureBadCredentialsEvent.class);
eventPublisher.setAdditionalExceptionMappings(mappings);

// Use with provider manager
ProviderManager providerManager = new ProviderManager(providers);
providerManager.setAuthenticationEventPublisher(eventPublisher);

Critical Implementation Notes:

  • Default failure event used for unmapped exceptions
  • Exception mappings allow custom event types
  • Event publisher must be set on ProviderManager to publish events

Authentication Details

AuthenticationDetailsSource

Creates authentication details objects from a context (typically an HTTP request).

package org.springframework.security.authentication;

interface AuthenticationDetailsSource<C, T> {
    T buildDetails(C context);
}

Method Details:

  • buildDetails(C) - Creates a details object from the provided context. May return null.

Usage Pattern:

// Custom details source
public class CustomAuthenticationDetailsSource
        implements AuthenticationDetailsSource<HttpServletRequest, CustomDetails> {

    @Override
    public CustomDetails buildDetails(HttpServletRequest request) {
        return new CustomDetails(
            request.getRemoteAddr(),
            request.getHeader("User-Agent"),
            request.getSession() != null ? request.getSession().getId() : null,
            request.getHeader("X-Forwarded-For")
        );
    }
}

// Usage
AuthenticationDetailsSource<HttpServletRequest, CustomDetails> detailsSource =
    new CustomAuthenticationDetailsSource();

UsernamePasswordAuthenticationToken authRequest =
    new UsernamePasswordAuthenticationToken("user", "password");
authRequest.setDetails(detailsSource.buildDetails(request));

Account Status Checking

UserDetailsChecker

Checks user account status before or after authentication.

package org.springframework.security.core.userdetails;

interface UserDetailsChecker {
    void check(UserDetails toCheck);
}

Method Details:

  • check(UserDetails) - Examines user details and throws AccountStatusException if account is not valid.

Usage Pattern:

public class CustomUserDetailsChecker implements UserDetailsChecker {

    @Override
    public void check(UserDetails toCheck) {
        if (!toCheck.isEnabled()) {
            throw new DisabledException("Account is disabled");
        }
        if (!toCheck.isAccountNonExpired()) {
            throw new AccountExpiredException("Account has expired");
        }
        if (!toCheck.isAccountNonLocked()) {
            throw new LockedException("Account is locked");
        }
        if (!toCheck.isCredentialsNonExpired()) {
            throw new CredentialsExpiredException("Credentials have expired");
        }
    }
}

AccountStatusUserDetailsChecker

Default implementation checking standard account flags.

package org.springframework.security.authentication;

class AccountStatusUserDetailsChecker implements UserDetailsChecker {
    AccountStatusUserDetailsChecker();

    void check(UserDetails user);
}

Implementation:

  • Throws DisabledException if !user.isEnabled()
  • Throws AccountExpiredException if !user.isAccountNonExpired()
  • Throws LockedException if !user.isAccountNonLocked()
  • Throws CredentialsExpiredException if !user.isCredentialsNonExpired()

Usage Pattern:

UserDetailsChecker accountStatusChecker = new AccountStatusUserDetailsChecker();

try {
    accountStatusChecker.check(userDetails);
    // Account is valid
} catch (DisabledException e) {
    log.error("Account is disabled: {}", userDetails.getUsername());
} catch (LockedException e) {
    log.error("Account is locked: {}", userDetails.getUsername());
} catch (AccountExpiredException e) {
    log.error("Account expired: {}", userDetails.getUsername());
} catch (CredentialsExpiredException e) {
    log.error("Credentials expired: {}", userDetails.getUsername());
}

Caching User Details

CachingUserDetailsService

Caching decorator for UserDetailsService.

package org.springframework.security.authentication;

class CachingUserDetailsService implements UserDetailsService {
    CachingUserDetailsService(UserDetailsService delegate);

    UserDetails loadUserByUsername(String username)
        throws UsernameNotFoundException;
    UserCache getUserCache();
    void setUserCache(UserCache userCache);
}

Method Details:

  • Constructor takes the delegate UserDetailsService to cache.
  • setUserCache(UserCache) - Sets the cache implementation (default: NullUserCache).

Usage Pattern:

UserDetailsService delegate = new JdbcDaoImpl(dataSource);
UserCache cache = new SpringCacheBasedUserCache(cacheManager.getCache("users"));

CachingUserDetailsService cachingService = new CachingUserDetailsService(delegate);
cachingService.setUserCache(cache);

// First call loads from database
UserDetails user1 = cachingService.loadUserByUsername("john");

// Second call loads from cache
UserDetails user2 = cachingService.loadUserByUsername("john");

Critical Implementation Notes:

  • Cache key is username
  • Cache miss delegates to underlying service
  • Cache hit returns cached user details
  • Cache should be thread-safe