Skip to content

Commit c71940c

Browse files
committed
Add OpenID Connect 1.0 Logout Endpoint
Closes gh-266
1 parent 5251eea commit c71940c

File tree

62 files changed

+1742
-84
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1742
-84
lines changed

docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/entity/client/Client.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2022 the original author or authors.
2+
* Copyright 2020-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -39,6 +39,8 @@ public class Client {
3939
@Column(length = 1000)
4040
private String redirectUris;
4141
@Column(length = 1000)
42+
private String postLogoutRedirectUris;
43+
@Column(length = 1000)
4244
private String scopes;
4345
@Column(length = 2000)
4446
private String clientSettings;
@@ -118,6 +120,14 @@ public void setRedirectUris(String redirectUris) {
118120
this.redirectUris = redirectUris;
119121
}
120122

123+
public String getPostLogoutRedirectUris() {
124+
return this.postLogoutRedirectUris;
125+
}
126+
127+
public void setPostLogoutRedirectUris(String postLogoutRedirectUris) {
128+
this.postLogoutRedirectUris = postLogoutRedirectUris;
129+
}
130+
121131
public String getScopes() {
122132
return scopes;
123133
}

docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/repository/authorization/AuthorizationRepository.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2022 the original author or authors.
2+
* Copyright 2022-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,10 +30,12 @@ public interface AuthorizationRepository extends JpaRepository<Authorization, St
3030
Optional<Authorization> findByAuthorizationCodeValue(String authorizationCode);
3131
Optional<Authorization> findByAccessTokenValue(String accessToken);
3232
Optional<Authorization> findByRefreshTokenValue(String refreshToken);
33+
Optional<Authorization> findByOidcIdTokenValue(String idToken);
3334
@Query("select a from Authorization a where a.state = :token" +
3435
" or a.authorizationCodeValue = :token" +
3536
" or a.accessTokenValue = :token" +
36-
" or a.refreshTokenValue = :token"
37+
" or a.refreshTokenValue = :token" +
38+
" or a.oidcIdTokenValue = :token"
3739
)
38-
Optional<Authorization> findByStateOrAuthorizationCodeValueOrAccessTokenValueOrRefreshTokenValue(@Param("token") String token);
40+
Optional<Authorization> findByStateOrAuthorizationCodeValueOrAccessTokenValueOrRefreshTokenValueOrOidcIdTokenValue(@Param("token") String token);
3941
}

docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/service/authorization/JpaOAuth2AuthorizationService.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2022 the original author or authors.
2+
* Copyright 2022-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@
3535
import org.springframework.security.oauth2.core.OAuth2Token;
3636
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
3737
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
38+
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
3839
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
3940
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;
4041
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
@@ -88,7 +89,7 @@ public OAuth2Authorization findByToken(String token, OAuth2TokenType tokenType)
8889

8990
Optional<Authorization> result;
9091
if (tokenType == null) {
91-
result = this.authorizationRepository.findByStateOrAuthorizationCodeValueOrAccessTokenValueOrRefreshTokenValue(token);
92+
result = this.authorizationRepository.findByStateOrAuthorizationCodeValueOrAccessTokenValueOrRefreshTokenValueOrOidcIdTokenValue(token);
9293
} else if (OAuth2ParameterNames.STATE.equals(tokenType.getValue())) {
9394
result = this.authorizationRepository.findByState(token);
9495
} else if (OAuth2ParameterNames.CODE.equals(tokenType.getValue())) {
@@ -97,6 +98,8 @@ public OAuth2Authorization findByToken(String token, OAuth2TokenType tokenType)
9798
result = this.authorizationRepository.findByAccessTokenValue(token);
9899
} else if (OAuth2ParameterNames.REFRESH_TOKEN.equals(tokenType.getValue())) {
99100
result = this.authorizationRepository.findByRefreshTokenValue(token);
101+
} else if (OidcParameterNames.ID_TOKEN.equals(tokenType.getValue())) {
102+
result = this.authorizationRepository.findByOidcIdTokenValue(token);
100103
} else {
101104
result = Optional.empty();
102105
}

docs/src/docs/asciidoc/examples/src/main/java/sample/jpa/service/client/JpaRegisteredClientRepository.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2022 the original author or authors.
2+
* Copyright 2022-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -78,6 +78,8 @@ private RegisteredClient toObject(Client client) {
7878
client.getAuthorizationGrantTypes());
7979
Set<String> redirectUris = StringUtils.commaDelimitedListToSet(
8080
client.getRedirectUris());
81+
Set<String> postLogoutRedirectUris = StringUtils.commaDelimitedListToSet(
82+
client.getPostLogoutRedirectUris());
8183
Set<String> clientScopes = StringUtils.commaDelimitedListToSet(
8284
client.getScopes());
8385

@@ -94,6 +96,7 @@ private RegisteredClient toObject(Client client) {
9496
authorizationGrantTypes.forEach(grantType ->
9597
grantTypes.add(resolveAuthorizationGrantType(grantType))))
9698
.redirectUris((uris) -> uris.addAll(redirectUris))
99+
.postLogoutRedirectUris((uris) -> uris.addAll(postLogoutRedirectUris))
97100
.scopes((scopes) -> scopes.addAll(clientScopes));
98101

99102
Map<String, Object> clientSettingsMap = parseMap(client.getClientSettings());
@@ -124,6 +127,7 @@ private Client toEntity(RegisteredClient registeredClient) {
124127
entity.setClientAuthenticationMethods(StringUtils.collectionToCommaDelimitedString(clientAuthenticationMethods));
125128
entity.setAuthorizationGrantTypes(StringUtils.collectionToCommaDelimitedString(authorizationGrantTypes));
126129
entity.setRedirectUris(StringUtils.collectionToCommaDelimitedString(registeredClient.getRedirectUris()));
130+
entity.setPostLogoutRedirectUris(StringUtils.collectionToCommaDelimitedString(registeredClient.getPostLogoutRedirectUris()));
127131
entity.setScopes(StringUtils.collectionToCommaDelimitedString(registeredClient.getScopes()));
128132
entity.setClientSettings(writeMap(registeredClient.getClientSettings().getSettings()));
129133
entity.setTokenSettings(writeMap(registeredClient.getTokenSettings().getSettings()));

docs/src/docs/asciidoc/examples/src/test/java/sample/gettingStarted/SecurityConfigTests.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2022 the original author or authors.
2+
* Copyright 2020-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -102,7 +102,8 @@ public void oidcLoginWhenGettingStartedConfigUsedThenSuccess() throws Exception
102102
assertThatAuthorization(refreshToken, null).isNotNull();
103103

104104
String idToken = (String) tokenResponse.get(OidcParameterNames.ID_TOKEN);
105-
assertThatAuthorization(idToken, OidcParameterNames.ID_TOKEN).isNull(); // id_token is not searchable
105+
assertThatAuthorization(idToken, OidcParameterNames.ID_TOKEN).isNotNull();
106+
assertThatAuthorization(idToken, null).isNotNull();
106107

107108
OAuth2Authorization authorization = findAuthorization(accessToken, OAuth2ParameterNames.ACCESS_TOKEN);
108109
assertThat(authorization.getToken(idToken)).isNotNull();

docs/src/docs/asciidoc/examples/src/test/java/sample/jpa/JpaTests.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2022 the original author or authors.
2+
* Copyright 2020-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -117,7 +117,8 @@ public void oidcLoginWhenJpaCoreServicesAutowiredThenUsed() throws Exception {
117117
assertThatAuthorization(refreshToken, null).isNotNull();
118118

119119
String idToken = (String) tokenResponse.get(OidcParameterNames.ID_TOKEN);
120-
assertThatAuthorization(idToken, OidcParameterNames.ID_TOKEN).isNull(); // id_token is not searchable
120+
assertThatAuthorization(idToken, OidcParameterNames.ID_TOKEN).isNotNull();
121+
assertThatAuthorization(idToken, null).isNotNull();
121122

122123
OAuth2Authorization authorization = findAuthorization(accessToken, OAuth2ParameterNames.ACCESS_TOKEN);
123124
assertThat(authorization.getToken(idToken)).isNotNull();

docs/src/docs/asciidoc/examples/src/test/java/sample/util/RegisteredClients.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2022 the original author or authors.
2+
* Copyright 2020-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -37,6 +37,7 @@ public static RegisteredClient messagingClient() {
3737
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
3838
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
3939
.redirectUri("http://127.0.0.1:8080/authorized")
40+
.postLogoutRedirectUri("http://127.0.0.1:8080/index")
4041
.scope(OidcScopes.OPENID)
4142
.scope("message.read")
4243
.scope("message.write")

docs/src/docs/asciidoc/guides/how-to-jpa.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ CREATE TABLE client (
4545
clientAuthenticationMethods varchar(1000) NOT NULL,
4646
authorizationGrantTypes varchar(1000) NOT NULL,
4747
redirectUris varchar(1000) DEFAULT NULL,
48+
postLogoutRedirectUris varchar(1000) DEFAULT NULL,
4849
scopes varchar(1000) NOT NULL,
4950
clientSettings varchar(2000) NOT NULL,
5051
tokenSettings varchar(2000) NOT NULL,

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2022 the original author or authors.
2+
* Copyright 2020-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,6 +26,8 @@
2626
import org.springframework.security.oauth2.core.OAuth2AccessToken;
2727
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
2828
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
29+
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
30+
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
2931
import org.springframework.util.Assert;
3032

3133
/**
@@ -150,13 +152,16 @@ private static boolean hasToken(OAuth2Authorization authorization, String token,
150152
return matchesState(authorization, token) ||
151153
matchesAuthorizationCode(authorization, token) ||
152154
matchesAccessToken(authorization, token) ||
155+
matchesIdToken(authorization, token) ||
153156
matchesRefreshToken(authorization, token);
154157
} else if (OAuth2ParameterNames.STATE.equals(tokenType.getValue())) {
155158
return matchesState(authorization, token);
156159
} else if (OAuth2ParameterNames.CODE.equals(tokenType.getValue())) {
157160
return matchesAuthorizationCode(authorization, token);
158161
} else if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenType)) {
159162
return matchesAccessToken(authorization, token);
163+
} else if (OidcParameterNames.ID_TOKEN.equals(tokenType.getValue())) {
164+
return matchesIdToken(authorization, token);
160165
} else if (OAuth2TokenType.REFRESH_TOKEN.equals(tokenType)) {
161166
return matchesRefreshToken(authorization, token);
162167
}
@@ -185,6 +190,12 @@ private static boolean matchesRefreshToken(OAuth2Authorization authorization, St
185190
return refreshToken != null && refreshToken.getToken().getTokenValue().equals(token);
186191
}
187192

193+
private static boolean matchesIdToken(OAuth2Authorization authorization, String token) {
194+
OAuth2Authorization.Token<OidcIdToken> idToken =
195+
authorization.getToken(OidcIdToken.class);
196+
return idToken != null && idToken.getToken().getTokenValue().equals(token);
197+
}
198+
188199
private static final class MaxSizeHashMap<K, V> extends LinkedHashMap<K, V> {
189200
private final int maxSize;
190201

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2022 the original author or authors.
2+
* Copyright 2020-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -53,6 +53,7 @@
5353
import org.springframework.security.oauth2.core.OAuth2Token;
5454
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
5555
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
56+
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
5657
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
5758
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
5859
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
@@ -112,11 +113,12 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
112113

113114
private static final String PK_FILTER = "id = ?";
114115
private static final String UNKNOWN_TOKEN_TYPE_FILTER = "state = ? OR authorization_code_value = ? OR " +
115-
"access_token_value = ? OR refresh_token_value = ?";
116+
"access_token_value = ? OR oidc_id_token_value = ? OR refresh_token_value = ?";
116117

117118
private static final String STATE_FILTER = "state = ?";
118119
private static final String AUTHORIZATION_CODE_FILTER = "authorization_code_value = ?";
119120
private static final String ACCESS_TOKEN_FILTER = "access_token_value = ?";
121+
private static final String ID_TOKEN_FILTER = "oidc_id_token_value = ?";
120122
private static final String REFRESH_TOKEN_FILTER = "refresh_token_value = ?";
121123

122124
// @formatter:off
@@ -240,6 +242,7 @@ public OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType t
240242
parameters.add(new SqlParameterValue(Types.VARCHAR, token));
241243
parameters.add(mapToSqlParameter("authorization_code_value", token));
242244
parameters.add(mapToSqlParameter("access_token_value", token));
245+
parameters.add(mapToSqlParameter("oidc_id_token_value", token));
243246
parameters.add(mapToSqlParameter("refresh_token_value", token));
244247
return findBy(UNKNOWN_TOKEN_TYPE_FILTER, parameters);
245248
} else if (OAuth2ParameterNames.STATE.equals(tokenType.getValue())) {
@@ -251,6 +254,9 @@ public OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType t
251254
} else if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenType)) {
252255
parameters.add(mapToSqlParameter("access_token_value", token));
253256
return findBy(ACCESS_TOKEN_FILTER, parameters);
257+
} else if (OidcParameterNames.ID_TOKEN.equals(tokenType.getValue())) {
258+
parameters.add(mapToSqlParameter("oidc_id_token_value", token));
259+
return findBy(ID_TOKEN_FILTER, parameters);
254260
} else if (OAuth2TokenType.REFRESH_TOKEN.equals(tokenType)) {
255261
parameters.add(mapToSqlParameter("refresh_token_value", token));
256262
return findBy(REFRESH_TOKEN_FILTER, parameters);

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2022 the original author or authors.
2+
* Copyright 2020-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -77,6 +77,7 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
7777
+ "client_authentication_methods, "
7878
+ "authorization_grant_types, "
7979
+ "redirect_uris, "
80+
+ "post_logout_redirect_uris, "
8081
+ "scopes, "
8182
+ "client_settings,"
8283
+ "token_settings";
@@ -90,13 +91,13 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
9091

9192
// @formatter:off
9293
private static final String INSERT_REGISTERED_CLIENT_SQL = "INSERT INTO " + TABLE_NAME
93-
+ "(" + COLUMN_NAMES + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
94+
+ "(" + COLUMN_NAMES + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
9495
// @formatter:on
9596

9697
// @formatter:off
9798
private static final String UPDATE_REGISTERED_CLIENT_SQL = "UPDATE " + TABLE_NAME
9899
+ " SET client_name = ?, client_authentication_methods = ?, authorization_grant_types = ?,"
99-
+ " redirect_uris = ?, scopes = ?, client_settings = ?, token_settings = ?"
100+
+ " redirect_uris = ?, post_logout_redirect_uris = ?, scopes = ?, client_settings = ?, token_settings = ?"
100101
+ " WHERE " + PK_FILTER;
101102
// @formatter:on
102103

@@ -241,6 +242,7 @@ public RegisteredClient mapRow(ResultSet rs, int rowNum) throws SQLException {
241242
Set<String> clientAuthenticationMethods = StringUtils.commaDelimitedListToSet(rs.getString("client_authentication_methods"));
242243
Set<String> authorizationGrantTypes = StringUtils.commaDelimitedListToSet(rs.getString("authorization_grant_types"));
243244
Set<String> redirectUris = StringUtils.commaDelimitedListToSet(rs.getString("redirect_uris"));
245+
Set<String> postLogoutRedirectUris = StringUtils.commaDelimitedListToSet(rs.getString("post_logout_redirect_uris"));
244246
Set<String> clientScopes = StringUtils.commaDelimitedListToSet(rs.getString("scopes"));
245247

246248
// @formatter:off
@@ -257,6 +259,7 @@ public RegisteredClient mapRow(ResultSet rs, int rowNum) throws SQLException {
257259
authorizationGrantTypes.forEach(grantType ->
258260
grantTypes.add(resolveAuthorizationGrantType(grantType))))
259261
.redirectUris((uris) -> uris.addAll(redirectUris))
262+
.postLogoutRedirectUris((uris) -> uris.addAll(postLogoutRedirectUris))
260263
.scopes((scopes) -> scopes.addAll(clientScopes));
261264
// @formatter:on
262265

@@ -354,6 +357,7 @@ public List<SqlParameterValue> apply(RegisteredClient registeredClient) {
354357
new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(clientAuthenticationMethods)),
355358
new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(authorizationGrantTypes)),
356359
new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(registeredClient.getRedirectUris())),
360+
new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(registeredClient.getPostLogoutRedirectUris())),
357361
new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(registeredClient.getScopes())),
358362
new SqlParameterValue(Types.VARCHAR, writeMap(registeredClient.getClientSettings().getSettings())),
359363
new SqlParameterValue(Types.VARCHAR, writeMap(registeredClient.getTokenSettings().getSettings())));

0 commit comments

Comments
 (0)