Skip to content

Commit b235c45

Browse files
committed
Support Updating Authorities
There are a number of scenarios where it's desireable to update the authorities in an authentication after identity has already been established. For example, if a second factor is required or if temporary authorization is needed for a given page, these likely won't update the principal; they simply need to add more authorities to the existing authentication.
1 parent 179f7f8 commit b235c45

File tree

14 files changed

+153
-11
lines changed

14 files changed

+153
-11
lines changed

cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationToken.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.apereo.cas.client.validation.Assertion;
2323

2424
import org.springframework.security.authentication.AbstractAuthenticationToken;
25+
import org.springframework.security.core.AuthenticationResult;
2526
import org.springframework.security.core.GrantedAuthority;
2627
import org.springframework.security.core.userdetails.UserDetails;
2728
import org.springframework.util.Assert;
@@ -33,7 +34,7 @@
3334
* @author Ben Alex
3435
* @author Scott Battaglia
3536
*/
36-
public class CasAuthenticationToken extends AbstractAuthenticationToken implements Serializable {
37+
public class CasAuthenticationToken extends AbstractAuthenticationToken implements Serializable, AuthenticationResult {
3738

3839
private static final long serialVersionUID = 620L;
3940

@@ -104,6 +105,12 @@ private CasAuthenticationToken(final Integer keyHash, final Object principal, fi
104105
setAuthenticated(true);
105106
}
106107

108+
@Override
109+
public CasAuthenticationToken withGrantedAuthorities(Collection<GrantedAuthority> authorities) {
110+
return new CasAuthenticationToken(this.keyHash, getPrincipal(), getCredentials(), authorities, this.userDetails,
111+
this.assertion);
112+
}
113+
107114
private static Integer extractKeyHash(String key) {
108115
Assert.hasLength(key, "key cannot be null or empty");
109116
return key.hashCode();

core/src/main/java/org/springframework/security/authentication/RememberMeAuthenticationToken.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
import java.util.Collection;
2020

21+
import org.springframework.security.core.AuthenticationResult;
2122
import org.springframework.security.core.GrantedAuthority;
23+
import org.springframework.util.Assert;
2224

2325
/**
2426
* Represents a remembered <code>Authentication</code>.
@@ -29,7 +31,7 @@
2931
* @author Ben Alex
3032
* @author Luke Taylor
3133
*/
32-
public class RememberMeAuthenticationToken extends AbstractAuthenticationToken {
34+
public class RememberMeAuthenticationToken extends AbstractAuthenticationToken implements AuthenticationResult {
3335

3436
private static final long serialVersionUID = 620L;
3537

@@ -70,6 +72,12 @@ private RememberMeAuthenticationToken(Integer keyHash, Object principal,
7072
setAuthenticated(true);
7173
}
7274

75+
@Override
76+
public RememberMeAuthenticationToken withGrantedAuthorities(Collection<GrantedAuthority> authorities) {
77+
Assert.isTrue(isAuthenticated(), "cannot grant authorities to unauthenticated tokens");
78+
return new RememberMeAuthenticationToken(this.keyHash, getPrincipal(), authorities);
79+
}
80+
7381
/**
7482
* Always returns an empty <code>String</code>
7583
* @return an empty String

core/src/main/java/org/springframework/security/authentication/TestingAuthenticationToken.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
import java.util.Collection;
2020
import java.util.List;
2121

22+
import org.springframework.security.core.AuthenticationResult;
2223
import org.springframework.security.core.GrantedAuthority;
2324
import org.springframework.security.core.authority.AuthorityUtils;
25+
import org.springframework.util.Assert;
2426

2527
/**
2628
* An {@link org.springframework.security.core.Authentication} implementation that is
@@ -30,7 +32,7 @@
3032
*
3133
* @author Ben Alex
3234
*/
33-
public class TestingAuthenticationToken extends AbstractAuthenticationToken {
35+
public class TestingAuthenticationToken extends AbstractAuthenticationToken implements AuthenticationResult {
3436

3537
private static final long serialVersionUID = 1L;
3638

@@ -61,6 +63,12 @@ public TestingAuthenticationToken(Object principal, Object credentials,
6163
setAuthenticated(true);
6264
}
6365

66+
@Override
67+
public TestingAuthenticationToken withGrantedAuthorities(Collection<GrantedAuthority> authorities) {
68+
Assert.isTrue(isAuthenticated(), "cannot grant authorities to unauthenticated tokens");
69+
return new TestingAuthenticationToken(getPrincipal(), this.credentials, authorities);
70+
}
71+
6472
@Override
6573
public Object getCredentials() {
6674
return this.credentials;

core/src/main/java/org/springframework/security/authentication/UsernamePasswordAuthenticationToken.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.jspecify.annotations.Nullable;
2222

23+
import org.springframework.security.core.AuthenticationResult;
2324
import org.springframework.security.core.GrantedAuthority;
2425
import org.springframework.util.Assert;
2526

@@ -35,7 +36,7 @@
3536
* @author Ben Alex
3637
* @author Norbert Nowak
3738
*/
38-
public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
39+
public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken implements AuthenticationResult {
3940

4041
private static final long serialVersionUID = 620L;
4142

@@ -100,6 +101,12 @@ public static UsernamePasswordAuthenticationToken authenticated(Object principal
100101
return new UsernamePasswordAuthenticationToken(principal, credentials, authorities);
101102
}
102103

104+
@Override
105+
public UsernamePasswordAuthenticationToken withGrantedAuthorities(Collection<GrantedAuthority> authorities) {
106+
Assert.isTrue(isAuthenticated(), "cannot grant authorities to unauthenticated tokens");
107+
return new UsernamePasswordAuthenticationToken(getPrincipal(), getCredentials(), authorities);
108+
}
109+
103110
@Override
104111
public @Nullable Object getCredentials() {
105112
return this.credentials;

core/src/main/java/org/springframework/security/authentication/jaas/JaasAuthenticationToken.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.security.authentication.jaas;
1818

19+
import java.util.ArrayList;
20+
import java.util.Collection;
1921
import java.util.List;
2022

2123
import javax.security.auth.login.LoginContext;
@@ -24,6 +26,7 @@
2426

2527
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
2628
import org.springframework.security.core.GrantedAuthority;
29+
import org.springframework.util.Assert;
2730

2831
/**
2932
* UsernamePasswordAuthenticationToken extension to carry the Jaas LoginContext that the
@@ -52,4 +55,11 @@ public LoginContext getLoginContext() {
5255
return this.loginContext;
5356
}
5457

58+
@Override
59+
public JaasAuthenticationToken withGrantedAuthorities(Collection<GrantedAuthority> authorities) {
60+
Assert.isTrue(isAuthenticated(), "cannot grant authorities to unauthenticated tokens");
61+
return new JaasAuthenticationToken(getPrincipal(), getCredentials(), new ArrayList<>(authorities),
62+
this.loginContext);
63+
}
64+
5565
}

core/src/main/java/org/springframework/security/authentication/ott/OneTimeTokenAuthenticationToken.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,22 @@
1919
import java.io.Serial;
2020
import java.util.Collection;
2121
import java.util.Collections;
22+
import java.util.Objects;
2223

2324
import org.jspecify.annotations.Nullable;
2425

2526
import org.springframework.security.authentication.AbstractAuthenticationToken;
27+
import org.springframework.security.core.AuthenticationResult;
2628
import org.springframework.security.core.GrantedAuthority;
29+
import org.springframework.util.Assert;
2730

2831
/**
2932
* Represents a One-Time Token authentication that can be authenticated or not.
3033
*
3134
* @author Marcus da Coregio
3235
* @since 6.4
3336
*/
34-
public class OneTimeTokenAuthenticationToken extends AbstractAuthenticationToken {
37+
public class OneTimeTokenAuthenticationToken extends AbstractAuthenticationToken implements AuthenticationResult {
3538

3639
@Serial
3740
private static final long serialVersionUID = -8691636031126328365L;
@@ -56,6 +59,13 @@ public OneTimeTokenAuthenticationToken(Object principal, Collection<? extends Gr
5659
setAuthenticated(true);
5760
}
5861

62+
@Override
63+
public OneTimeTokenAuthenticationToken withGrantedAuthorities(Collection<GrantedAuthority> authorities) {
64+
Assert.isTrue(isAuthenticated(), "cannot grant authorities to unauthenticated tokens");
65+
Object principal = Objects.requireNonNull(this.principal);
66+
return OneTimeTokenAuthenticationToken.authenticated(principal, authorities);
67+
}
68+
5969
/**
6070
* Creates an unauthenticated token
6171
* @param tokenValue the one-time token value
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2004-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.core;
18+
19+
import java.io.Serial;
20+
import java.util.Collection;
21+
import java.util.HashSet;
22+
import java.util.function.Consumer;
23+
24+
import org.jspecify.annotations.NullMarked;
25+
26+
@NullMarked
27+
public interface AuthenticationResult extends Authentication {
28+
29+
@Serial
30+
long serialVersionUID = -525010730472051621L;
31+
32+
default AuthenticationResult withGrantedAuthorities(Consumer<Collection<GrantedAuthority>> consumer) {
33+
Collection<GrantedAuthority> existing = new HashSet<>(getAuthorities());
34+
consumer.accept(existing);
35+
return withGrantedAuthorities(existing);
36+
}
37+
38+
AuthenticationResult withGrantedAuthorities(Collection<GrantedAuthority> authorities);
39+
40+
default boolean isAuthenticated() {
41+
return true;
42+
}
43+
44+
}

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthenticationToken.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.springframework.security.authentication.AbstractAuthenticationToken;
2222
import org.springframework.security.core.Authentication;
23+
import org.springframework.security.core.AuthenticationResult;
2324
import org.springframework.security.core.GrantedAuthority;
2425
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
2526
import org.springframework.security.oauth2.core.user.OAuth2User;
@@ -40,7 +41,7 @@
4041
* @see OAuth2User
4142
* @see OAuth2AuthorizedClient
4243
*/
43-
public class OAuth2AuthenticationToken extends AbstractAuthenticationToken {
44+
public class OAuth2AuthenticationToken extends AbstractAuthenticationToken implements AuthenticationResult {
4445

4546
private static final long serialVersionUID = 620L;
4647

@@ -65,6 +66,11 @@ public OAuth2AuthenticationToken(OAuth2User principal, Collection<? extends Gran
6566
this.setAuthenticated(true);
6667
}
6768

69+
@Override
70+
public OAuth2AuthenticationToken withGrantedAuthorities(Collection<GrantedAuthority> authorities) {
71+
return new OAuth2AuthenticationToken(getPrincipal(), authorities, this.authorizedClientRegistrationId);
72+
}
73+
6874
@Override
6975
public OAuth2User getPrincipal() {
7076
return this.principal;

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthentication.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.LinkedHashMap;
2222
import java.util.Map;
2323

24+
import org.springframework.security.core.AuthenticationResult;
2425
import org.springframework.security.core.GrantedAuthority;
2526
import org.springframework.security.core.Transient;
2627
import org.springframework.security.oauth2.core.OAuth2AccessToken;
@@ -35,7 +36,8 @@
3536
* @since 5.2
3637
*/
3738
@Transient
38-
public class BearerTokenAuthentication extends AbstractOAuth2TokenAuthenticationToken<OAuth2AccessToken> {
39+
public class BearerTokenAuthentication extends AbstractOAuth2TokenAuthenticationToken<OAuth2AccessToken>
40+
implements AuthenticationResult {
3941

4042
private static final long serialVersionUID = 620L;
4143

@@ -56,6 +58,11 @@ public BearerTokenAuthentication(OAuth2AuthenticatedPrincipal principal, OAuth2A
5658
setAuthenticated(true);
5759
}
5860

61+
@Override
62+
public BearerTokenAuthentication withGrantedAuthorities(Collection<GrantedAuthority> authorities) {
63+
return new BearerTokenAuthentication((OAuth2AuthenticatedPrincipal) getPrincipal(), getToken(), authorities);
64+
}
65+
5966
@Override
6067
public Map<String, Object> getTokenAttributes() {
6168
return this.attributes;

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationToken.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.Collection;
2020
import java.util.Map;
2121

22+
import org.springframework.security.core.AuthenticationResult;
2223
import org.springframework.security.core.GrantedAuthority;
2324
import org.springframework.security.core.Transient;
2425
import org.springframework.security.oauth2.jwt.Jwt;
@@ -33,7 +34,8 @@
3334
* @see Jwt
3435
*/
3536
@Transient
36-
public class JwtAuthenticationToken extends AbstractOAuth2TokenAuthenticationToken<Jwt> {
37+
public class JwtAuthenticationToken extends AbstractOAuth2TokenAuthenticationToken<Jwt>
38+
implements AuthenticationResult {
3739

3840
private static final long serialVersionUID = 620L;
3941

@@ -71,6 +73,11 @@ public JwtAuthenticationToken(Jwt jwt, Collection<? extends GrantedAuthority> au
7173
this.name = name;
7274
}
7375

76+
@Override
77+
public JwtAuthenticationToken withGrantedAuthorities(Collection<GrantedAuthority> authorities) {
78+
return new JwtAuthenticationToken(getToken(), authorities, this.name);
79+
}
80+
7481
@Override
7582
public Map<String, Object> getTokenAttributes() {
7683
return this.getToken().getClaims();

0 commit comments

Comments
 (0)