25 changed files with 1115 additions and 268 deletions
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
/* |
||||
* Copyright 2020-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.context; |
||||
|
||||
import java.util.Map; |
||||
|
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* A facility for holding information associated to a specific context. |
||||
* |
||||
* @author Joe Grandja |
||||
* @since 0.1.0 |
||||
*/ |
||||
public interface Context { |
||||
|
||||
@Nullable |
||||
<V> V get(Object key); |
||||
|
||||
@Nullable |
||||
default <V> V get(Class<V> key) { |
||||
Assert.notNull(key, "key cannot be null"); |
||||
V value = get((Object) key); |
||||
return key.isInstance(value) ? value : null; |
||||
} |
||||
|
||||
boolean hasKey(Object key); |
||||
|
||||
static Context of(Map<Object, Object> context) { |
||||
return new DefaultContext(context); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
|
||||
/* |
||||
* Copyright 2020-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.context; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* @author Joe Grandja |
||||
* @since 0.1.0 |
||||
*/ |
||||
final class DefaultContext implements Context { |
||||
private final Map<Object, Object> context; |
||||
|
||||
DefaultContext(Map<Object, Object> context) { |
||||
Assert.notNull(context, "context cannot be null"); |
||||
this.context = Collections.unmodifiableMap(new HashMap<>(context)); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
@Override |
||||
@Nullable |
||||
public <V> V get(Object key) { |
||||
return hasKey(key) ? (V) this.context.get(key) : null; |
||||
} |
||||
|
||||
@Override |
||||
public boolean hasKey(Object key) { |
||||
Assert.notNull(key, "key cannot be null"); |
||||
return this.context.containsKey(key); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,123 @@
@@ -0,0 +1,123 @@
|
||||
/* |
||||
* Copyright 2020-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.server.authorization.authentication; |
||||
|
||||
import java.time.Instant; |
||||
import java.time.temporal.ChronoUnit; |
||||
import java.util.Collections; |
||||
import java.util.Set; |
||||
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; |
||||
import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames; |
||||
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames; |
||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; |
||||
import org.springframework.security.oauth2.jwt.JoseHeader; |
||||
import org.springframework.security.oauth2.jwt.JwtClaimsSet; |
||||
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext; |
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; |
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationAttributeNames; |
||||
import org.springframework.security.oauth2.server.authorization.TokenType; |
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; |
||||
import org.springframework.util.CollectionUtils; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* @author Joe Grandja |
||||
* @since 0.1.0 |
||||
*/ |
||||
final class JwtEncodingContextUtils { |
||||
|
||||
private JwtEncodingContextUtils() { |
||||
} |
||||
|
||||
static JwtEncodingContext.Builder accessTokenContext(RegisteredClient registeredClient, OAuth2Authorization authorization) { |
||||
// @formatter:off
|
||||
return accessTokenContext(registeredClient, authorization, |
||||
authorization.getAttribute(OAuth2AuthorizationAttributeNames.AUTHORIZED_SCOPES)); |
||||
// @formatter:on
|
||||
} |
||||
|
||||
static JwtEncodingContext.Builder accessTokenContext(RegisteredClient registeredClient, OAuth2Authorization authorization, |
||||
Set<String> authorizedScopes) { |
||||
// @formatter:off
|
||||
return accessTokenContext(registeredClient, authorization.getPrincipalName(), authorizedScopes) |
||||
.authorization(authorization); |
||||
// @formatter:on
|
||||
} |
||||
|
||||
static JwtEncodingContext.Builder accessTokenContext(RegisteredClient registeredClient, |
||||
String principalName, Set<String> authorizedScopes) { |
||||
|
||||
JoseHeader.Builder headersBuilder = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256); |
||||
|
||||
String issuer = "http://auth-server:9000"; // TODO Allow configuration for issuer claim
|
||||
Instant issuedAt = Instant.now(); |
||||
Instant expiresAt = issuedAt.plus(registeredClient.getTokenSettings().accessTokenTimeToLive()); |
||||
|
||||
// @formatter:off
|
||||
JwtClaimsSet.Builder claimsBuilder = JwtClaimsSet.builder() |
||||
.issuer(issuer) |
||||
.subject(principalName) |
||||
.audience(Collections.singletonList(registeredClient.getClientId())) |
||||
.issuedAt(issuedAt) |
||||
.expiresAt(expiresAt) |
||||
.notBefore(issuedAt); |
||||
if (!CollectionUtils.isEmpty(authorizedScopes)) { |
||||
claimsBuilder.claim(OAuth2ParameterNames.SCOPE, authorizedScopes); |
||||
} |
||||
// @formatter:on
|
||||
|
||||
// @formatter:off
|
||||
return JwtEncodingContext.with(headersBuilder, claimsBuilder) |
||||
.registeredClient(registeredClient) |
||||
.tokenType(TokenType.ACCESS_TOKEN); |
||||
// @formatter:on
|
||||
} |
||||
|
||||
static JwtEncodingContext.Builder idTokenContext(RegisteredClient registeredClient, OAuth2Authorization authorization) { |
||||
JoseHeader.Builder headersBuilder = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256); |
||||
|
||||
String issuer = "http://auth-server:9000"; // TODO Allow configuration for issuer claim
|
||||
Instant issuedAt = Instant.now(); |
||||
Instant expiresAt = issuedAt.plus(30, ChronoUnit.MINUTES); // TODO Allow configuration for id token time-to-live
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( |
||||
OAuth2AuthorizationAttributeNames.AUTHORIZATION_REQUEST); |
||||
String nonce = (String) authorizationRequest.getAdditionalParameters().get(OidcParameterNames.NONCE); |
||||
|
||||
// @formatter:off
|
||||
JwtClaimsSet.Builder claimsBuilder = JwtClaimsSet.builder() |
||||
.issuer(issuer) |
||||
.subject(authorization.getPrincipalName()) |
||||
.audience(Collections.singletonList(registeredClient.getClientId())) |
||||
.issuedAt(issuedAt) |
||||
.expiresAt(expiresAt) |
||||
.claim(IdTokenClaimNames.AZP, registeredClient.getClientId()); |
||||
if (StringUtils.hasText(nonce)) { |
||||
claimsBuilder.claim(IdTokenClaimNames.NONCE, nonce); |
||||
} |
||||
// TODO Add 'auth_time' claim
|
||||
// @formatter:on
|
||||
|
||||
// @formatter:off
|
||||
return JwtEncodingContext.with(headersBuilder, claimsBuilder) |
||||
.registeredClient(registeredClient) |
||||
.authorization(authorization) |
||||
.tokenType(new TokenType(OidcParameterNames.ID_TOKEN)); |
||||
// @formatter:on
|
||||
} |
||||
|
||||
} |
||||
@ -1,97 +0,0 @@
@@ -1,97 +0,0 @@
|
||||
/* |
||||
* Copyright 2020-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.server.authorization.authentication; |
||||
|
||||
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator; |
||||
import org.springframework.security.crypto.keygen.StringKeyGenerator; |
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken; |
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken2; |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; |
||||
import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames; |
||||
import org.springframework.security.oauth2.jwt.JoseHeader; |
||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; |
||||
import org.springframework.security.oauth2.jwt.Jwt; |
||||
import org.springframework.security.oauth2.jwt.JwtClaimsSet; |
||||
import org.springframework.security.oauth2.jwt.JwtEncoder; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
import java.time.Duration; |
||||
import java.time.Instant; |
||||
import java.time.temporal.ChronoUnit; |
||||
import java.util.Base64; |
||||
import java.util.Collections; |
||||
import java.util.Set; |
||||
|
||||
/** |
||||
* @author Alexey Nesterov |
||||
* @since 0.0.3 |
||||
*/ |
||||
class OAuth2TokenIssuerUtil { |
||||
|
||||
private static final StringKeyGenerator TOKEN_GENERATOR = new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96); |
||||
|
||||
static Jwt issueJwtAccessToken(JwtEncoder jwtEncoder, String subject, String audience, Set<String> scopes, Duration tokenTimeToLive) { |
||||
JoseHeader joseHeader = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).build(); |
||||
|
||||
String issuer = "http://auth-server:9000"; // TODO Allow configuration for issuer claim
|
||||
Instant issuedAt = Instant.now(); |
||||
Instant expiresAt = issuedAt.plus(tokenTimeToLive); |
||||
|
||||
JwtClaimsSet jwtClaimsSet = JwtClaimsSet.builder() |
||||
.issuer(issuer) |
||||
.subject(subject) |
||||
.audience(Collections.singletonList(audience)) |
||||
.issuedAt(issuedAt) |
||||
.expiresAt(expiresAt) |
||||
.notBefore(issuedAt) |
||||
.claim(OAuth2ParameterNames.SCOPE, scopes) |
||||
.build(); |
||||
|
||||
return jwtEncoder.encode(joseHeader, jwtClaimsSet); |
||||
} |
||||
|
||||
static Jwt issueIdToken(JwtEncoder jwtEncoder, String subject, String audience, String nonce) { |
||||
JoseHeader joseHeader = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).build(); |
||||
|
||||
String issuer = "http://auth-server:9000"; // TODO Allow configuration for issuer claim
|
||||
Instant issuedAt = Instant.now(); |
||||
Instant expiresAt = issuedAt.plus(30, ChronoUnit.MINUTES); // TODO Allow configuration for id token time-to-live
|
||||
|
||||
JwtClaimsSet.Builder builder = JwtClaimsSet.builder() |
||||
.issuer(issuer) |
||||
.subject(subject) |
||||
.audience(Collections.singletonList(audience)) |
||||
.issuedAt(issuedAt) |
||||
.expiresAt(expiresAt) |
||||
.claim(IdTokenClaimNames.AZP, audience); |
||||
if (StringUtils.hasText(nonce)) { |
||||
builder.claim(IdTokenClaimNames.NONCE, nonce); |
||||
} |
||||
|
||||
// TODO Add 'auth_time' claim
|
||||
|
||||
JwtClaimsSet jwtClaimsSet = builder.build(); |
||||
|
||||
return jwtEncoder.encode(joseHeader, jwtClaimsSet); |
||||
} |
||||
|
||||
static OAuth2RefreshToken issueRefreshToken(Duration tokenTimeToLive) { |
||||
Instant issuedAt = Instant.now(); |
||||
Instant expiresAt = issuedAt.plus(tokenTimeToLive); |
||||
|
||||
return new OAuth2RefreshToken2(TOKEN_GENERATOR.generateKey(), issuedAt, expiresAt); |
||||
} |
||||
} |
||||
@ -0,0 +1,87 @@
@@ -0,0 +1,87 @@
|
||||
/* |
||||
* Copyright 2020-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.server.authorization.token; |
||||
|
||||
import java.util.Map; |
||||
import java.util.function.Consumer; |
||||
|
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.security.oauth2.core.context.Context; |
||||
import org.springframework.security.oauth2.jwt.JoseHeader; |
||||
import org.springframework.security.oauth2.jwt.JwtClaimsSet; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* @author Joe Grandja |
||||
* @since 0.1.0 |
||||
* @see OAuth2TokenContext |
||||
* @see JoseHeader.Builder |
||||
* @see JwtClaimsSet.Builder |
||||
*/ |
||||
public final class JwtEncodingContext implements OAuth2TokenContext { |
||||
private final Context context; |
||||
|
||||
private JwtEncodingContext(Map<Object, Object> context) { |
||||
this.context = Context.of(context); |
||||
} |
||||
|
||||
@Nullable |
||||
@Override |
||||
public <V> V get(Object key) { |
||||
return this.context.get(key); |
||||
} |
||||
|
||||
@Override |
||||
public boolean hasKey(Object key) { |
||||
return this.context.hasKey(key); |
||||
} |
||||
|
||||
public JoseHeader.Builder getHeaders() { |
||||
return get(JoseHeader.Builder.class); |
||||
} |
||||
|
||||
public JwtClaimsSet.Builder getClaims() { |
||||
return get(JwtClaimsSet.Builder.class); |
||||
} |
||||
|
||||
public static Builder with(JoseHeader.Builder headersBuilder, JwtClaimsSet.Builder claimsBuilder) { |
||||
return new Builder(headersBuilder, claimsBuilder); |
||||
} |
||||
|
||||
public static final class Builder extends AbstractBuilder<JwtEncodingContext, Builder> { |
||||
|
||||
private Builder(JoseHeader.Builder headersBuilder, JwtClaimsSet.Builder claimsBuilder) { |
||||
Assert.notNull(headersBuilder, "headersBuilder cannot be null"); |
||||
Assert.notNull(claimsBuilder, "claimsBuilder cannot be null"); |
||||
put(JoseHeader.Builder.class, headersBuilder); |
||||
put(JwtClaimsSet.Builder.class, claimsBuilder); |
||||
} |
||||
|
||||
public Builder headers(Consumer<JoseHeader.Builder> headersConsumer) { |
||||
headersConsumer.accept(get(JoseHeader.Builder.class)); |
||||
return this; |
||||
} |
||||
|
||||
public Builder claims(Consumer<JwtClaimsSet.Builder> claimsConsumer) { |
||||
claimsConsumer.accept(get(JwtClaimsSet.Builder.class)); |
||||
return this; |
||||
} |
||||
|
||||
public JwtEncodingContext build() { |
||||
return new JwtEncodingContext(this.context); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,119 @@
@@ -0,0 +1,119 @@
|
||||
/* |
||||
* Copyright 2020-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.server.authorization.token; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.function.Consumer; |
||||
|
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType; |
||||
import org.springframework.security.oauth2.core.context.Context; |
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; |
||||
import org.springframework.security.oauth2.server.authorization.TokenType; |
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* @author Joe Grandja |
||||
* @since 0.1.0 |
||||
* @see Context |
||||
*/ |
||||
public interface OAuth2TokenContext extends Context { |
||||
|
||||
default RegisteredClient getRegisteredClient() { |
||||
return get(RegisteredClient.class); |
||||
} |
||||
|
||||
default <T extends Authentication> T getPrincipal() { |
||||
return get(AbstractBuilder.PRINCIPAL_AUTHENTICATION_KEY); |
||||
} |
||||
|
||||
@Nullable |
||||
default OAuth2Authorization getAuthorization() { |
||||
return get(OAuth2Authorization.class); |
||||
} |
||||
|
||||
default TokenType getTokenType() { |
||||
return get(TokenType.class); |
||||
} |
||||
|
||||
default AuthorizationGrantType getAuthorizationGrantType() { |
||||
return get(AuthorizationGrantType.class); |
||||
} |
||||
|
||||
default <T extends Authentication> T getAuthorizationGrant() { |
||||
return get(AbstractBuilder.AUTHORIZATION_GRANT_AUTHENTICATION_KEY); |
||||
} |
||||
|
||||
abstract class AbstractBuilder<T extends OAuth2TokenContext, B extends AbstractBuilder<T, B>> { |
||||
private static final String PRINCIPAL_AUTHENTICATION_KEY = |
||||
Authentication.class.getName().concat(".PRINCIPAL"); |
||||
private static final String AUTHORIZATION_GRANT_AUTHENTICATION_KEY = |
||||
Authentication.class.getName().concat(".AUTHORIZATION_GRANT"); |
||||
protected final Map<Object, Object> context = new HashMap<>(); |
||||
|
||||
public B registeredClient(RegisteredClient registeredClient) { |
||||
return put(RegisteredClient.class, registeredClient); |
||||
} |
||||
|
||||
public B principal(Authentication principal) { |
||||
return put(PRINCIPAL_AUTHENTICATION_KEY, principal); |
||||
} |
||||
|
||||
public B authorization(OAuth2Authorization authorization) { |
||||
return put(OAuth2Authorization.class, authorization); |
||||
} |
||||
|
||||
public B tokenType(TokenType tokenType) { |
||||
return put(TokenType.class, tokenType); |
||||
} |
||||
|
||||
public B authorizationGrantType(AuthorizationGrantType authorizationGrantType) { |
||||
return put(AuthorizationGrantType.class, authorizationGrantType); |
||||
} |
||||
|
||||
public B authorizationGrant(Authentication authorizationGrant) { |
||||
return put(AUTHORIZATION_GRANT_AUTHENTICATION_KEY, authorizationGrant); |
||||
} |
||||
|
||||
public B put(Object key, Object value) { |
||||
Assert.notNull(key, "key cannot be null"); |
||||
Assert.notNull(value, "value cannot be null"); |
||||
this.context.put(key, value); |
||||
return getThis(); |
||||
} |
||||
|
||||
public B context(Consumer<Map<Object, Object>> contextConsumer) { |
||||
contextConsumer.accept(this.context); |
||||
return getThis(); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
protected <V> V get(Object key) { |
||||
return (V) this.context.get(key); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
protected B getThis() { |
||||
return (B) this; |
||||
} |
||||
|
||||
public abstract T build(); |
||||
|
||||
} |
||||
} |
||||
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
/* |
||||
* Copyright 2020-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.server.authorization.token; |
||||
|
||||
/** |
||||
* @author Joe Grandja |
||||
* @since 0.1.0 |
||||
* @see OAuth2TokenContext |
||||
*/ |
||||
@FunctionalInterface |
||||
public interface OAuth2TokenCustomizer<C extends OAuth2TokenContext> { |
||||
|
||||
void customize(C context); |
||||
|
||||
} |
||||
@ -0,0 +1,118 @@
@@ -0,0 +1,118 @@
|
||||
/* |
||||
* Copyright 2020-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.server.authorization.token; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType; |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; |
||||
import org.springframework.security.oauth2.jwt.JoseHeader; |
||||
import org.springframework.security.oauth2.jwt.JwtClaimsSet; |
||||
import org.springframework.security.oauth2.jwt.TestJoseHeaders; |
||||
import org.springframework.security.oauth2.jwt.TestJwtClaimsSets; |
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; |
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationAttributeNames; |
||||
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations; |
||||
import org.springframework.security.oauth2.server.authorization.TokenType; |
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken; |
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken; |
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; |
||||
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
|
||||
/** |
||||
* Tests for {@link JwtEncodingContext}. |
||||
* |
||||
* @author Joe Grandja |
||||
*/ |
||||
public class JwtEncodingContextTests { |
||||
|
||||
@Test |
||||
public void withWhenHeadersNullThenThrowIllegalArgumentException() { |
||||
assertThatThrownBy(() -> JwtEncodingContext.with(null, TestJwtClaimsSets.jwtClaimsSet())) |
||||
.isInstanceOf(IllegalArgumentException.class) |
||||
.hasMessage("headersBuilder cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void withWhenClaimsNullThenThrowIllegalArgumentException() { |
||||
assertThatThrownBy(() -> JwtEncodingContext.with(TestJoseHeaders.joseHeader(), null)) |
||||
.isInstanceOf(IllegalArgumentException.class) |
||||
.hasMessage("claimsBuilder cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void setWhenValueNullThenThrowIllegalArgumentException() { |
||||
JwtEncodingContext.Builder builder = JwtEncodingContext |
||||
.with(TestJoseHeaders.joseHeader(), TestJwtClaimsSets.jwtClaimsSet()); |
||||
assertThatThrownBy(() -> builder.registeredClient(null)) |
||||
.isInstanceOf(IllegalArgumentException.class); |
||||
assertThatThrownBy(() -> builder.principal(null)) |
||||
.isInstanceOf(IllegalArgumentException.class); |
||||
assertThatThrownBy(() -> builder.authorization(null)) |
||||
.isInstanceOf(IllegalArgumentException.class); |
||||
assertThatThrownBy(() -> builder.tokenType(null)) |
||||
.isInstanceOf(IllegalArgumentException.class); |
||||
assertThatThrownBy(() -> builder.authorizationGrantType(null)) |
||||
.isInstanceOf(IllegalArgumentException.class); |
||||
assertThatThrownBy(() -> builder.authorizationGrant(null)) |
||||
.isInstanceOf(IllegalArgumentException.class); |
||||
assertThatThrownBy(() -> builder.put(null, "")) |
||||
.isInstanceOf(IllegalArgumentException.class); |
||||
} |
||||
|
||||
@Test |
||||
public void buildWhenAllValuesProvidedThenAllValuesAreSet() { |
||||
JoseHeader.Builder headers = TestJoseHeaders.joseHeader(); |
||||
JwtClaimsSet.Builder claims = TestJwtClaimsSets.jwtClaimsSet(); |
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); |
||||
TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "password"); |
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization().build(); |
||||
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient); |
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( |
||||
OAuth2AuthorizationAttributeNames.AUTHORIZATION_REQUEST); |
||||
OAuth2AuthorizationCodeAuthenticationToken authorizationGrant = |
||||
new OAuth2AuthorizationCodeAuthenticationToken( |
||||
"code", clientPrincipal, authorizationRequest.getRedirectUri(), null); |
||||
|
||||
JwtEncodingContext context = JwtEncodingContext.with(headers, claims) |
||||
.registeredClient(registeredClient) |
||||
.principal(principal) |
||||
.authorization(authorization) |
||||
.tokenType(TokenType.ACCESS_TOKEN) |
||||
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) |
||||
.authorizationGrant(authorizationGrant) |
||||
.put("custom-key-1", "custom-value-1") |
||||
.context(ctx -> ctx.put("custom-key-2", "custom-value-2")) |
||||
.build(); |
||||
|
||||
assertThat(context.getHeaders()).isEqualTo(headers); |
||||
assertThat(context.getClaims()).isEqualTo(claims); |
||||
assertThat(context.getRegisteredClient()).isEqualTo(registeredClient); |
||||
assertThat(context.<Authentication>getPrincipal()).isEqualTo(principal); |
||||
assertThat(context.getAuthorization()).isEqualTo(authorization); |
||||
assertThat(context.getTokenType()).isEqualTo(TokenType.ACCESS_TOKEN); |
||||
assertThat(context.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); |
||||
assertThat(context.<Authentication>getAuthorizationGrant()).isEqualTo(authorizationGrant); |
||||
assertThat(context.<String>get("custom-key-1")).isEqualTo("custom-value-1"); |
||||
assertThat(context.<String>get("custom-key-2")).isEqualTo("custom-value-2"); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue