Skip to content

Commit 1657a7e

Browse files
committed
Add user consent page
Closes spring-projectsgh-42
1 parent d15afd7 commit 1657a7e

File tree

16 files changed

+1000
-162
lines changed

16 files changed

+1000
-162
lines changed

oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerSecurity.java

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,8 @@
1717

1818
import org.springframework.core.Ordered;
1919
import org.springframework.core.annotation.Order;
20-
import org.springframework.http.HttpMethod;
2120
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
2221
import org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer;
23-
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter;
24-
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
2522
import org.springframework.security.web.util.matcher.OrRequestMatcher;
2623
import org.springframework.security.web.util.matcher.RequestMatcher;
2724

@@ -41,22 +38,17 @@ public class OAuth2AuthorizationServerSecurity extends WebSecurityConfigurerAdap
4138
protected void configure(HttpSecurity http) throws Exception {
4239
OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =
4340
new OAuth2AuthorizationServerConfigurer<>();
41+
RequestMatcher[] endpointMatchers = authorizationServerConfigurer
42+
.getEndpointMatchers().toArray(new RequestMatcher[0]);
4443

4544
http
46-
.requestMatcher(new OrRequestMatcher(authorizationServerConfigurer.getEndpointMatchers()))
45+
.requestMatcher(new OrRequestMatcher(endpointMatchers))
4746
.authorizeRequests(authorizeRequests ->
48-
authorizeRequests
49-
.anyRequest().authenticated()
47+
authorizeRequests.anyRequest().authenticated()
5048
)
5149
.formLogin(withDefaults())
52-
.csrf(csrf -> csrf.ignoringRequestMatchers(tokenEndpointMatcher()))
50+
.csrf(csrf -> csrf.ignoringRequestMatchers(endpointMatchers))
5351
.apply(authorizationServerConfigurer);
5452
}
5553
// @formatter:on
56-
57-
private static RequestMatcher tokenEndpointMatcher() {
58-
return new AntPathRequestMatcher(
59-
OAuth2TokenEndpointFilter.DEFAULT_TOKEN_ENDPOINT_URI,
60-
HttpMethod.POST.name());
61-
}
6254
}

oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
4141
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
4242
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
43+
import org.springframework.security.web.util.matcher.OrRequestMatcher;
4344
import org.springframework.security.web.util.matcher.RequestMatcher;
4445
import org.springframework.util.Assert;
4546
import org.springframework.util.StringUtils;
@@ -63,8 +64,13 @@
6364
public final class OAuth2AuthorizationServerConfigurer<B extends HttpSecurityBuilder<B>>
6465
extends AbstractHttpConfigurer<OAuth2AuthorizationServerConfigurer<B>, B> {
6566

66-
private final RequestMatcher authorizationEndpointMatcher = new AntPathRequestMatcher(
67-
OAuth2AuthorizationEndpointFilter.DEFAULT_AUTHORIZATION_ENDPOINT_URI, HttpMethod.GET.name());
67+
private final RequestMatcher authorizationEndpointMatcher = new OrRequestMatcher(
68+
new AntPathRequestMatcher(
69+
OAuth2AuthorizationEndpointFilter.DEFAULT_AUTHORIZATION_ENDPOINT_URI,
70+
HttpMethod.GET.name()),
71+
new AntPathRequestMatcher(
72+
OAuth2AuthorizationEndpointFilter.DEFAULT_AUTHORIZATION_ENDPOINT_URI,
73+
HttpMethod.POST.name()));
6874
private final RequestMatcher tokenEndpointMatcher = new AntPathRequestMatcher(
6975
OAuth2TokenEndpointFilter.DEFAULT_TOKEN_ENDPOINT_URI, HttpMethod.POST.name());
7076
private final RequestMatcher jwkSetEndpointMatcher = new AntPathRequestMatcher(

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationService.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ public void save(OAuth2Authorization authorization) {
4343
this.authorizations.put(authorizationId, authorization);
4444
}
4545

46+
@Override
47+
public void remove(OAuth2Authorization authorization) {
48+
Assert.notNull(authorization, "authorization cannot be null");
49+
OAuth2AuthorizationId authorizationId = new OAuth2AuthorizationId(
50+
authorization.getRegisteredClientId(), authorization.getPrincipalName());
51+
this.authorizations.remove(authorizationId, authorization);
52+
}
53+
4654
@Override
4755
public OAuth2Authorization findByToken(String token, @Nullable TokenType tokenType) {
4856
Assert.hasText(token, "token cannot be empty");
@@ -53,7 +61,9 @@ public OAuth2Authorization findByToken(String token, @Nullable TokenType tokenTy
5361
}
5462

5563
private boolean hasToken(OAuth2Authorization authorization, String token, TokenType tokenType) {
56-
if (TokenType.AUTHORIZATION_CODE.equals(tokenType)) {
64+
if (OAuth2AuthorizationAttributeNames.STATE.equals(tokenType.getValue())) {
65+
return token.equals(authorization.getAttribute(OAuth2AuthorizationAttributeNames.STATE));
66+
} else if (TokenType.AUTHORIZATION_CODE.equals(tokenType)) {
5767
return token.equals(authorization.getAttribute(OAuth2AuthorizationAttributeNames.CODE));
5868
} else if (TokenType.ACCESS_TOKEN.equals(tokenType)) {
5969
return authorization.getAccessToken() != null &&

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationAttributeNames.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@
3030
*/
3131
public interface OAuth2AuthorizationAttributeNames {
3232

33+
/**
34+
* The name of the attribute used for correlating the user consent request/response.
35+
*/
36+
String STATE = OAuth2Authorization.class.getName().concat(".STATE");
37+
3338
/**
3439
* The name of the attribute used for the {@link OAuth2ParameterNames#CODE} parameter.
3540
*/
@@ -40,6 +45,11 @@ public interface OAuth2AuthorizationAttributeNames {
4045
*/
4146
String AUTHORIZATION_REQUEST = OAuth2Authorization.class.getName().concat(".AUTHORIZATION_REQUEST");
4247

48+
/**
49+
* The name of the attribute used for the authorized scope(s).
50+
*/
51+
String AUTHORIZED_SCOPES = OAuth2Authorization.class.getName().concat(".AUTHORIZED_SCOPES");
52+
4353
/**
4454
* The name of the attribute used for the attributes/claims of the {@link OAuth2AccessToken}.
4555
*/

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationService.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ public interface OAuth2AuthorizationService {
3434
*/
3535
void save(OAuth2Authorization authorization);
3636

37+
/**
38+
* Removes the {@link OAuth2Authorization}.
39+
*
40+
* @param authorization the {@link OAuth2Authorization}
41+
*/
42+
void remove(OAuth2Authorization authorization);
43+
3744
/**
3845
* Returns the {@link OAuth2Authorization} containing the provided {@code token},
3946
* or {@code null} if not found.

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import java.time.Instant;
4545
import java.time.temporal.ChronoUnit;
4646
import java.util.Collections;
47+
import java.util.Set;
4748

4849
/**
4950
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Authorization Code Grant.
@@ -123,6 +124,7 @@ public Authentication authenticate(Authentication authentication) throws Authent
123124

124125
Instant issuedAt = Instant.now();
125126
Instant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS); // TODO Allow configuration for access token time-to-live
127+
Set<String> authorizedScopes = authorization.getAttribute(OAuth2AuthorizationAttributeNames.AUTHORIZED_SCOPES);
126128

127129
JwtClaimsSet jwtClaimsSet = JwtClaimsSet.withClaims()
128130
.issuer(issuer)
@@ -131,7 +133,7 @@ public Authentication authenticate(Authentication authentication) throws Authent
131133
.issuedAt(issuedAt)
132134
.expiresAt(expiresAt)
133135
.notBefore(issuedAt)
134-
.claim(OAuth2ParameterNames.SCOPE, authorizationRequest.getScopes())
136+
.claim(OAuth2ParameterNames.SCOPE, authorizedScopes)
135137
.build();
136138

137139
Jwt jwt = this.jwtEncoder.encode(joseHeader, jwtClaimsSet);

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/ClientSettings.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
public class ClientSettings extends Settings {
2929
private static final String CLIENT_SETTING_BASE = "spring.security.oauth2.authorization-server.client.";
3030
public static final String REQUIRE_PROOF_KEY = CLIENT_SETTING_BASE.concat("require-proof-key");
31+
public static final String REQUIRE_USER_CONSENT = CLIENT_SETTING_BASE.concat("require-user-consent");
3132

3233
/**
3334
* Constructs a {@code ClientSettings}.
@@ -67,9 +68,32 @@ public ClientSettings requireProofKey(boolean requireProofKey) {
6768
return this;
6869
}
6970

71+
/**
72+
* Returns {@code true} if the user's consent is required when the client requests access.
73+
* The default is {@code false}.
74+
*
75+
* @return {@code true} if the user's consent is required when the client requests access, {@code false} otherwise
76+
*/
77+
public boolean requireUserConsent() {
78+
return setting(REQUIRE_USER_CONSENT);
79+
}
80+
81+
/**
82+
* Set to {@code true} if the user's consent is required when the client requests access.
83+
* This applies to all interactive flows (e.g. {@code authorization_code} and {@code device_code}).
84+
*
85+
* @param requireUserConsent {@code true} if the user's consent is required when the client requests access, {@code false} otherwise
86+
* @return the {@link ClientSettings}
87+
*/
88+
public ClientSettings requireUserConsent(boolean requireUserConsent) {
89+
setting(REQUIRE_USER_CONSENT, requireUserConsent);
90+
return this;
91+
}
92+
7093
protected static Map<String, Object> defaultSettings() {
7194
Map<String, Object> settings = new HashMap<>();
7295
settings.put(REQUIRE_PROOF_KEY, false);
96+
settings.put(REQUIRE_USER_CONSENT, false);
7397
return settings;
7498
}
7599
}

0 commit comments

Comments
 (0)