Move and rename OAuth2IntrospectionClaimAccessor/Names

Introduced OAuth2TokenIntrospectionClaimAccessor and OAuth2TokenIntrospectionClaimNames
with copied implementation from OAuth2IntrospectionClaimAccessor/Names.
OAuth2IntrospectionClaimAccessor and OAuth2IntrospectionClaimNames are
now deprecated.

Also method getScopes() returning list of scopes was introduced
and getScope() is now deprecated.

Closes gh-9647
This commit is contained in:
Dávid Kováč 2021-06-12 01:33:00 +02:00 committed by Josh Cummings
parent b83a4c2985
commit 3ff825576b
19 changed files with 592 additions and 256 deletions

View File

@ -27,6 +27,7 @@
<suppress files="OidcParameterNames\.java" checks="InterfaceIsType"/>
<suppress files="BearerTokenErrorCodes\.java" checks="InterfaceIsType"/>
<suppress files="OAuth2IntrospectionClaimNames\.java" checks="InterfaceIsType"/>
<suppress files="OAuth2TokenIntrospectionClaimNames\.java" checks="InterfaceIsType"/>
<suppress files="Saml2ErrorCodes\.java" checks="InterfaceIsType"/>
<!-- Method Visibility that we can't reduce -->

View File

@ -0,0 +1,150 @@
/*
* Copyright 2002-2021 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.core;
import java.net.URL;
import java.time.Instant;
import java.util.List;
import org.springframework.lang.Nullable;
/**
* A {@link ClaimAccessor} for the &quot;claims&quot; that may be contained in the
* Introspection Response.
*
* @author David Kovac
* @since 5.6
* @see ClaimAccessor
* @see OAuth2TokenIntrospectionClaimNames
* @see <a target="_blank" href=
* "https://tools.ietf.org/html/rfc7662#section-2.2">Introspection Response</a>
*/
public interface OAuth2TokenIntrospectionClaimAccessor extends ClaimAccessor {
/**
* Returns the indicator {@code (active)} whether or not the token is currently active
* @return the indicator whether or not the token is currently active
*/
default boolean isActive() {
return Boolean.TRUE.equals(getClaimAsBoolean(OAuth2TokenIntrospectionClaimNames.ACTIVE));
}
/**
* Returns a human-readable identifier {@code (username)} for the resource owner that
* authorized the token
* @return a human-readable identifier for the resource owner that authorized the
* token
*/
@Nullable
default String getUsername() {
return getClaimAsString(OAuth2TokenIntrospectionClaimNames.USERNAME);
}
/**
* Returns the client identifier {@code (client_id)} for the token
* @return the client identifier for the token
*/
@Nullable
default String getClientId() {
return getClaimAsString(OAuth2TokenIntrospectionClaimNames.CLIENT_ID);
}
/**
* Returns the scopes {@code (scope)} associated with the token
* @return the scopes associated with the token
*/
@Nullable
default List<String> getScopes() {
return getClaimAsStringList(OAuth2TokenIntrospectionClaimNames.SCOPE);
}
/**
* Returns the type of the token {@code (token_type)}, for example {@code bearer}.
* @return the type of the token, for example {@code bearer}.
*/
@Nullable
default String getTokenType() {
return getClaimAsString(OAuth2TokenIntrospectionClaimNames.TOKEN_TYPE);
}
/**
* Returns a timestamp {@code (exp)} indicating when the token expires
* @return a timestamp indicating when the token expires
*/
@Nullable
default Instant getExpiresAt() {
return getClaimAsInstant(OAuth2TokenIntrospectionClaimNames.EXP);
}
/**
* Returns a timestamp {@code (iat)} indicating when the token was issued
* @return a timestamp indicating when the token was issued
*/
@Nullable
default Instant getIssuedAt() {
return getClaimAsInstant(OAuth2TokenIntrospectionClaimNames.IAT);
}
/**
* Returns a timestamp {@code (nbf)} indicating when the token is not to be used
* before
* @return a timestamp indicating when the token is not to be used before
*/
@Nullable
default Instant getNotBefore() {
return getClaimAsInstant(OAuth2TokenIntrospectionClaimNames.NBF);
}
/**
* Returns usually a machine-readable identifier {@code (sub)} of the resource owner
* who authorized the token
* @return usually a machine-readable identifier of the resource owner who authorized
* the token
*/
@Nullable
default String getSubject() {
return getClaimAsString(OAuth2TokenIntrospectionClaimNames.SUB);
}
/**
* Returns the intended audience {@code (aud)} for the token
* @return the intended audience for the token
*/
@Nullable
default List<String> getAudience() {
return getClaimAsStringList(OAuth2TokenIntrospectionClaimNames.AUD);
}
/**
* Returns the issuer {@code (iss)} of the token
* @return the issuer of the token
*/
@Nullable
default URL getIssuer() {
return getClaimAsURL(OAuth2TokenIntrospectionClaimNames.ISS);
}
/**
* Returns the identifier {@code (jti)} for the token
* @return the identifier for the token
*/
@Nullable
default String getId() {
return getClaimAsString(OAuth2TokenIntrospectionClaimNames.JTI);
}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright 2002-2021 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.core;
/**
* The names of the &quot;Introspection Claims&quot; defined by an
* <a target="_blank" href="https://tools.ietf.org/html/rfc7662#section-2.2">Introspection
* Response</a>.
*
* @author Josh Cummings
* @since 5.6
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7662#section-2.2">OAuth
* 2.0 Token Introspection (RFC7662)</a>
* @see <a target="_blank" href=
* "https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml#token-introspection-response">OAuth
* Parameters (IANA)</a>
*/
public interface OAuth2TokenIntrospectionClaimNames {
/**
* {@code active} - Indicator whether or not the token is currently active
*/
String ACTIVE = "active";
/**
* {@code username} - A human-readable identifier for the resource owner that
* authorized the token
*/
String USERNAME = "username";
/**
* {@code client_id} - The Client identifier for the token
*/
String CLIENT_ID = "client_id";
/**
* {@code scope} - The scopes for the token
*/
String SCOPE = "scope";
/**
* {@code token_type} - The type of the token, for example {@code bearer}.
*/
String TOKEN_TYPE = "token_type";
/**
* {@code exp} - A timestamp indicating when the token expires
*/
String EXP = "exp";
/**
* {@code iat} - A timestamp indicating when the token was issued
*/
String IAT = "iat";
/**
* {@code nbf} - A timestamp indicating when the token is not to be used before
*/
String NBF = "nbf";
/**
* {@code sub} - Usually a machine-readable identifier of the resource owner who
* authorized the token
*/
String SUB = "sub";
/**
* {@code aud} - The intended audience for the token
*/
String AUD = "aud";
/**
* {@code iss} - The issuer of the token
*/
String ISS = "iss";
/**
* {@code jti} - The identifier for the token
*/
String JTI = "jti";
}

View File

@ -0,0 +1,197 @@
/*
* Copyright 2002-2021 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.core;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Instant;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link OAuth2TokenIntrospectionClaimAccessor}.
*
* @author David Kovac
*/
public class OAuth2TokenIntrospectionClaimAccessorTests {
private final Map<String, Object> claims = new HashMap<>();
private final OAuth2TokenIntrospectionClaimAccessor claimAccessor = (() -> this.claims);
@BeforeEach
public void setup() {
this.claims.clear();
}
@Test
public void isActiveWhenActiveClaimNotExistingThenReturnFalse() {
assertThat(this.claimAccessor.isActive()).isFalse();
}
@Test
public void isActiveWhenActiveClaimValueIsNullThenReturnFalse() {
this.claims.put(OAuth2TokenIntrospectionClaimNames.ACTIVE, null);
assertThat(this.claimAccessor.isActive()).isFalse();
}
@Test
public void isActiveWhenActiveClaimValueIsTrueThenReturnTrue() {
this.claims.put(OAuth2TokenIntrospectionClaimNames.ACTIVE, "true");
assertThat(this.claimAccessor.isActive()).isTrue();
}
@Test
public void getUsernameWhenUsernameClaimNotExistingThenReturnNull() {
assertThat(this.claimAccessor.getUsername()).isNull();
}
@Test
public void getUsernameWhenUsernameClaimExistingThenReturnUsername() {
String expectedUsernameValue = "username";
this.claims.put(OAuth2TokenIntrospectionClaimNames.USERNAME, expectedUsernameValue);
assertThat(this.claimAccessor.getUsername()).isEqualTo(expectedUsernameValue);
}
@Test
public void getClientIdWhenClientIdClaimNotExistingThenReturnNull() {
assertThat(this.claimAccessor.getUsername()).isNull();
}
@Test
public void getClientIdWhenClientIdClaimExistingThenReturnClientId() {
String expectedClientIdValue = "clientId";
this.claims.put(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, expectedClientIdValue);
assertThat(this.claimAccessor.getClientId()).isEqualTo(expectedClientIdValue);
}
@Test
public void getScopesWhenScopeClaimNotExistingThenReturnNull() {
assertThat(this.claimAccessor.getScopes()).isNull();
}
@Test
public void getScopesWhenScopeClaimExistingThenReturnScope() {
List<String> expectedScopeValue = Arrays.asList("scope1", "scope2");
this.claims.put(OAuth2TokenIntrospectionClaimNames.SCOPE, expectedScopeValue);
assertThat(this.claimAccessor.getScopes()).hasSameElementsAs(expectedScopeValue);
}
@Test
public void getTokenTypeWhenTokenTypeClaimNotExistingThenReturnNull() {
assertThat(this.claimAccessor.getTokenType()).isNull();
}
@Test
public void getTokenTypeWhenTokenTypeClaimExistingThenReturnTokenType() {
String expectedTokenTypeValue = "tokenType";
this.claims.put(OAuth2TokenIntrospectionClaimNames.TOKEN_TYPE, expectedTokenTypeValue);
assertThat(this.claimAccessor.getTokenType()).isEqualTo(expectedTokenTypeValue);
}
@Test
public void getExpiresAtWhenExpiresAtClaimNotExistingThenReturnNull() {
assertThat(this.claimAccessor.getExpiresAt()).isNull();
}
@Test
public void getExpiresAtWhenExpiresAtClaimExistingThenReturnExpiresAt() {
Instant expectedExpiresAtValue = Instant.now();
this.claims.put(OAuth2TokenIntrospectionClaimNames.EXP, expectedExpiresAtValue);
assertThat(this.claimAccessor.getExpiresAt()).isEqualTo(expectedExpiresAtValue);
}
@Test
public void getIssuedAtWhenIssuedAtClaimNotExistingThenReturnNull() {
assertThat(this.claimAccessor.getExpiresAt()).isNull();
}
@Test
public void getIssuedAtWhenIssuedAtClaimExistingThenReturnIssuedAt() {
Instant expectedIssuedAtValue = Instant.now();
this.claims.put(OAuth2TokenIntrospectionClaimNames.IAT, expectedIssuedAtValue);
assertThat(this.claimAccessor.getIssuedAt()).isEqualTo(expectedIssuedAtValue);
}
@Test
public void getNotBeforeWhenNotBeforeClaimNotExistingThenReturnNull() {
assertThat(this.claimAccessor.getNotBefore()).isNull();
}
@Test
public void getNotBeforeWhenNotBeforeClaimExistingThenReturnNotBefore() {
Instant expectedNotBeforeValue = Instant.now();
this.claims.put(OAuth2TokenIntrospectionClaimNames.NBF, expectedNotBeforeValue);
assertThat(this.claimAccessor.getNotBefore()).isEqualTo(expectedNotBeforeValue);
}
@Test
public void getSubjectWhenSubjectClaimNotExistingThenReturnNull() {
assertThat(this.claimAccessor.getSubject()).isNull();
}
@Test
public void getSubjectWhenSubjectClaimExistingThenReturnSubject() {
String expectedSubjectValue = "subject";
this.claims.put(OAuth2TokenIntrospectionClaimNames.SUB, expectedSubjectValue);
assertThat(this.claimAccessor.getSubject()).isEqualTo(expectedSubjectValue);
}
@Test
public void getAudienceWhenAudienceClaimNotExistingThenReturnNull() {
assertThat(this.claimAccessor.getAudience()).isNull();
}
@Test
public void getAudienceWhenAudienceClaimExistingThenReturnAudience() {
List<String> expectedAudienceValue = Arrays.asList("audience1", "audience2");
this.claims.put(OAuth2TokenIntrospectionClaimNames.AUD, expectedAudienceValue);
assertThat(this.claimAccessor.getAudience()).hasSameElementsAs(expectedAudienceValue);
}
@Test
public void getIssuerWhenIssuerClaimNotExistingThenReturnNull() {
assertThat(this.claimAccessor.getIssuer()).isNull();
}
@Test
public void getIssuerWhenIssuerClaimExistingThenReturnIssuer() throws MalformedURLException {
URL expectedIssuerValue = new URL("https://issuer.com");
this.claims.put(OAuth2TokenIntrospectionClaimNames.ISS, expectedIssuerValue);
assertThat(this.claimAccessor.getIssuer()).isEqualTo(expectedIssuerValue);
}
@Test
public void getIdWhenJtiClaimNotExistingThenReturnNull() {
assertThat(this.claimAccessor.getId()).isNull();
}
@Test
public void getIdWhenJtiClaimExistingThenReturnId() {
String expectedIdValue = "id";
this.claims.put(OAuth2TokenIntrospectionClaimNames.JTI, expectedIdValue);
assertThat(this.claimAccessor.getId()).isEqualTo(expectedIdValue);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 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,10 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
import org.springframework.security.oauth2.server.resource.introspection.BadOpaqueTokenException;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
import org.springframework.util.Assert;
@ -117,8 +117,8 @@ public final class OpaqueTokenAuthenticationProvider implements AuthenticationPr
}
private AbstractAuthenticationToken convert(OAuth2AuthenticatedPrincipal principal, String token) {
Instant iat = principal.getAttribute(OAuth2IntrospectionClaimNames.ISSUED_AT);
Instant exp = principal.getAttribute(OAuth2IntrospectionClaimNames.EXPIRES_AT);
Instant iat = principal.getAttribute(OAuth2TokenIntrospectionClaimNames.IAT);
Instant exp = principal.getAttribute(OAuth2TokenIntrospectionClaimNames.EXP);
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, token, iat, exp);
return new BearerTokenAuthentication(principal, accessToken, principal.getAuthorities());
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 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.
@ -27,10 +27,10 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
import org.springframework.security.oauth2.server.resource.introspection.BadOpaqueTokenException;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
import org.springframework.util.Assert;
@ -89,8 +89,8 @@ public class OpaqueTokenReactiveAuthenticationManager implements ReactiveAuthent
// @formatter:off
return this.introspector.introspect(token)
.map((principal) -> {
Instant iat = principal.getAttribute(OAuth2IntrospectionClaimNames.ISSUED_AT);
Instant exp = principal.getAttribute(OAuth2IntrospectionClaimNames.EXPIRES_AT);
Instant iat = principal.getAttribute(OAuth2TokenIntrospectionClaimNames.IAT);
Instant exp = principal.getAttribute(OAuth2TokenIntrospectionClaimNames.EXP);
// construct token
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, token, iat, exp);
return new BearerTokenAuthentication(principal, accessToken, principal.getAuthorities());

View File

@ -42,6 +42,7 @@ import org.springframework.http.client.support.BasicAuthenticationInterceptor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@ -192,28 +193,28 @@ public class NimbusOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
for (Audience audience : response.getAudience()) {
audiences.add(audience.getValue());
}
claims.put(OAuth2IntrospectionClaimNames.AUDIENCE, Collections.unmodifiableList(audiences));
claims.put(OAuth2TokenIntrospectionClaimNames.AUD, Collections.unmodifiableList(audiences));
}
if (response.getClientID() != null) {
claims.put(OAuth2IntrospectionClaimNames.CLIENT_ID, response.getClientID().getValue());
claims.put(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, response.getClientID().getValue());
}
if (response.getExpirationTime() != null) {
Instant exp = response.getExpirationTime().toInstant();
claims.put(OAuth2IntrospectionClaimNames.EXPIRES_AT, exp);
claims.put(OAuth2TokenIntrospectionClaimNames.EXP, exp);
}
if (response.getIssueTime() != null) {
Instant iat = response.getIssueTime().toInstant();
claims.put(OAuth2IntrospectionClaimNames.ISSUED_AT, iat);
claims.put(OAuth2TokenIntrospectionClaimNames.IAT, iat);
}
if (response.getIssuer() != null) {
claims.put(OAuth2IntrospectionClaimNames.ISSUER, issuer(response.getIssuer().getValue()));
claims.put(OAuth2TokenIntrospectionClaimNames.ISS, issuer(response.getIssuer().getValue()));
}
if (response.getNotBeforeTime() != null) {
claims.put(OAuth2IntrospectionClaimNames.NOT_BEFORE, response.getNotBeforeTime().toInstant());
claims.put(OAuth2TokenIntrospectionClaimNames.NBF, response.getNotBeforeTime().toInstant());
}
if (response.getScope() != null) {
List<String> scopes = Collections.unmodifiableList(response.getScope().toStringList());
claims.put(OAuth2IntrospectionClaimNames.SCOPE, scopes);
claims.put(OAuth2TokenIntrospectionClaimNames.SCOPE, scopes);
for (String scope : scopes) {
authorities.add(new SimpleGrantedAuthority(this.authorityPrefix + scope));
}
@ -227,7 +228,7 @@ public class NimbusOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
}
catch (Exception ex) {
throw new OAuth2IntrospectionException(
"Invalid " + OAuth2IntrospectionClaimNames.ISSUER + " value: " + uri);
"Invalid " + OAuth2TokenIntrospectionClaimNames.ISS + " value: " + uri);
}
}

View File

@ -38,6 +38,7 @@ import org.springframework.http.MediaType;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
import org.springframework.util.Assert;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ClientResponse;
@ -159,28 +160,28 @@ public class NimbusReactiveOpaqueTokenIntrospector implements ReactiveOpaqueToke
for (Audience audience : response.getAudience()) {
audiences.add(audience.getValue());
}
claims.put(OAuth2IntrospectionClaimNames.AUDIENCE, Collections.unmodifiableList(audiences));
claims.put(OAuth2TokenIntrospectionClaimNames.AUD, Collections.unmodifiableList(audiences));
}
if (response.getClientID() != null) {
claims.put(OAuth2IntrospectionClaimNames.CLIENT_ID, response.getClientID().getValue());
claims.put(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, response.getClientID().getValue());
}
if (response.getExpirationTime() != null) {
Instant exp = response.getExpirationTime().toInstant();
claims.put(OAuth2IntrospectionClaimNames.EXPIRES_AT, exp);
claims.put(OAuth2TokenIntrospectionClaimNames.EXP, exp);
}
if (response.getIssueTime() != null) {
Instant iat = response.getIssueTime().toInstant();
claims.put(OAuth2IntrospectionClaimNames.ISSUED_AT, iat);
claims.put(OAuth2TokenIntrospectionClaimNames.IAT, iat);
}
if (response.getIssuer() != null) {
claims.put(OAuth2IntrospectionClaimNames.ISSUER, issuer(response.getIssuer().getValue()));
claims.put(OAuth2TokenIntrospectionClaimNames.ISS, issuer(response.getIssuer().getValue()));
}
if (response.getNotBeforeTime() != null) {
claims.put(OAuth2IntrospectionClaimNames.NOT_BEFORE, response.getNotBeforeTime().toInstant());
claims.put(OAuth2TokenIntrospectionClaimNames.NBF, response.getNotBeforeTime().toInstant());
}
if (response.getScope() != null) {
List<String> scopes = Collections.unmodifiableList(response.getScope().toStringList());
claims.put(OAuth2IntrospectionClaimNames.SCOPE, scopes);
claims.put(OAuth2TokenIntrospectionClaimNames.SCOPE, scopes);
for (String scope : scopes) {
authorities.add(new SimpleGrantedAuthority(this.authorityPrefix + scope));
@ -195,7 +196,7 @@ public class NimbusReactiveOpaqueTokenIntrospector implements ReactiveOpaqueToke
}
catch (Exception ex) {
throw new OAuth2IntrospectionException(
"Invalid " + OAuth2IntrospectionClaimNames.ISSUER + " value: " + uri);
"Invalid " + OAuth2TokenIntrospectionClaimNames.ISS + " value: " + uri);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 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.
@ -16,16 +16,16 @@
package org.springframework.security.oauth2.server.resource.introspection;
import java.net.URL;
import java.time.Instant;
import java.util.List;
import org.springframework.lang.Nullable;
import org.springframework.security.oauth2.core.ClaimAccessor;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimAccessor;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
/**
* A {@link ClaimAccessor} for the &quot;claims&quot; that may be contained in the
* Introspection Response.
*
* @deprecated Use {@link OAuth2TokenIntrospectionClaimAccessor} instead
* @author David Kovac
* @since 5.4
* @see ClaimAccessor
@ -34,107 +34,17 @@ import org.springframework.security.oauth2.core.ClaimAccessor;
* @see <a target="_blank" href=
* "https://tools.ietf.org/html/rfc7662#section-2.2">Introspection Response</a>
*/
public interface OAuth2IntrospectionClaimAccessor extends ClaimAccessor {
/**
* Returns the indicator {@code (active)} whether or not the token is currently active
* @return the indicator whether or not the token is currently active
*/
default boolean isActive() {
return Boolean.TRUE.equals(this.getClaimAsBoolean(OAuth2IntrospectionClaimNames.ACTIVE));
}
@Deprecated
public interface OAuth2IntrospectionClaimAccessor extends OAuth2TokenIntrospectionClaimAccessor {
/**
* Returns the scopes {@code (scope)} associated with the token
* @return the scopes associated with the token
* @deprecated Since 5.6. Use {@link #getScopes()} instead
*/
@Nullable
default String getScope() {
return this.getClaimAsString(OAuth2IntrospectionClaimNames.SCOPE);
}
/**
* Returns the client identifier {@code (client_id)} for the token
* @return the client identifier for the token
*/
default String getClientId() {
return this.getClaimAsString(OAuth2IntrospectionClaimNames.CLIENT_ID);
}
/**
* Returns a human-readable identifier {@code (username)} for the resource owner that
* authorized the token
* @return a human-readable identifier for the resource owner that authorized the
* token
*/
default String getUsername() {
return this.getClaimAsString(OAuth2IntrospectionClaimNames.USERNAME);
}
/**
* Returns the type of the token {@code (token_type)}, for example {@code bearer}.
* @return the type of the token, for example {@code bearer}.
*/
default String getTokenType() {
return this.getClaimAsString(OAuth2IntrospectionClaimNames.TOKEN_TYPE);
}
/**
* Returns a timestamp {@code (exp)} indicating when the token expires
* @return a timestamp indicating when the token expires
*/
default Instant getExpiresAt() {
return this.getClaimAsInstant(OAuth2IntrospectionClaimNames.EXPIRES_AT);
}
/**
* Returns a timestamp {@code (iat)} indicating when the token was issued
* @return a timestamp indicating when the token was issued
*/
default Instant getIssuedAt() {
return this.getClaimAsInstant(OAuth2IntrospectionClaimNames.ISSUED_AT);
}
/**
* Returns a timestamp {@code (nbf)} indicating when the token is not to be used
* before
* @return a timestamp indicating when the token is not to be used before
*/
default Instant getNotBefore() {
return this.getClaimAsInstant(OAuth2IntrospectionClaimNames.NOT_BEFORE);
}
/**
* Returns usually a machine-readable identifier {@code (sub)} of the resource owner
* who authorized the token
* @return usually a machine-readable identifier of the resource owner who authorized
* the token
*/
default String getSubject() {
return this.getClaimAsString(OAuth2IntrospectionClaimNames.SUBJECT);
}
/**
* Returns the intended audience {@code (aud)} for the token
* @return the intended audience for the token
*/
default List<String> getAudience() {
return this.getClaimAsStringList(OAuth2IntrospectionClaimNames.AUDIENCE);
}
/**
* Returns the issuer {@code (iss)} of the token
* @return the issuer of the token
*/
default URL getIssuer() {
return this.getClaimAsURL(OAuth2IntrospectionClaimNames.ISSUER);
}
/**
* Returns the identifier {@code (jti)} for the token
* @return the identifier for the token
*/
default String getId() {
return this.getClaimAsString(OAuth2IntrospectionClaimNames.JTI);
return getClaimAsString(OAuth2TokenIntrospectionClaimNames.SCOPE);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 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.
@ -16,76 +16,55 @@
package org.springframework.security.oauth2.server.resource.introspection;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
/**
* The names of the &quot;Introspection Claims&quot; defined by an
* <a target="_blank" href="https://tools.ietf.org/html/rfc7662#section-2.2">Introspection
* Response</a>.
*
* @deprecated Use {@link OAuth2TokenIntrospectionClaimNames} instead
* @author Josh Cummings
* @since 5.2
*/
public interface OAuth2IntrospectionClaimNames {
/**
* {@code active} - Indicator whether or not the token is currently active
*/
String ACTIVE = "active";
/**
* {@code scope} - The scopes for the token
*/
String SCOPE = "scope";
/**
* {@code client_id} - The Client identifier for the token
*/
String CLIENT_ID = "client_id";
/**
* {@code username} - A human-readable identifier for the resource owner that
* authorized the token
*/
String USERNAME = "username";
/**
* {@code token_type} - The type of the token, for example {@code bearer}.
*/
String TOKEN_TYPE = "token_type";
@Deprecated
public interface OAuth2IntrospectionClaimNames extends OAuth2TokenIntrospectionClaimNames {
/**
* {@code exp} - A timestamp indicating when the token expires
* @deprecated use {@link #EXP} instead
*/
String EXPIRES_AT = "exp";
String EXPIRES_AT = EXP;
/**
* {@code iat} - A timestamp indicating when the token was issued
* @deprecated use {@link #IAT} instead
*/
String ISSUED_AT = "iat";
String ISSUED_AT = IAT;
/**
* {@code nbf} - A timestamp indicating when the token is not to be used before
* @deprecated use {@link #NBF} instead
*/
String NOT_BEFORE = "nbf";
String NOT_BEFORE = NBF;
/**
* {@code sub} - Usually a machine-readable identifier of the resource owner who
* authorized the token
* @deprecated use {@link #SUB} instead
*/
String SUBJECT = "sub";
String SUBJECT = SUB;
/**
* {@code aud} - The intended audience for the token
* @deprecated use {@link #AUD} instead
*/
String AUDIENCE = "aud";
String AUDIENCE = AUD;
/**
* {@code iss} - The issuer of the token
* @deprecated use {@link #ISS} instead
*/
String ISSUER = "iss";
/**
* {@code jti} - The identifier for the token
*/
String JTI = "jti";
String ISSUER = ISS;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 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.
@ -29,7 +29,6 @@ import java.util.function.Consumer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionAuthenticatedPrincipal;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames;
/**
* Test values of {@link OAuth2AuthenticatedPrincipal}s
@ -48,15 +47,15 @@ public final class TestOAuth2AuthenticatedPrincipals {
public static OAuth2AuthenticatedPrincipal active(Consumer<Map<String, Object>> attributesConsumer) {
Map<String, Object> attributes = new HashMap<>();
attributes.put(OAuth2IntrospectionClaimNames.ACTIVE, true);
attributes.put(OAuth2IntrospectionClaimNames.AUDIENCE, Arrays.asList("https://protected.example.net/resource"));
attributes.put(OAuth2IntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4");
attributes.put(OAuth2IntrospectionClaimNames.EXPIRES_AT, Instant.ofEpochSecond(1419356238));
attributes.put(OAuth2IntrospectionClaimNames.NOT_BEFORE, Instant.ofEpochSecond(29348723984L));
attributes.put(OAuth2IntrospectionClaimNames.ISSUER, url("https://server.example.com/"));
attributes.put(OAuth2IntrospectionClaimNames.SCOPE, Arrays.asList("read", "write", "dolphin"));
attributes.put(OAuth2IntrospectionClaimNames.SUBJECT, "Z5O3upPC88QrAjx00dis");
attributes.put(OAuth2IntrospectionClaimNames.USERNAME, "jdoe");
attributes.put(OAuth2TokenIntrospectionClaimNames.ACTIVE, true);
attributes.put(OAuth2TokenIntrospectionClaimNames.AUD, Arrays.asList("https://protected.example.net/resource"));
attributes.put(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4");
attributes.put(OAuth2TokenIntrospectionClaimNames.EXP, Instant.ofEpochSecond(1419356238));
attributes.put(OAuth2TokenIntrospectionClaimNames.NBF, Instant.ofEpochSecond(29348723984L));
attributes.put(OAuth2TokenIntrospectionClaimNames.ISS, url("https://server.example.com/"));
attributes.put(OAuth2TokenIntrospectionClaimNames.SCOPE, Arrays.asList("read", "write", "dolphin"));
attributes.put(OAuth2TokenIntrospectionClaimNames.SUB, "Z5O3upPC88QrAjx00dis");
attributes.put(OAuth2TokenIntrospectionClaimNames.USERNAME, "jdoe");
attributesConsumer.accept(attributes);
Collection<GrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority("SCOPE_read"),
new SimpleGrantedAuthority("SCOPE_write"), new SimpleGrantedAuthority("SCOPE_dolphin"));

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 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.
@ -33,7 +33,7 @@ import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
@ -58,9 +58,9 @@ public class BearerTokenAuthenticationTests {
@BeforeEach
public void setUp() {
this.attributesMap.put(OAuth2IntrospectionClaimNames.SUBJECT, this.name);
this.attributesMap.put(OAuth2IntrospectionClaimNames.CLIENT_ID, "client_id");
this.attributesMap.put(OAuth2IntrospectionClaimNames.USERNAME, "username");
this.attributesMap.put(OAuth2TokenIntrospectionClaimNames.SUB, this.name);
this.attributesMap.put(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, "client_id");
this.attributesMap.put(OAuth2TokenIntrospectionClaimNames.USERNAME, "username");
this.principal = new DefaultOAuth2AuthenticatedPrincipal(this.attributesMap, null);
}
@ -86,7 +86,7 @@ public class BearerTokenAuthenticationTests {
BearerTokenAuthentication authenticated = new BearerTokenAuthentication(this.principal, this.token, null);
// @formatter:off
assertThat(authenticated.getName())
.isEqualTo(this.principal.getAttribute(OAuth2IntrospectionClaimNames.SUBJECT));
.isEqualTo(this.principal.getAttribute(OAuth2TokenIntrospectionClaimNames.SUB));
// @formatter:on
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 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.
@ -27,10 +27,10 @@ import org.junit.jupiter.api.Test;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
import org.springframework.security.oauth2.core.TestOAuth2AuthenticatedPrincipals;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionAuthenticatedPrincipal;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
@ -61,16 +61,16 @@ public class OpaqueTokenAuthenticationProviderTests {
// @formatter:off
assertThat(attributes)
.isNotNull()
.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
.containsEntry(OAuth2IntrospectionClaimNames.AUDIENCE,
.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)
.containsEntry(OAuth2TokenIntrospectionClaimNames.AUD,
Arrays.asList("https://protected.example.net/resource"))
.containsEntry(OAuth2IntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4")
.containsEntry(OAuth2IntrospectionClaimNames.EXPIRES_AT, Instant.ofEpochSecond(1419356238))
.containsEntry(OAuth2IntrospectionClaimNames.ISSUER, new URL("https://server.example.com/"))
.containsEntry(OAuth2IntrospectionClaimNames.NOT_BEFORE, Instant.ofEpochSecond(29348723984L))
.containsEntry(OAuth2IntrospectionClaimNames.SCOPE, Arrays.asList("read", "write", "dolphin"))
.containsEntry(OAuth2IntrospectionClaimNames.SUBJECT, "Z5O3upPC88QrAjx00dis")
.containsEntry(OAuth2IntrospectionClaimNames.USERNAME, "jdoe")
.containsEntry(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4")
.containsEntry(OAuth2TokenIntrospectionClaimNames.EXP, Instant.ofEpochSecond(1419356238))
.containsEntry(OAuth2TokenIntrospectionClaimNames.ISS, new URL("https://server.example.com/"))
.containsEntry(OAuth2TokenIntrospectionClaimNames.NBF, Instant.ofEpochSecond(29348723984L))
.containsEntry(OAuth2TokenIntrospectionClaimNames.SCOPE, Arrays.asList("read", "write", "dolphin"))
.containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, "Z5O3upPC88QrAjx00dis")
.containsEntry(OAuth2TokenIntrospectionClaimNames.USERNAME, "jdoe")
.containsEntry("extension_field", "twenty-seven");
assertThat(result.getAuthorities())
.extracting("authority")
@ -92,7 +92,7 @@ public class OpaqueTokenAuthenticationProviderTests {
// @formatter:off
assertThat(attributes)
.isNotNull()
.doesNotContainKey(OAuth2IntrospectionClaimNames.SCOPE);
.doesNotContainKey(OAuth2TokenIntrospectionClaimNames.SCOPE);
// @formatter:on
assertThat(result.getAuthorities()).isEmpty();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 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.
@ -28,10 +28,10 @@ import reactor.core.publisher.Mono;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
import org.springframework.security.oauth2.core.TestOAuth2AuthenticatedPrincipals;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionAuthenticatedPrincipal;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
@ -62,16 +62,16 @@ public class OpaqueTokenReactiveAuthenticationManagerTests {
// @formatter:off
assertThat(attributes)
.isNotNull()
.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
.containsEntry(OAuth2IntrospectionClaimNames.AUDIENCE,
.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)
.containsEntry(OAuth2TokenIntrospectionClaimNames.AUD,
Arrays.asList("https://protected.example.net/resource"))
.containsEntry(OAuth2IntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4")
.containsEntry(OAuth2IntrospectionClaimNames.EXPIRES_AT, Instant.ofEpochSecond(1419356238))
.containsEntry(OAuth2IntrospectionClaimNames.ISSUER, new URL("https://server.example.com/"))
.containsEntry(OAuth2IntrospectionClaimNames.NOT_BEFORE, Instant.ofEpochSecond(29348723984L))
.containsEntry(OAuth2IntrospectionClaimNames.SCOPE, Arrays.asList("read", "write", "dolphin"))
.containsEntry(OAuth2IntrospectionClaimNames.SUBJECT, "Z5O3upPC88QrAjx00dis")
.containsEntry(OAuth2IntrospectionClaimNames.USERNAME, "jdoe")
.containsEntry(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4")
.containsEntry(OAuth2TokenIntrospectionClaimNames.EXP, Instant.ofEpochSecond(1419356238))
.containsEntry(OAuth2TokenIntrospectionClaimNames.ISS, new URL("https://server.example.com/"))
.containsEntry(OAuth2TokenIntrospectionClaimNames.NBF, Instant.ofEpochSecond(29348723984L))
.containsEntry(OAuth2TokenIntrospectionClaimNames.SCOPE, Arrays.asList("read", "write", "dolphin"))
.containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, "Z5O3upPC88QrAjx00dis")
.containsEntry(OAuth2TokenIntrospectionClaimNames.USERNAME, "jdoe")
.containsEntry("extension_field", "twenty-seven");
assertThat(result.getAuthorities())
.extracting("authority")
@ -90,7 +90,7 @@ public class OpaqueTokenReactiveAuthenticationManagerTests {
Authentication result = provider.authenticate(new BearerTokenAuthenticationToken("token")).block();
assertThat(result.getPrincipal()).isInstanceOf(OAuth2IntrospectionAuthenticatedPrincipal.class);
Map<String, Object> attributes = ((OAuth2AuthenticatedPrincipal) result.getPrincipal()).getAttributes();
assertThat(attributes).isNotNull().doesNotContainKey(OAuth2IntrospectionClaimNames.SCOPE);
assertThat(attributes).isNotNull().doesNotContainKey(OAuth2TokenIntrospectionClaimNames.SCOPE);
assertThat(result.getAuthorities()).isEmpty();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 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.
@ -40,6 +40,7 @@ import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
import org.springframework.web.client.RestOperations;
import static org.assertj.core.api.Assertions.assertThat;
@ -140,15 +141,15 @@ public class NimbusOpaqueTokenIntrospectorTests {
// @formatter:off
assertThat(authority.getAttributes())
.isNotNull()
.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
.containsEntry(OAuth2IntrospectionClaimNames.AUDIENCE,
.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)
.containsEntry(OAuth2TokenIntrospectionClaimNames.AUD,
Arrays.asList("https://protected.example.net/resource"))
.containsEntry(OAuth2IntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4")
.containsEntry(OAuth2IntrospectionClaimNames.EXPIRES_AT, Instant.ofEpochSecond(1419356238))
.containsEntry(OAuth2IntrospectionClaimNames.ISSUER, new URL("https://server.example.com/"))
.containsEntry(OAuth2IntrospectionClaimNames.SCOPE, Arrays.asList("read", "write", "dolphin"))
.containsEntry(OAuth2IntrospectionClaimNames.SUBJECT, "Z5O3upPC88QrAjx00dis")
.containsEntry(OAuth2IntrospectionClaimNames.USERNAME, "jdoe")
.containsEntry(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4")
.containsEntry(OAuth2TokenIntrospectionClaimNames.EXP, Instant.ofEpochSecond(1419356238))
.containsEntry(OAuth2TokenIntrospectionClaimNames.ISS, new URL("https://server.example.com/"))
.containsEntry(OAuth2TokenIntrospectionClaimNames.SCOPE, Arrays.asList("read", "write", "dolphin"))
.containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, "Z5O3upPC88QrAjx00dis")
.containsEntry(OAuth2TokenIntrospectionClaimNames.USERNAME, "jdoe")
.containsEntry("extension_field", "twenty-seven");
// @formatter:on
}
@ -182,9 +183,9 @@ public class NimbusOpaqueTokenIntrospectorTests {
@Test
public void introspectWhenActiveTokenThenParsesValuesInResponse() {
Map<String, Object> introspectedValues = new HashMap<>();
introspectedValues.put(OAuth2IntrospectionClaimNames.ACTIVE, true);
introspectedValues.put(OAuth2IntrospectionClaimNames.AUDIENCE, Arrays.asList("aud"));
introspectedValues.put(OAuth2IntrospectionClaimNames.NOT_BEFORE, 29348723984L);
introspectedValues.put(OAuth2TokenIntrospectionClaimNames.ACTIVE, true);
introspectedValues.put(OAuth2TokenIntrospectionClaimNames.AUD, Arrays.asList("aud"));
introspectedValues.put(OAuth2TokenIntrospectionClaimNames.NBF, 29348723984L);
RestOperations restOperations = mock(RestOperations.class);
OpaqueTokenIntrospector introspectionClient = new NimbusOpaqueTokenIntrospector(INTROSPECTION_URL,
restOperations);
@ -194,11 +195,11 @@ public class NimbusOpaqueTokenIntrospectorTests {
// @formatter:off
assertThat(authority.getAttributes())
.isNotNull()
.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
.containsEntry(OAuth2IntrospectionClaimNames.AUDIENCE, Arrays.asList("aud"))
.containsEntry(OAuth2IntrospectionClaimNames.NOT_BEFORE, Instant.ofEpochSecond(29348723984L))
.doesNotContainKey(OAuth2IntrospectionClaimNames.CLIENT_ID)
.doesNotContainKey(OAuth2IntrospectionClaimNames.SCOPE);
.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)
.containsEntry(OAuth2TokenIntrospectionClaimNames.AUD, Arrays.asList("aud"))
.containsEntry(OAuth2TokenIntrospectionClaimNames.NBF, Instant.ofEpochSecond(29348723984L))
.doesNotContainKey(OAuth2TokenIntrospectionClaimNames.CLIENT_ID)
.doesNotContainKey(OAuth2TokenIntrospectionClaimNames.SCOPE);
// @formatter:on
}

View File

@ -37,6 +37,7 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
@ -111,15 +112,15 @@ public class NimbusReactiveOpaqueTokenIntrospectorTests {
// @formatter:off
assertThat(authority.getAttributes())
.isNotNull()
.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
.containsEntry(OAuth2IntrospectionClaimNames.AUDIENCE,
.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)
.containsEntry(OAuth2TokenIntrospectionClaimNames.AUD,
Arrays.asList("https://protected.example.net/resource"))
.containsEntry(OAuth2IntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4")
.containsEntry(OAuth2IntrospectionClaimNames.EXPIRES_AT, Instant.ofEpochSecond(1419356238))
.containsEntry(OAuth2IntrospectionClaimNames.ISSUER, new URL("https://server.example.com/"))
.containsEntry(OAuth2IntrospectionClaimNames.SCOPE, Arrays.asList("read", "write", "dolphin"))
.containsEntry(OAuth2IntrospectionClaimNames.SUBJECT, "Z5O3upPC88QrAjx00dis")
.containsEntry(OAuth2IntrospectionClaimNames.USERNAME, "jdoe")
.containsEntry(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4")
.containsEntry(OAuth2TokenIntrospectionClaimNames.EXP, Instant.ofEpochSecond(1419356238))
.containsEntry(OAuth2TokenIntrospectionClaimNames.ISS, new URL("https://server.example.com/"))
.containsEntry(OAuth2TokenIntrospectionClaimNames.SCOPE, Arrays.asList("read", "write", "dolphin"))
.containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, "Z5O3upPC88QrAjx00dis")
.containsEntry(OAuth2TokenIntrospectionClaimNames.USERNAME, "jdoe")
.containsEntry("extension_field", "twenty-seven");
// @formatter:on
}
@ -151,9 +152,9 @@ public class NimbusReactiveOpaqueTokenIntrospectorTests {
@Test
public void authenticateWhenActiveTokenThenParsesValuesInResponse() {
Map<String, Object> introspectedValues = new HashMap<>();
introspectedValues.put(OAuth2IntrospectionClaimNames.ACTIVE, true);
introspectedValues.put(OAuth2IntrospectionClaimNames.AUDIENCE, Arrays.asList("aud"));
introspectedValues.put(OAuth2IntrospectionClaimNames.NOT_BEFORE, 29348723984L);
introspectedValues.put(OAuth2TokenIntrospectionClaimNames.ACTIVE, true);
introspectedValues.put(OAuth2TokenIntrospectionClaimNames.AUD, Arrays.asList("aud"));
introspectedValues.put(OAuth2TokenIntrospectionClaimNames.NBF, 29348723984L);
WebClient webClient = mockResponse(new JSONObject(introspectedValues).toJSONString());
NimbusReactiveOpaqueTokenIntrospector introspectionClient = new NimbusReactiveOpaqueTokenIntrospector(
INTROSPECTION_URL, webClient);
@ -161,11 +162,11 @@ public class NimbusReactiveOpaqueTokenIntrospectorTests {
// @formatter:off
assertThat(authority.getAttributes())
.isNotNull()
.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
.containsEntry(OAuth2IntrospectionClaimNames.AUDIENCE, Arrays.asList("aud"))
.containsEntry(OAuth2IntrospectionClaimNames.NOT_BEFORE, Instant.ofEpochSecond(29348723984L))
.doesNotContainKey(OAuth2IntrospectionClaimNames.CLIENT_ID)
.doesNotContainKey(OAuth2IntrospectionClaimNames.SCOPE);
.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)
.containsEntry(OAuth2TokenIntrospectionClaimNames.AUD, Arrays.asList("aud"))
.containsEntry(OAuth2TokenIntrospectionClaimNames.NBF, Instant.ofEpochSecond(29348723984L))
.doesNotContainKey(OAuth2TokenIntrospectionClaimNames.CLIENT_ID)
.doesNotContainKey(OAuth2TokenIntrospectionClaimNames.SCOPE);
// @formatter:on
}

View File

@ -56,6 +56,7 @@ import org.springframework.security.oauth2.client.web.server.WebSessionServerOAu
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
@ -71,7 +72,6 @@ import org.springframework.security.oauth2.server.resource.authentication.Bearer
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionAuthenticatedPrincipal;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames;
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors;
import org.springframework.security.web.server.csrf.CsrfWebFilter;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
@ -648,14 +648,14 @@ public final class SecurityMockServerConfigurers {
private Map<String, Object> defaultAttributes() {
Map<String, Object> attributes = new HashMap<>();
attributes.put(OAuth2IntrospectionClaimNames.SUBJECT, "user");
attributes.put(OAuth2IntrospectionClaimNames.SCOPE, "read");
attributes.put(OAuth2TokenIntrospectionClaimNames.SUB, "user");
attributes.put(OAuth2TokenIntrospectionClaimNames.SCOPE, "read");
return attributes;
}
private Collection<GrantedAuthority> defaultAuthorities() {
Map<String, Object> attributes = this.attributes.get();
Object scope = attributes.get(OAuth2IntrospectionClaimNames.SCOPE);
Object scope = attributes.get(OAuth2TokenIntrospectionClaimNames.SCOPE);
if (scope == null) {
return Collections.emptyList();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 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.
@ -70,6 +70,7 @@ import org.springframework.security.oauth2.client.web.method.annotation.OAuth2Au
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
@ -85,7 +86,6 @@ import org.springframework.security.oauth2.server.resource.authentication.Bearer
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionAuthenticatedPrincipal;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames;
import org.springframework.security.test.context.TestSecurityContextHolder;
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers;
import org.springframework.security.test.web.support.WebTestUtils;
@ -1191,14 +1191,14 @@ public final class SecurityMockMvcRequestPostProcessors {
private Map<String, Object> defaultAttributes() {
Map<String, Object> attributes = new HashMap<>();
attributes.put(OAuth2IntrospectionClaimNames.SUBJECT, "user");
attributes.put(OAuth2IntrospectionClaimNames.SCOPE, "read");
attributes.put(OAuth2TokenIntrospectionClaimNames.SUB, "user");
attributes.put(OAuth2TokenIntrospectionClaimNames.SCOPE, "read");
return attributes;
}
private Collection<GrantedAuthority> defaultAuthorities() {
Map<String, Object> attributes = this.attributes.get();
Object scope = attributes.get(OAuth2IntrospectionClaimNames.SCOPE);
Object scope = attributes.get(OAuth2TokenIntrospectionClaimNames.SCOPE);
if (scope == null) {
return Collections.emptyList();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 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.
@ -29,9 +29,9 @@ import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
import org.springframework.security.oauth2.core.TestOAuth2AuthenticatedPrincipals;
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames;
import org.springframework.security.web.reactive.result.method.annotation.CurrentSecurityContextArgumentResolver;
import org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter;
import org.springframework.test.web.reactive.server.WebTestClient;
@ -64,7 +64,7 @@ public class SecurityMockServerConfigurerOpaqueTokenTests extends AbstractMockSe
BearerTokenAuthentication token = (BearerTokenAuthentication) context.getAuthentication();
assertThat(token.getAuthorities()).isNotEmpty();
assertThat(token.getToken()).isNotNull();
assertThat(token.getTokenAttributes().get(OAuth2IntrospectionClaimNames.SUBJECT)).isEqualTo("user");
assertThat(token.getTokenAttributes().get(OAuth2TokenIntrospectionClaimNames.SUB)).isEqualTo("user");
}
@Test
@ -83,12 +83,12 @@ public class SecurityMockServerConfigurerOpaqueTokenTests extends AbstractMockSe
String sub = new String("my-subject");
this.client
.mutateWith(SecurityMockServerConfigurers.mockOpaqueToken()
.attributes((attributes) -> attributes.put(OAuth2IntrospectionClaimNames.SUBJECT, sub)))
.attributes((attributes) -> attributes.put(OAuth2TokenIntrospectionClaimNames.SUB, sub)))
.get().exchange().expectStatus().isOk();
SecurityContext context = this.securityContextController.removeSecurityContext();
assertThat(context.getAuthentication()).isInstanceOf(BearerTokenAuthentication.class);
BearerTokenAuthentication token = (BearerTokenAuthentication) context.getAuthentication();
assertThat(token.getTokenAttributes().get(OAuth2IntrospectionClaimNames.SUBJECT)).isSameAs(sub);
assertThat(token.getTokenAttributes().get(OAuth2TokenIntrospectionClaimNames.SUB)).isSameAs(sub);
}
@Test
@ -108,23 +108,23 @@ public class SecurityMockServerConfigurerOpaqueTokenTests extends AbstractMockSe
.active((a) -> a.put("scope", "user"));
this.client
.mutateWith(SecurityMockServerConfigurers.mockOpaqueToken()
.attributes((a) -> a.put(OAuth2IntrospectionClaimNames.SUBJECT, "foo")).principal(principal))
.attributes((a) -> a.put(OAuth2TokenIntrospectionClaimNames.SUB, "foo")).principal(principal))
.get().exchange().expectStatus().isOk();
SecurityContext context = this.securityContextController.removeSecurityContext();
assertThat(context.getAuthentication()).isInstanceOf(BearerTokenAuthentication.class);
BearerTokenAuthentication token = (BearerTokenAuthentication) context.getAuthentication();
assertThat((String) ((OAuth2AuthenticatedPrincipal) token.getPrincipal())
.getAttribute(OAuth2IntrospectionClaimNames.SUBJECT))
.isEqualTo(principal.getAttribute(OAuth2IntrospectionClaimNames.SUBJECT));
.getAttribute(OAuth2TokenIntrospectionClaimNames.SUB))
.isEqualTo(principal.getAttribute(OAuth2TokenIntrospectionClaimNames.SUB));
this.client
.mutateWith(SecurityMockServerConfigurers.mockOpaqueToken().principal(principal)
.attributes((a) -> a.put(OAuth2IntrospectionClaimNames.SUBJECT, "bar")))
.attributes((a) -> a.put(OAuth2TokenIntrospectionClaimNames.SUB, "bar")))
.get().exchange().expectStatus().isOk();
context = this.securityContextController.removeSecurityContext();
assertThat(context.getAuthentication()).isInstanceOf(BearerTokenAuthentication.class);
token = (BearerTokenAuthentication) context.getAuthentication();
assertThat((String) ((OAuth2AuthenticatedPrincipal) token.getPrincipal())
.getAttribute(OAuth2IntrospectionClaimNames.SUBJECT)).isEqualTo("bar");
.getAttribute(OAuth2TokenIntrospectionClaimNames.SUB)).isEqualTo("bar");
}
}