CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-apereo-cas--cas-server-core-authentication-api

Core authentication API module for Apereo CAS providing fundamental authentication interfaces, implementations, and components for the CAS server infrastructure

Pending
Overview
Eval results
Files

credential-handling.mddocs/

Credential Handling

Credentials represent the authentication information provided by users, such as usernames/passwords, certificates, tokens, or other authentication artifacts. The CAS authentication API provides a comprehensive framework for handling various credential types with validation, security, and extensibility features.

Core Credential Interface

package org.apereo.cas.authentication;

import java.io.Serializable;

public interface Credential extends Serializable {
    String getId();
    CredentialMetadata getCredentialMetadata();
}

Mutable Credential Interface

package org.apereo.cas.authentication;

public interface MutableCredential extends Credential {
    void setId(String id);
}

Credential Metadata

package org.apereo.cas.authentication;

import java.time.ZonedDateTime;
import java.util.Map;

public interface CredentialMetadata {
    String getId();
    Map<String, Object> getProperties();
    void addProperty(String name, Object value);
    ZonedDateTime getAuthenticationDate();
    void setAuthenticationDate(ZonedDateTime authenticationDate);
    Class<? extends Credential> getCredentialClass();
}

Abstract Credential Base Class

package org.apereo.cas.authentication.credential;

import org.apereo.cas.authentication.Credential;
import org.apereo.cas.authentication.CredentialMetadata;
import org.apereo.cas.authentication.metadata.BasicCredentialMetadata;
import org.springframework.binding.validation.ValidationContext;

public abstract class AbstractCredential implements Credential {
    private CredentialMetadata credentialMetadata;
    
    public CredentialMetadata getCredentialMetadata() {
        if (credentialMetadata == null) {
            this.credentialMetadata = new BasicCredentialMetadata(this);
        }
        return this.credentialMetadata;
    }
    
    public void setCredentialMetadata(CredentialMetadata credentialMetadata) {
        this.credentialMetadata = credentialMetadata;
    }
    
    public boolean isValid() {
        return StringUtils.isNotBlank(getId());
    }
    
    public void validate(ValidationContext context) {
        // Default validation - subclasses can override
        if (!isValid()) {
            context.getMessageContext().addMessage(
                new MessageBuilder()
                    .error()
                    .source(getClass().getSimpleName())
                    .defaultText("Invalid credential")
                    .code("credential.invalid")
                    .build());
        }
    }
}

Username/Password Credentials

Basic Username/Password Credential

The most common credential type supporting username/password authentication:

package org.apereo.cas.authentication.credential;

import org.apereo.cas.authentication.MutableCredential;
import org.springframework.binding.validation.ValidationContext;
import jakarta.validation.constraints.Size;
import java.util.Map;

public class UsernamePasswordCredential extends AbstractCredential implements MutableCredential {
    
    public static final String AUTHENTICATION_ATTRIBUTE_PASSWORD = "credential";
    
    @Size(min = 1, message = "username.required")
    private String username;
    
    @Size(min = 1, message = "password.required")
    private char[] password;
    
    private String source;
    private Map<String, Object> customFields = new LinkedHashMap<>();
    
    // Constructors
    public UsernamePasswordCredential() {}
    
    public UsernamePasswordCredential(String username, String password) {
        this.username = username;
        assignPassword(StringUtils.defaultString(password));
    }
    
    public UsernamePasswordCredential(String username, char[] password,
                                    String source, Map<String, Object> customFields) {
        this.username = username;
        this.password = password.clone();
        this.source = source;
        this.customFields = new HashMap<>(customFields);
    }
    
    // MutableCredential implementation
    public String getId() {
        return this.username;
    }
    
    public void setId(String id) {
        this.username = id;
    }
    
    // Password handling
    public char[] getPassword() {
        return password != null ? password.clone() : null;
    }
    
    public void setPassword(char[] password) {
        this.password = password != null ? password.clone() : null;
    }
    
    public String toPassword() {
        return password != null ? new String(password) : null;
    }
    
    public void assignPassword(String password) {
        if (password != null) {
            this.password = password.toCharArray();
        } else {
            this.password = null;
        }
    }
    
    // Properties
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    
    public String getSource() { return source; }
    public void setSource(String source) { this.source = source; }
    
    public Map<String, Object> getCustomFields() { return customFields; }
    public void setCustomFields(Map<String, Object> customFields) { 
        this.customFields = customFields; 
    }
    
    // Validation
    @Override
    public void validate(ValidationContext context) {
        super.validate(context);
        
        MessageContext messageContext = context.getMessageContext();
        if (!"submit".equalsIgnoreCase(context.getUserEvent()) || 
            messageContext.hasErrorMessages()) {
            return;
        }
        
        // Validate username
        if (StringUtils.isBlank(username)) {
            messageContext.addMessage(new MessageBuilder()
                .error()
                .source("username")
                .defaultText("Username is required")
                .code("username.required")
                .build());
        }
        
        // Validate password
        if (password == null || password.length == 0) {
            messageContext.addMessage(new MessageBuilder()
                .error()
                .source("password")
                .defaultText("Password is required")
                .code("password.required")
                .build());
        }
    }
    
    @Override
    public boolean equals(Object obj) {
        if (obj == null || !this.getClass().equals(obj.getClass())) {
            return false;
        }
        UsernamePasswordCredential other = (UsernamePasswordCredential) obj;
        return Objects.equals(this.username, other.username);
    }
    
    @Override
    public int hashCode() {
        return Objects.hashCode(this.username);
    }
    
    @Override
    public String toString() {
        return String.format("UsernamePasswordCredential[username=%s, source=%s]", 
                           username, source);
    }
}

Remember-Me Username/Password Credential

Extended credential that supports "Remember Me" functionality:

package org.apereo.cas.authentication.credential;

import org.apereo.cas.authentication.RememberMeCredential;

public class RememberMeUsernamePasswordCredential extends UsernamePasswordCredential 
    implements RememberMeCredential {
    
    public static final String AUTHENTICATION_ATTRIBUTE_REMEMBER_ME = "rememberMe";
    
    private boolean rememberMe;
    
    // Constructors
    public RememberMeUsernamePasswordCredential() {
        super();
    }
    
    public RememberMeUsernamePasswordCredential(String username, String password, 
                                              boolean rememberMe) {
        super(username, password);
        this.rememberMe = rememberMe;
    }
    
    // RememberMeCredential implementation
    public boolean isRememberMe() {
        return this.rememberMe;
    }
    
    public void setRememberMe(boolean rememberMe) {
        this.rememberMe = rememberMe;
    }
    
    @Override
    public String toString() {
        return String.format("RememberMeUsernamePasswordCredential[username=%s, rememberMe=%s]", 
                           getUsername(), rememberMe);
    }
}

Token-Based Credentials

One-Time Password Credential

For OTP-based authentication systems:

package org.apereo.cas.authentication.credential;

import jakarta.validation.constraints.Size;

public class OneTimePasswordCredential extends AbstractCredential {
    
    @Size(min = 1, message = "oneTimePassword.required")
    private String password;
    
    private String id;
    
    // Constructors
    public OneTimePasswordCredential() {}
    
    public OneTimePasswordCredential(String id, String password) {
        this.id = id;
        this.password = password;
    }
    
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }
    
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
    
    @Override
    public void validate(ValidationContext context) {
        super.validate(context);
        
        MessageContext messageContext = context.getMessageContext();
        
        if (StringUtils.isBlank(id)) {
            messageContext.addMessage(new MessageBuilder()
                .error()
                .source("id")
                .defaultText("ID is required")
                .code("id.required")
                .build());
        }
        
        if (StringUtils.isBlank(password)) {
            messageContext.addMessage(new MessageBuilder()
                .error()
                .source("password")
                .defaultText("One-time password is required")
                .code("oneTimePassword.required")
                .build());
        }
    }
}

One-Time Token Credential

For single-use token authentication:

package org.apereo.cas.authentication.credential;

import jakarta.validation.constraints.Size;

public class OneTimeTokenCredential extends AbstractCredential {
    
    @Size(min = 1, message = "token.required")
    private String token;
    
    private String id;
    
    // Constructors
    public OneTimeTokenCredential() {}
    
    public OneTimeTokenCredential(String id, String token) {
        this.id = id;
        this.token = token;
    }
    
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }
    
    public String getToken() { return token; }
    public void setToken(String token) { this.token = token; }
    
    @Override
    public void validate(ValidationContext context) {
        super.validate(context);
        
        MessageContext messageContext = context.getMessageContext();
        
        if (StringUtils.isBlank(token)) {
            messageContext.addMessage(new MessageBuilder()
                .error()
                .source("token")
                .defaultText("Token is required")
                .code("token.required")
                .build());
        }
    }
}

Service-Based Credentials

Basic Identifiable Credential

Simple credential with just an identifier:

package org.apereo.cas.authentication.credential;

public class BasicIdentifiableCredential extends AbstractCredential {
    
    private String id;
    
    // Constructors
    public BasicIdentifiableCredential() {}
    
    public BasicIdentifiableCredential(String id) {
        this.id = id;
    }
    
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }
    
    @Override
    public String toString() {
        return String.format("BasicIdentifiableCredential[id=%s]", id);
    }
}

HTTP-Based Service Credential

Credential for HTTP service callback authentication:

package org.apereo.cas.authentication.credential;

import org.apereo.cas.services.RegisteredService;
import java.net.URL;

public class HttpBasedServiceCredential extends AbstractCredential {
    
    private final URL callbackUrl;
    private final RegisteredService registeredService;
    
    public HttpBasedServiceCredential(URL callbackUrl, RegisteredService registeredService) {
        this.callbackUrl = callbackUrl;
        this.registeredService = registeredService;
    }
    
    public String getId() {
        return this.callbackUrl != null ? this.callbackUrl.toExternalForm() : null;
    }
    
    public URL getCallbackUrl() { return callbackUrl; }
    
    public RegisteredService getService() { return registeredService; }
    
    @Override
    public String toString() {
        return String.format("HttpBasedServiceCredential[callbackUrl=%s, service=%s]", 
                           callbackUrl, registeredService != null ? registeredService.getName() : "null");
    }
}

Credential Validation

Validation Context Usage

// Validate credential in Spring WebFlow context
ValidationContext context = new DefaultValidationContext(requestContext, "credentialValidation");
UsernamePasswordCredential credential = new UsernamePasswordCredential("user", "pass");

credential.validate(context);

if (context.getMessageContext().hasErrorMessages()) {
    // Handle validation errors
    for (Message message : context.getMessageContext().getAllMessages()) {
        System.out.println("Validation error: " + message.getText());
    }
}

Custom Validation

public class CustomUsernamePasswordCredential extends UsernamePasswordCredential {
    
    @Override
    public void validate(ValidationContext context) {
        super.validate(context);
        
        MessageContext messageContext = context.getMessageContext();
        
        // Custom username validation
        String username = getUsername();
        if (username != null && username.contains("@")) {
            if (!isValidEmail(username)) {
                messageContext.addMessage(new MessageBuilder()
                    .error()
                    .source("username")
                    .defaultText("Invalid email format")
                    .code("username.invalid.email")
                    .build());
            }
        }
        
        // Password complexity validation
        String password = toPassword();
        if (password != null && !isPasswordComplex(password)) {
            messageContext.addMessage(new MessageBuilder()
                .error()
                .source("password")
                .defaultText("Password does not meet complexity requirements")
                .code("password.complexity.insufficient")
                .build());
        }
    }
    
    private boolean isValidEmail(String email) {
        return email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
    }
    
    private boolean isPasswordComplex(String password) {
        return password.length() >= 8 &&
               password.matches(".*[A-Z].*") &&
               password.matches(".*[a-z].*") &&
               password.matches(".*[0-9].*") &&
               password.matches(".*[!@#$%^&*()].*");
    }
}

Credential Security

Password Security

public class SecureUsernamePasswordCredential extends UsernamePasswordCredential {
    
    @Override
    public void setPassword(char[] password) {
        super.setPassword(password);
        
        // Clear the input parameter for security
        if (password != null) {
            Arrays.fill(password, '\0');
        }
    }
    
    @Override
    public void assignPassword(String password) {
        super.assignPassword(password);
        
        // Note: String parameter cannot be cleared, 
        // prefer char[] methods for security
    }
    
    public void clearPassword() {
        char[] password = getPassword();
        if (password != null) {
            Arrays.fill(password, '\0');
        }
        setPassword(null);
    }
}

Credential Encryption

public class EncryptedUsernamePasswordCredential extends UsernamePasswordCredential {
    
    private final Cipher cipher;
    
    public EncryptedUsernamePasswordCredential(Cipher cipher) {
        this.cipher = cipher;
    }
    
    @Override
    public void setPassword(char[] password) {
        if (password != null && cipher != null) {
            try {
                byte[] encrypted = cipher.doFinal(new String(password).getBytes());
                String encodedPassword = Base64.getEncoder().encodeToString(encrypted);
                super.assignPassword(encodedPassword);
                
                // Clear original password
                Arrays.fill(password, '\0');
            } catch (Exception e) {
                throw new RuntimeException("Failed to encrypt password", e);
            }
        } else {
            super.setPassword(password);
        }
    }
    
    public char[] getDecryptedPassword() throws Exception {
        String encryptedPassword = toPassword();
        if (encryptedPassword != null && cipher != null) {
            byte[] encrypted = Base64.getDecoder().decode(encryptedPassword);
            byte[] decrypted = cipher.doFinal(encrypted);
            return new String(decrypted).toCharArray();
        }
        return getPassword();
    }
}

Credential Metadata Management

Basic Credential Metadata

package org.apereo.cas.authentication.metadata;

import org.apereo.cas.authentication.Credential;
import org.apereo.cas.authentication.CredentialMetadata;
import java.time.ZonedDateTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class BasicCredentialMetadata implements CredentialMetadata {
    
    private final String id;
    private final Class<? extends Credential> credentialClass;
    private final Map<String, Object> properties = new ConcurrentHashMap<>();
    private ZonedDateTime authenticationDate;
    
    public BasicCredentialMetadata(Credential credential) {
        this.id = credential.getId();
        this.credentialClass = credential.getClass();
        this.authenticationDate = ZonedDateTime.now();
    }
    
    public String getId() { return id; }
    
    public Class<? extends Credential> getCredentialClass() { return credentialClass; }
    
    public Map<String, Object> getProperties() { return properties; }
    
    public void addProperty(String name, Object value) {
        properties.put(name, value);
    }
    
    public ZonedDateTime getAuthenticationDate() { return authenticationDate; }
    
    public void setAuthenticationDate(ZonedDateTime authenticationDate) {
        this.authenticationDate = authenticationDate;
    }
}

Integration Interfaces

Remember Me Support

package org.apereo.cas.authentication;

public interface RememberMeCredential {
    String AUTHENTICATION_ATTRIBUTE_REMEMBER_ME = "rememberMe";
    
    boolean isRememberMe();
    void setRememberMe(boolean rememberMe);
}

Credential Selection

// Create credential selection predicates
Predicate<Credential> usernamePasswordPredicate = 
    credential -> credential instanceof UsernamePasswordCredential;

Predicate<Credential> tokenCredentialPredicate = 
    credential -> credential instanceof OneTimeTokenCredential;

Predicate<Credential> sourcePredicate = credential -> {
    if (credential instanceof UsernamePasswordCredential) {
        UsernamePasswordCredential upc = (UsernamePasswordCredential) credential;
        return "LDAP".equals(upc.getSource());
    }
    return false;
};

// Using predicates for handler selection
public class ConditionalAuthenticationHandler extends AbstractAuthenticationHandler {
    
    private final Predicate<Credential> credentialSelector;
    
    public ConditionalAuthenticationHandler(Predicate<Credential> credentialSelector) {
        this.credentialSelector = credentialSelector;
    }
    
    @Override
    public boolean supports(Credential credential) {
        return credentialSelector.test(credential);
    }
}

Configuration Examples

Spring Configuration

@Configuration
public class CredentialConfiguration {
    
    @Bean
    public Predicate<Credential> usernamePasswordCredentialSelector() {
        return credential -> credential instanceof UsernamePasswordCredential;
    }
    
    @Bean
    public Predicate<Credential> rememberMeCredentialSelector() {
        return credential -> credential instanceof RememberMeCredential && 
                           ((RememberMeCredential) credential).isRememberMe();
    }
    
    @Bean
    public MessageSource credentialValidationMessageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("credential-validation-messages");
        return messageSource;
    }
}

Programmatic Usage

// Create and validate credentials
UsernamePasswordCredential credential = new UsernamePasswordCredential("user", "password");
credential.setSource("LDAP");
credential.getCustomFields().put("department", "IT");

// Validate credential
ValidationContext validationContext = new DefaultValidationContext();
credential.validate(validationContext);

if (!validationContext.getMessageContext().hasErrorMessages()) {
    // Credential is valid, proceed with authentication
    AuthenticationHandler handler = getAppropriateHandler(credential);
    AuthenticationHandlerExecutionResult result = handler.authenticate(credential);
}

// Working with remember-me credentials
RememberMeUsernamePasswordCredential rememberMeCredential = 
    new RememberMeUsernamePasswordCredential("user", "password", true);

// Check remember-me status
if (rememberMeCredential.isRememberMe()) {
    // Apply remember-me logic
}

// Secure password handling
char[] password = "securePassword".toCharArray();
try {
    UsernamePasswordCredential secureCredential = 
        new UsernamePasswordCredential("user", password);
    // Use credential
} finally {
    // Always clear sensitive data
    Arrays.fill(password, '\0');
}

Credential handling provides the foundation for secure authentication data management, supporting various credential types, validation mechanisms, and security practices essential for enterprise authentication systems.

Install with Tessl CLI

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

docs

adaptive-authentication.md

authentication-handlers.md

authentication-policies.md

credential-handling.md

index.md

password-policies.md

principal-resolution.md

tile.json