Working on SAML 2

This commit is contained in:
DarioGii
2025-02-01 23:30:28 +00:00
committed by Dario Ghunney Ware
parent 704da399d4
commit 6478674d9b
25 changed files with 339 additions and 277 deletions

View File

@@ -6,6 +6,7 @@ import java.security.interfaces.RSAPrivateKey;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.Resource;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
@@ -14,6 +15,7 @@ import org.springframework.security.saml2.provider.service.authentication.Saml2A
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import com.coveo.saml.SamlClient;
import com.coveo.saml.SamlException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -49,9 +51,8 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
} else if (authentication instanceof OAuth2AuthenticationToken) {
// Handle OAuth2 logout redirection
getRedirect_oauth2(request, response, authentication);
}
// Handle Username/Password logout
else if (authentication instanceof UsernamePasswordAuthenticationToken) {
} else if (authentication instanceof UsernamePasswordAuthenticationToken) {
// Handle Username/Password logout
getRedirectStrategy().sendRedirect(request, response, LOGOUT_PATH);
} else {
// Handle unknown authentication types
@@ -90,27 +91,7 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
certificates.add(certificate);
// Construct URLs required for SAML configuration
String serverUrl =
SPDFApplication.getStaticBaseUrl() + ":" + SPDFApplication.getStaticPort();
String relyingPartyIdentifier =
serverUrl + "/saml2/service-provider-metadata/" + registrationId;
String assertionConsumerServiceUrl = serverUrl + "/login/saml2/sso/" + registrationId;
String idpUrl = samlConf.getIdpSingleLogoutUrl();
String idpIssuer = samlConf.getIdpIssuer();
// Create SamlClient instance for SAML logout
SamlClient samlClient =
new SamlClient(
relyingPartyIdentifier,
assertionConsumerServiceUrl,
idpUrl,
idpIssuer,
certificates,
SamlClient.SamlIdpBinding.POST);
SamlClient samlClient = getSamlClient(registrationId, samlConf, certificates);
// Read private key for service provider
Resource privateKeyResource = samlConf.getPrivateKey();
@@ -127,7 +108,6 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
}
}
// Redirect for OAuth2 authentication logout
private void getRedirect_oauth2(
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException {
@@ -164,12 +144,45 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
response.sendRedirect(redirectUrl);
}
default -> {
log.info("Redirecting to default logout URL: {}", redirectUrl);
response.sendRedirect(redirectUrl);
String logoutUrl = oauth.getLogoutUrl();
if (StringUtils.isNotBlank(logoutUrl)) {
log.info("Redirecting to logout URL: {}", logoutUrl);
response.sendRedirect(logoutUrl);
} else {
log.info("Redirecting to default logout URL: {}", redirectUrl);
response.sendRedirect(redirectUrl);
}
}
}
}
// Redirect for OAuth2 authentication logout
private static SamlClient getSamlClient(
String registrationId, SAML2 samlConf, List<X509Certificate> certificates)
throws SamlException {
String serverUrl =
SPDFApplication.getStaticBaseUrl() + ":" + SPDFApplication.getStaticPort();
String relyingPartyIdentifier =
serverUrl + "/saml2/service-provider-metadata/" + registrationId;
String assertionConsumerServiceUrl = serverUrl + "/login/saml2/sso/" + registrationId;
String idpUrl = samlConf.getIdpSingleLogoutUrl();
String idpIssuer = samlConf.getIdpIssuer();
// Create SamlClient instance for SAML logout
return new SamlClient(
relyingPartyIdentifier,
assertionConsumerServiceUrl,
idpUrl,
idpIssuer,
certificates,
SamlClient.SamlIdpBinding.POST);
}
/**
* Handles different error scenarios during logout. Will return a <code>String</code> containing
* the error request parameter.

View File

@@ -23,6 +23,10 @@ import org.springframework.security.saml2.provider.service.web.authentication.Op
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.context.DelegatingSecurityContextRepository;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
import org.springframework.security.web.savedrequest.NullRequestCache;
@@ -51,11 +55,7 @@ public class SecurityConfiguration {
private final CustomUserDetailsService userDetailsService;
private final UserService userService;
@Qualifier("loginEnabled")
private final boolean loginEnabledValue;
@Qualifier("runningEE")
private final boolean runningEE;
private final ApplicationProperties applicationProperties;
@@ -105,10 +105,11 @@ public class SecurityConfiguration {
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
public SecurityFilterChain filterChain(HttpSecurity http, SecurityContextRepository securityContextRepository) throws Exception {
if (applicationProperties.getSecurity().getCsrfDisabled() || !loginEnabledValue) {
http.csrf(csrf -> csrf.disable());
}
if (loginEnabledValue) {
http.addFilterBefore(
userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
@@ -164,8 +165,7 @@ public class SecurityConfiguration {
.logoutSuccessHandler(
new CustomLogoutSuccessHandler(applicationProperties))
.clearAuthentication(true)
.invalidateHttpSession( // Invalidate session
true)
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID", "remember-me"));
http.rememberMe(
rememberMeConfigurer -> // Use the configurator directly
@@ -234,7 +234,7 @@ public class SecurityConfiguration {
.
/*
This Custom handler is used to check if the OAUTH2 user trying to log in, already exists in the database.
If user exists, login proceeds as usual. If user does not exist, then it is autocreated but only if 'OAUTH2AutoCreateUser'
If user exists, login proceeds as usual. If user does not exist, then it is auto-created but only if 'OAUTH2AutoCreateUser'
is set as true, else login fails with an error message advising the same.
*/
successHandler(
@@ -258,14 +258,20 @@ public class SecurityConfiguration {
.permitAll());
}
// Handle SAML
if (applicationProperties.getSecurity().isSaml2Active()) {
// && runningEE
if (applicationProperties.getSecurity().isSaml2Active() && runningEE) {
// Configure the authentication provider
OpenSaml4AuthenticationProvider authenticationProvider =
new OpenSaml4AuthenticationProvider();
authenticationProvider.setResponseAuthenticationConverter(
new CustomSaml2ResponseAuthenticationConverter(userService));
http.authenticationProvider(authenticationProvider)
.securityContext(security ->
security.securityContextRepository(
new DelegatingSecurityContextRepository(
new RequestAttributeSecurityContextRepository(),
new HttpSessionSecurityContextRepository())
)
)
.saml2Login(
saml2 -> {
try {
@@ -284,12 +290,13 @@ public class SecurityConfiguration {
.authenticationRequestResolver(
saml2AuthenticationRequestResolver);
} catch (Exception e) {
log.error("Error configuring SAML2 login", e);
log.error("Error configuring SAML 2 login", e);
throw new RuntimeException(e);
}
});
}
} else {
log.info("SAML 2 login is not enabled. Using default.");
http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
}
return http.build();
@@ -315,7 +322,7 @@ public class SecurityConfiguration {
}
@Bean
public boolean activSecurity() {
public boolean activeSecurity() {
return true;
}
}

View File

@@ -88,7 +88,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
// Use API key to authenticate. This requires you to have an authentication
// provider for API keys.
Optional<User> user = userService.getUserByApiKey(apiKey);
if (!user.isPresent()) {
if (user.isEmpty()) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter().write("Invalid API Key.");
return;

View File

@@ -373,18 +373,15 @@ public class UserService implements UserServiceInterface {
public void invalidateUserSessions(String username) {
String usernameP = "";
for (Object principal : sessionRegistry.getAllPrincipals()) {
for (SessionInformation sessionsInformation :
sessionRegistry.getAllSessions(principal, false)) {
if (principal instanceof UserDetails) {
UserDetails userDetails = (UserDetails) principal;
if (principal instanceof UserDetails userDetails) {
usernameP = userDetails.getUsername();
} else if (principal instanceof OAuth2User) {
OAuth2User oAuth2User = (OAuth2User) principal;
} else if (principal instanceof OAuth2User oAuth2User) {
usernameP = oAuth2User.getName();
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
CustomSaml2AuthenticatedPrincipal saml2User =
(CustomSaml2AuthenticatedPrincipal) principal;
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal saml2User) {
usernameP = saml2User.getName();
} else if (principal instanceof String) {
usernameP = (String) principal;
@@ -398,6 +395,7 @@ public class UserService implements UserServiceInterface {
public String getCurrentUsername() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
return ((UserDetails) principal).getUsername();
} else if (principal instanceof OAuth2User) {
@@ -406,8 +404,6 @@ public class UserService implements UserServiceInterface {
applicationProperties.getSecurity().getOauth2().getUseAsUsername());
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
return ((CustomSaml2AuthenticatedPrincipal) principal).getName();
} else if (principal instanceof String) {
return (String) principal;
} else {
return principal.toString();
}

View File

@@ -50,8 +50,11 @@ public class CustomOAuth2AuthenticationFailureHandler
if (error.getErrorCode().equals("Password must not be null")) {
errorCode = "userAlreadyExistsWeb";
}
log.error("OAuth2 Authentication error: " + errorCode);
log.error("OAuth2AuthenticationException", exception);
log.error(
"OAuth2 Authentication error: {}",
errorCode != null ? errorCode : exception.getMessage(),
exception);
getRedirectStrategy().sendRedirect(request, response, "/login?errorOAuth=" + errorCode);
}
log.error("Unhandled authentication exception", exception);

View File

@@ -47,23 +47,31 @@ public class CustomOAuth2UserService implements OAuth2UserService<OidcUserReques
OAUTH2 oauth2 = applicationProperties.getSecurity().getOauth2();
UsernameAttribute usernameAttribute =
UsernameAttribute.valueOf(oauth2.getUseAsUsername().toUpperCase());
String username = usernameAttribute.getName();
String usernameAttributeKey = usernameAttribute.getName();
Optional<User> internalUser = userService.findByUsernameIgnoreCase(username);
// todo: save user by OIDC ID instead of username
Optional<User> internalUser =
userService.findByUsernameIgnoreCase(user.getAttribute(usernameAttributeKey));
if (internalUser.isPresent()) {
if (loginAttemptService.isBlocked(username)) {
String internalUsername = internalUser.get().getUsername();
if (loginAttemptService.isBlocked(internalUsername)) {
throw new LockedException(
"Your account has been locked due to too many failed login attempts.");
"The account "
+ internalUsername
+ " has been locked due to too many failed login attempts.");
}
if (userService.hasPassword(username)) {
if (userService.hasPassword(usernameAttributeKey)) {
throw new IllegalArgumentException("Password must not be null");
}
}
// Return a new OidcUser with adjusted attributes
return new DefaultOidcUser(
user.getAuthorities(), userRequest.getIdToken(), user.getUserInfo(), username);
user.getAuthorities(),
userRequest.getIdToken(),
user.getUserInfo(),
usernameAttributeKey);
} catch (IllegalArgumentException e) {
log.error("Error loading OIDC user: {}", e.getMessage());
throw new OAuth2AuthenticationException(new OAuth2Error(e.getMessage()), e);

View File

@@ -94,7 +94,7 @@ public class OAuth2Configuration {
.clientId(keycloak.getClientId())
.clientSecret(keycloak.getClientSecret())
.scope(keycloak.getScopes())
.userNameAttributeName(keycloak.getUseAsUsername().name())
.userNameAttributeName(keycloak.getUseAsUsername().getName())
.clientName(keycloak.getClientName())
.build())
: Optional.empty();
@@ -125,7 +125,7 @@ public class OAuth2Configuration {
.authorizationUri(google.getAuthorizationUri())
.tokenUri(google.getTokenUri())
.userInfoUri(google.getUserInfoUri())
.userNameAttributeName(google.getUseAsUsername().name())
.userNameAttributeName(google.getUseAsUsername().getName())
.clientName(google.getClientName())
.redirectUri(REDIRECT_URI_PATH + google.getName())
.authorizationGrantType(AUTHORIZATION_CODE)
@@ -158,7 +158,7 @@ public class OAuth2Configuration {
.authorizationUri(github.getAuthorizationUri())
.tokenUri(github.getTokenUri())
.userInfoUri(github.getUserInfoUri())
.userNameAttributeName(github.getUseAsUsername().name())
.userNameAttributeName(github.getUseAsUsername().getName())
.clientName(github.getClientName())
.redirectUri(REDIRECT_URI_PATH + github.getName())
.authorizationGrantType(AUTHORIZATION_CODE)
@@ -186,6 +186,7 @@ public class OAuth2Configuration {
oauth.getClientSecret(),
oauth.getScopes(),
UsernameAttribute.valueOf(oauth.getUseAsUsername().toUpperCase()),
oauth.getLogoutUrl(),
null,
null,
null);
@@ -220,9 +221,7 @@ public class OAuth2Configuration {
*/
@Bean
@ConditionalOnProperty(
value = "security.oauth2.enabled",
havingValue = "true")
@ConditionalOnProperty(value = "security.oauth2.enabled", havingValue = "true")
GrantedAuthoritiesMapper userAuthoritiesMapper() {
return (authorities) -> {
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();

View File

@@ -8,7 +8,6 @@ import org.springframework.security.saml2.core.Saml2Error;
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -22,7 +21,9 @@ public class CustomSaml2AuthenticationFailureHandler extends SimpleUrlAuthentica
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception)
throws IOException, ServletException {
throws IOException {
log.error("Authentication error", exception);
if (exception instanceof Saml2AuthenticationException) {
Saml2Error error = ((Saml2AuthenticationException) exception).getSaml2Error();
getRedirectStrategy()
@@ -34,6 +35,5 @@ public class CustomSaml2AuthenticationFailureHandler extends SimpleUrlAuthentica
response,
"/login?errorOAuth=not_authentication_provider_found");
}
log.error("AuthenticationException: " + exception);
}
}

View File

@@ -112,13 +112,11 @@ public class CustomSaml2AuthenticationSuccessHandler
userService.processSSOPostLogin(username, saml2.getAutoCreateUser());
log.debug("Successfully processed authentication for user: {}", username);
response.sendRedirect(contextPath + "/");
return;
} catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) {
log.debug(
"Invalid username detected for user: {}, redirecting to logout",
username);
response.sendRedirect(contextPath + "/logout?invalidUsername=true");
return;
}
}
} else {

View File

@@ -21,7 +21,7 @@ import stirling.software.SPDF.model.User;
public class CustomSaml2ResponseAuthenticationConverter
implements Converter<ResponseToken, Saml2Authentication> {
private UserService userService;
private final UserService userService;
public CustomSaml2ResponseAuthenticationConverter(UserService userService) {
this.userService = userService;
@@ -61,10 +61,10 @@ public class CustomSaml2ResponseAuthenticationConverter
Map<String, List<Object>> attributes = extractAttributes(assertion);
// Debug log with actual values
log.debug("Extracted SAML Attributes: " + attributes);
log.debug("Extracted SAML Attributes: {}", attributes);
// Try to get username/identifier in order of preference
String userIdentifier = null;
String userIdentifier;
if (hasAttribute(attributes, "username")) {
userIdentifier = getFirstAttributeValue(attributes, "username");
} else if (hasAttribute(attributes, "emailaddress")) {
@@ -84,10 +84,8 @@ public class CustomSaml2ResponseAuthenticationConverter
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ROLE_USER");
if (userOpt.isPresent()) {
User user = userOpt.get();
if (user != null) {
simpleGrantedAuthority =
new SimpleGrantedAuthority(userService.findRole(user).getAuthority());
}
simpleGrantedAuthority =
new SimpleGrantedAuthority(userService.findRole(user).getAuthority());
}
List<String> sessionIndexes = new ArrayList<>();
@@ -102,7 +100,7 @@ public class CustomSaml2ResponseAuthenticationConverter
return new Saml2Authentication(
principal,
responseToken.getToken().getSaml2Response(),
Collections.singletonList(simpleGrantedAuthority));
List.of(simpleGrantedAuthority));
}
private boolean hasAttribute(Map<String, List<Object>> attributes, String name) {

View File

@@ -11,10 +11,12 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.security.saml2.core.Saml2X509Credential;
import org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType;
import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
import org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
import jakarta.servlet.http.HttpServletRequest;
@@ -39,7 +41,7 @@ public class SAML2Configuration {
@ConditionalOnProperty(name = "security.saml2.enabled", havingValue = "true")
public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception {
SAML2 samlConf = applicationProperties.getSecurity().getSaml2();
X509Certificate idpCert = CertificateUtils.readCertificate(samlConf.getidpCert());
X509Certificate idpCert = CertificateUtils.readCertificate(samlConf.getIdpCert());
Saml2X509Credential verificationCredential = Saml2X509Credential.verification(idpCert);
Resource privateKeyResource = samlConf.getPrivateKey();
Resource certificateResource = samlConf.getSpCert();
@@ -51,15 +53,26 @@ public class SAML2Configuration {
RelyingPartyRegistration rp =
RelyingPartyRegistration.withRegistrationId(samlConf.getRegistrationId())
.signingX509Credentials(c -> c.add(signingCredential))
.entityId(samlConf.getIdpIssuer())
.singleLogoutServiceBinding(Saml2MessageBinding.POST)
.singleLogoutServiceLocation(samlConf.getIdpSingleLogoutUrl())
.singleLogoutServiceResponseLocation("http://localhost:8080/login")
.assertionConsumerServiceBinding(Saml2MessageBinding.POST)
.assertionConsumerServiceLocation(
"{baseUrl}/login/saml2/sso/{registrationId}")
.assertingPartyMetadata(
metadata ->
metadata.entityId(samlConf.getIdpIssuer())
.singleSignOnServiceLocation(
samlConf.getIdpSingleLoginUrl())
.verificationX509Credentials(
c -> c.add(verificationCredential))
.singleSignOnServiceBinding(
Saml2MessageBinding.POST)
.singleSignOnServiceLocation(
samlConf.getIdpSingleLoginUrl())
.singleLogoutServiceBinding(
Saml2MessageBinding.POST)
.singleLogoutServiceLocation(
samlConf.getIdpSingleLogoutUrl())
.wantAuthnRequestsSigned(true))
.build();
return new InMemoryRelyingPartyRegistrationRepository(rp);
@@ -71,58 +84,93 @@ public class SAML2Configuration {
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {
OpenSaml4AuthenticationRequestResolver resolver =
new OpenSaml4AuthenticationRequestResolver(relyingPartyRegistrationRepository);
resolver.setAuthnRequestCustomizer(
customizer -> {
log.debug("Customizing SAML Authentication request");
AuthnRequest authnRequest = customizer.getAuthnRequest();
log.debug("AuthnRequest ID: {}", authnRequest.getID());
if (authnRequest.getID() == null) {
authnRequest.setID("ARQ" + UUID.randomUUID().toString());
}
log.debug("AuthnRequest new ID after set: {}", authnRequest.getID());
log.debug("AuthnRequest IssueInstant: {}", authnRequest.getIssueInstant());
log.debug(
"AuthnRequest Issuer: {}",
authnRequest.getIssuer() != null
? authnRequest.getIssuer().getValue()
: "null");
HttpServletRequest request = customizer.getRequest();
// Log HTTP request details
log.debug("HTTP Request Method: {}", request.getMethod());
log.debug("Request URI: {}", request.getRequestURI());
log.debug("Request URL: {}", request.getRequestURL().toString());
log.debug("Query String: {}", request.getQueryString());
log.debug("Remote Address: {}", request.getRemoteAddr());
// Log headers
Collections.list(request.getHeaderNames())
.forEach(
headerName -> {
log.debug(
"Header - {}: {}",
headerName,
request.getHeader(headerName));
});
// Log SAML specific parameters
log.debug("SAML Request Parameters:");
log.debug("SAMLRequest: {}", request.getParameter("SAMLRequest"));
log.debug("RelayState: {}", request.getParameter("RelayState"));
// Log session debugrmation if exists
if (request.getSession(false) != null) {
log.debug("Session ID: {}", request.getSession().getId());
}
// Log any assertions consumer service details if present
if (authnRequest.getAssertionConsumerServiceURL() != null) {
AuthnRequest authnRequest = customizer.getAuthnRequest();
HttpSessionSaml2AuthenticationRequestRepository requestRepository =
new HttpSessionSaml2AuthenticationRequestRepository();
AbstractSaml2AuthenticationRequest saml2AuthenticationRequest =
requestRepository.loadAuthenticationRequest(request);
if (saml2AuthenticationRequest != null) {
String sessionId = request.getSession(false).getId();
log.debug(
"AssertionConsumerServiceURL: {}",
authnRequest.getAssertionConsumerServiceURL());
}
// Log NameID policy if present
if (authnRequest.getNameIDPolicy() != null) {
log.debug(
"NameIDPolicy Format: {}",
authnRequest.getNameIDPolicy().getFormat());
"Retrieving SAML 2 authentication request ID from the current HTTP session {}",
sessionId);
String authenticationRequestId = saml2AuthenticationRequest.getId();
if (!authenticationRequestId.isBlank()) {
authnRequest.setID(authenticationRequestId);
} else {
log.warn(
"No authentication request found for HTTP session {}. Generating new ID",
sessionId);
authnRequest.setID("ARQ" + UUID.randomUUID().toString().substring(1));
}
} else {
log.debug("Generating new authentication request ID");
authnRequest.setID("ARQ" + UUID.randomUUID().toString().substring(1));
}
logAuthnRequestDetails(authnRequest);
logHttpRequestDetails(request);
});
return resolver;
}
private static void logAuthnRequestDetails(AuthnRequest authnRequest) {
String message =
"""
AuthnRequest:
ID: {}
Issuer: {}
IssueInstant: {}
AssertionConsumerService (ACS) URL: {}
""";
log.debug(
message,
authnRequest.getID(),
authnRequest.getIssuer() != null ? authnRequest.getIssuer().getValue() : null,
authnRequest.getIssueInstant(),
authnRequest.getAssertionConsumerServiceURL());
if (authnRequest.getNameIDPolicy() != null) {
log.debug("NameIDPolicy Format: {}", authnRequest.getNameIDPolicy().getFormat());
}
}
private static void logHttpRequestDetails(HttpServletRequest request) {
log.debug("HTTP Headers: ");
Collections.list(request.getHeaderNames())
.forEach(
headerName ->
log.debug("{}: {}", headerName, request.getHeader(headerName)));
String message =
"""
HTTP Request Method: {}
Session ID: {}
Request Path: {}
Query String: {}
Remote Address: {}
SAML Request Parameters:
SAMLRequest: {}
RelayState: {}
""";
log.debug(
message,
request.getMethod(),
request.getSession().getId(),
request.getRequestURI(),
request.getQueryString(),
request.getRemoteAddr(),
request.getParameter("SAMLRequest"),
request.getParameter("RelayState"));
}
}