Add OpenID Connect 1.0 Logout Endpoint

Closes gh-266
This commit is contained in:
Joe Grandja
2023-01-30 10:47:36 -05:00
parent 7c6516bbbb
commit 98e3fe807a
66 changed files with 2718 additions and 84 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,6 +39,8 @@ public class Client {
@Column(length = 1000)
private String redirectUris;
@Column(length = 1000)
private String postLogoutRedirectUris;
@Column(length = 1000)
private String scopes;
@Column(length = 2000)
private String clientSettings;
@@ -118,6 +120,14 @@ public class Client {
this.redirectUris = redirectUris;
}
public String getPostLogoutRedirectUris() {
return this.postLogoutRedirectUris;
}
public void setPostLogoutRedirectUris(String postLogoutRedirectUris) {
this.postLogoutRedirectUris = postLogoutRedirectUris;
}
public String getScopes() {
return scopes;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022 the original author or authors.
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,10 +30,12 @@ public interface AuthorizationRepository extends JpaRepository<Authorization, St
Optional<Authorization> findByAuthorizationCodeValue(String authorizationCode);
Optional<Authorization> findByAccessTokenValue(String accessToken);
Optional<Authorization> findByRefreshTokenValue(String refreshToken);
Optional<Authorization> findByOidcIdTokenValue(String idToken);
@Query("select a from Authorization a where a.state = :token" +
" or a.authorizationCodeValue = :token" +
" or a.accessTokenValue = :token" +
" or a.refreshTokenValue = :token"
" or a.refreshTokenValue = :token" +
" or a.oidcIdTokenValue = :token"
)
Optional<Authorization> findByStateOrAuthorizationCodeValueOrAccessTokenValueOrRefreshTokenValue(@Param("token") String token);
Optional<Authorization> findByStateOrAuthorizationCodeValueOrAccessTokenValueOrRefreshTokenValueOrOidcIdTokenValue(@Param("token") String token);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022 the original author or authors.
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@ import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.OAuth2Token;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
@@ -88,7 +89,7 @@ public class JpaOAuth2AuthorizationService implements OAuth2AuthorizationService
Optional<Authorization> result;
if (tokenType == null) {
result = this.authorizationRepository.findByStateOrAuthorizationCodeValueOrAccessTokenValueOrRefreshTokenValue(token);
result = this.authorizationRepository.findByStateOrAuthorizationCodeValueOrAccessTokenValueOrRefreshTokenValueOrOidcIdTokenValue(token);
} else if (OAuth2ParameterNames.STATE.equals(tokenType.getValue())) {
result = this.authorizationRepository.findByState(token);
} else if (OAuth2ParameterNames.CODE.equals(tokenType.getValue())) {
@@ -97,6 +98,8 @@ public class JpaOAuth2AuthorizationService implements OAuth2AuthorizationService
result = this.authorizationRepository.findByAccessTokenValue(token);
} else if (OAuth2ParameterNames.REFRESH_TOKEN.equals(tokenType.getValue())) {
result = this.authorizationRepository.findByRefreshTokenValue(token);
} else if (OidcParameterNames.ID_TOKEN.equals(tokenType.getValue())) {
result = this.authorizationRepository.findByOidcIdTokenValue(token);
} else {
result = Optional.empty();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022 the original author or authors.
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -78,6 +78,8 @@ public class JpaRegisteredClientRepository implements RegisteredClientRepository
client.getAuthorizationGrantTypes());
Set<String> redirectUris = StringUtils.commaDelimitedListToSet(
client.getRedirectUris());
Set<String> postLogoutRedirectUris = StringUtils.commaDelimitedListToSet(
client.getPostLogoutRedirectUris());
Set<String> clientScopes = StringUtils.commaDelimitedListToSet(
client.getScopes());
@@ -94,6 +96,7 @@ public class JpaRegisteredClientRepository implements RegisteredClientRepository
authorizationGrantTypes.forEach(grantType ->
grantTypes.add(resolveAuthorizationGrantType(grantType))))
.redirectUris((uris) -> uris.addAll(redirectUris))
.postLogoutRedirectUris((uris) -> uris.addAll(postLogoutRedirectUris))
.scopes((scopes) -> scopes.addAll(clientScopes));
Map<String, Object> clientSettingsMap = parseMap(client.getClientSettings());
@@ -124,6 +127,7 @@ public class JpaRegisteredClientRepository implements RegisteredClientRepository
entity.setClientAuthenticationMethods(StringUtils.collectionToCommaDelimitedString(clientAuthenticationMethods));
entity.setAuthorizationGrantTypes(StringUtils.collectionToCommaDelimitedString(authorizationGrantTypes));
entity.setRedirectUris(StringUtils.collectionToCommaDelimitedString(registeredClient.getRedirectUris()));
entity.setPostLogoutRedirectUris(StringUtils.collectionToCommaDelimitedString(registeredClient.getPostLogoutRedirectUris()));
entity.setScopes(StringUtils.collectionToCommaDelimitedString(registeredClient.getScopes()));
entity.setClientSettings(writeMap(registeredClient.getClientSettings().getSettings()));
entity.setTokenSettings(writeMap(registeredClient.getTokenSettings().getSettings()));

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -102,7 +102,8 @@ public class SecurityConfigTests {
assertThatAuthorization(refreshToken, null).isNotNull();
String idToken = (String) tokenResponse.get(OidcParameterNames.ID_TOKEN);
assertThatAuthorization(idToken, OidcParameterNames.ID_TOKEN).isNull(); // id_token is not searchable
assertThatAuthorization(idToken, OidcParameterNames.ID_TOKEN).isNotNull();
assertThatAuthorization(idToken, null).isNotNull();
OAuth2Authorization authorization = findAuthorization(accessToken, OAuth2ParameterNames.ACCESS_TOKEN);
assertThat(authorization.getToken(idToken)).isNotNull();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -117,7 +117,8 @@ public class JpaTests {
assertThatAuthorization(refreshToken, null).isNotNull();
String idToken = (String) tokenResponse.get(OidcParameterNames.ID_TOKEN);
assertThatAuthorization(idToken, OidcParameterNames.ID_TOKEN).isNull(); // id_token is not searchable
assertThatAuthorization(idToken, OidcParameterNames.ID_TOKEN).isNotNull();
assertThatAuthorization(idToken, null).isNotNull();
OAuth2Authorization authorization = findAuthorization(accessToken, OAuth2ParameterNames.ACCESS_TOKEN);
assertThat(authorization.getToken(idToken)).isNotNull();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,6 +37,7 @@ public class RegisteredClients {
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.redirectUri("http://127.0.0.1:8080/authorized")
.postLogoutRedirectUri("http://127.0.0.1:8080/index")
.scope(OidcScopes.OPENID)
.scope("message.read")
.scope("message.write")

View File

@@ -45,6 +45,7 @@ CREATE TABLE client (
clientAuthenticationMethods varchar(1000) NOT NULL,
authorizationGrantTypes varchar(1000) NOT NULL,
redirectUris varchar(1000) DEFAULT NULL,
postLogoutRedirectUris varchar(1000) DEFAULT NULL,
scopes varchar(1000) NOT NULL,
clientSettings varchar(2000) NOT NULL,
tokenSettings varchar(2000) NOT NULL,