From a201a2b862e3e20bce63b59d44def565b893d273 Mon Sep 17 00:00:00 2001
From: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
Date: Fri, 22 Aug 2025 16:21:26 -0600
Subject: [PATCH 01/14] Add Authentication.Builder
This commit adds a new default method to Authentication
for the purposes of creating a Builder based on the current
authentication, allowing other authentications to be
applied to it as a composite.
It also adds Builders for each one of the authentication
result classes.
Issue gh-17861
---
.../CasAuthenticationToken.java | 69 +++++++++++++++++++
.../CasAuthenticationTokenTests.java | 20 ++++++
.../AbstractAuthenticationToken.java | 34 +++++++++
.../RememberMeAuthenticationToken.java | 47 +++++++++++++
.../TestingAuthenticationToken.java | 48 +++++++++++++
.../UsernamePasswordAuthenticationToken.java | 44 ++++++++++++
.../jaas/JaasAuthenticationToken.java | 46 +++++++++++++
.../ott/OneTimeTokenAuthentication.java | 42 +++++++++++
.../security/core/Authentication.java | 54 ++++++++++++++-
.../core/NoopAuthenticationBuilder.java | 53 ++++++++++++++
.../AbstractAuthenticationBuilderTests.java | 61 ++++++++++++++++
.../TestingAuthenticationTokenTests.java | 15 ++++
...rnamePasswordAuthenticationTokenTests.java | 16 +++++
.../jaas/JaasAuthenticationTokenTests.java | 46 +++++++++++++
.../ott/OneTimeTokenAuthenticationTests.java | 41 +++++++++++
.../OAuth2AuthenticationToken.java | 42 +++++++++++
.../OAuth2AuthenticationTokenTests.java | 16 +++++
.../BearerTokenAuthentication.java | 43 ++++++++++++
.../JwtAuthenticationToken.java | 42 +++++++++++
.../BearerTokenAuthenticationTests.java | 18 +++++
.../JwtAuthenticationTokenTests.java | 14 ++++
.../Saml2AssertionAuthentication.java | 55 +++++++++++++++
.../Saml2AssertionAuthenticationTests.java | 44 ++++++++++++
.../PreAuthenticatedAuthenticationToken.java | 43 ++++++++++++
...AuthenticatedAuthenticationTokenTests.java | 14 ++++
.../WebAuthnAuthentication.java | 35 ++++++++++
.../WebAuthnAuthenticationTests.java | 15 ++++
27 files changed, 1016 insertions(+), 1 deletion(-)
create mode 100644 core/src/main/java/org/springframework/security/core/NoopAuthenticationBuilder.java
create mode 100644 core/src/test/java/org/springframework/security/authentication/AbstractAuthenticationBuilderTests.java
create mode 100644 core/src/test/java/org/springframework/security/authentication/jaas/JaasAuthenticationTokenTests.java
create mode 100644 core/src/test/java/org/springframework/security/authentication/ott/OneTimeTokenAuthenticationTests.java
create mode 100644 saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/Saml2AssertionAuthenticationTests.java
diff --git a/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationToken.java b/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationToken.java
index e19f8bd33c..9fd4ba204e 100644
--- a/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationToken.java
+++ b/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationToken.java
@@ -20,8 +20,10 @@ import java.io.Serializable;
import java.util.Collection;
import org.apereo.cas.client.validation.Assertion;
+import org.jspecify.annotations.NonNull;
import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;
@@ -153,6 +155,11 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken implemen
return this.userDetails;
}
+ @Override
+ public Builder toBuilder() {
+ return new Builder().apply(this);
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -162,4 +169,66 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken implemen
return (sb.toString());
}
+ /**
+ * A builder preserving the concrete {@link Authentication} type
+ *
+ * @since 7.0
+ */
+ public static final class Builder extends AbstractAuthenticationBuilder<@NonNull CasAuthenticationToken, Builder> {
+
+ private Integer keyHash;
+
+ private Object principal;
+
+ private Object credentials;
+
+ private UserDetails userDetails;
+
+ private Assertion assertion;
+
+ private Builder() {
+
+ }
+
+ public Builder apply(CasAuthenticationToken authentication) {
+ return super.apply(authentication).keyHash(authentication.keyHash)
+ .principal(authentication.principal)
+ .credentials(authentication.credentials)
+ .userDetails(authentication.userDetails)
+ .assertion(authentication.assertion);
+ }
+
+ public Builder keyHash(Integer keyHash) {
+ this.keyHash = keyHash;
+ return this;
+ }
+
+ public Builder principal(Object principal) {
+ this.principal = principal;
+ return this;
+ }
+
+ public Builder credentials(Object credentials) {
+ this.credentials = credentials;
+ return this;
+ }
+
+ public Builder userDetails(UserDetails userDetails) {
+ this.userDetails = userDetails;
+ return this;
+ }
+
+ public Builder assertion(Assertion assertion) {
+ this.assertion = assertion;
+ return this;
+ }
+
+ @Override
+ protected @NonNull CasAuthenticationToken build(CollectionAuthentication.
@@ -88,6 +92,11 @@ public class RememberMeAuthenticationToken extends AbstractAuthenticationToken {
return this.principal;
}
+ @Override
+ public Builder toBuilder() {
+ return new Builder().apply(this);
+ }
+
@Override
public boolean equals(Object obj) {
if (!super.equals(obj)) {
@@ -106,4 +115,42 @@ public class RememberMeAuthenticationToken extends AbstractAuthenticationToken {
return result;
}
+ /**
+ * A builder preserving the concrete {@link Authentication} type
+ *
+ * @since 7.0
+ */
+ public static final class Builder extends AbstractAuthenticationBuilderAuthenticationManager to indicate the authorities that the
* principal has been granted. Note that classes should not rely on this value as
@@ -64,7 +70,7 @@ public interface Authentication extends Principal, Serializable {
* instance.
*
+ * By default, this method adds the authorities from {@code authentication} to
+ * this builder
+ * @return the {@link Builder} for further configuration
+ */
+ default B apply(Authentication authentication) {
+ Assert.isTrue(authentication.isAuthenticated(), "cannot apply an unauthenticated token");
+ return authorities((a) -> a.addAll(authentication.getAuthorities()));
+ }
+
+ /**
+ * Apply these authorities to the builder.
+ * @param authorities the authorities to apply
+ * @return the {@link Builder} for further configuration
+ */
+ B authorities(Consumer