diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 01336261bf..8dc2bdd75c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,7 +12,7 @@ unboundid-ldapsdk = "6.0.8" bouncycastle = "1.70" kotlin = "1.8.21" bcpkix = "1.70" - +jpaseto="0.7.0" micronaut-test = "4.0.0-M3" micronaut-multitenancy = "5.0.0-M2" micronaut-reactor = "3.0.0-M1" @@ -33,6 +33,11 @@ micronaut-session = { module = "io.micronaut.session:micronaut-session-bom", ver micronaut-views = { module = "io.micronaut.views:micronaut-views-bom", version.ref = "micronaut-views" } micronaut-validation = { module = "io.micronaut.validation:micronaut-validation-bom", version.ref = "micronaut-validation" } +managed-jpaseto-api = { module = "dev.paseto:jpaseto-api", version.ref = "jpaseto" } +managed-jpaseto-bouncy-castle = { module = "dev.paseto:jpaseto-bouncy-castle", version.ref = "jpaseto" } +managed-jpaseto-impl = { module = "dev.paseto:jpaseto-impl", version.ref = "jpaseto" } +managed-jpaseto-jackson = { module = "dev.paseto:jpaseto-jackson", version.ref = "jpaseto" } + managed-nimbus-jose-jwt = { module = "com.nimbusds:nimbus-jose-jwt", version.ref = "managed-nimbus-jose-jwt" } geb-spock = { module = "org.gebish:geb-spock", version.ref = "geb" } junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api" } diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/JwtTokenGenerator.java b/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/JwtTokenGenerator.java index 45663078e8..9fca64d25c 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/JwtTokenGenerator.java +++ b/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/JwtTokenGenerator.java @@ -21,9 +21,9 @@ import com.nimbusds.jwt.PlainJWT; import io.micronaut.core.annotation.Nullable; import io.micronaut.security.authentication.Authentication; +import io.micronaut.security.token.claims.ClaimsGenerator; import io.micronaut.security.token.generator.TokenGenerator; import io.micronaut.security.token.jwt.encryption.EncryptionConfiguration; -import io.micronaut.security.token.jwt.generator.claims.ClaimsGenerator; import io.micronaut.security.token.jwt.signature.SignatureGeneratorConfiguration; import jakarta.inject.Named; import jakarta.inject.Singleton; diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/JWTClaimsSetGenerator.java b/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/JWTClaimsSetGenerator.java index 90c0752b9d..b83f834c92 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/JWTClaimsSetGenerator.java +++ b/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/JWTClaimsSetGenerator.java @@ -20,6 +20,10 @@ import io.micronaut.core.annotation.Nullable; import io.micronaut.runtime.ApplicationConfiguration; import io.micronaut.security.authentication.Authentication; +import io.micronaut.security.token.Claims; +import io.micronaut.security.token.claims.ClaimsAudienceProvider; +import io.micronaut.security.token.claims.ClaimsGenerator; +import io.micronaut.security.token.claims.JtiGenerator; import io.micronaut.security.token.config.TokenConfiguration; import jakarta.inject.Singleton; import java.time.Instant; @@ -43,7 +47,7 @@ public class JWTClaimsSetGenerator implements ClaimsGenerator { private static final String ROLES_KEY = "rolesKey"; private final TokenConfiguration tokenConfiguration; - private final JwtIdGenerator jwtIdGenerator; + private final JtiGenerator jwtIdGenerator; private final ClaimsAudienceProvider claimsAudienceProvider; private final String appName; @@ -54,7 +58,7 @@ public class JWTClaimsSetGenerator implements ClaimsGenerator { * @param applicationConfiguration The application configuration */ public JWTClaimsSetGenerator(TokenConfiguration tokenConfiguration, - @Nullable JwtIdGenerator jwtIdGenerator, + @Nullable JtiGenerator jwtIdGenerator, @Nullable ClaimsAudienceProvider claimsAudienceProvider, @Nullable ApplicationConfiguration applicationConfiguration) { this.tokenConfiguration = tokenConfiguration; @@ -189,7 +193,7 @@ protected void populateWithAuthentication(JWTClaimsSet.Builder builder, Authenti @Override public Map generateClaimsSet(Map oldClaims, Integer expiration) { JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder(); - List excludedClaims = Arrays.asList(JwtClaims.EXPIRATION_TIME, JwtClaims.ISSUED_AT, JwtClaims.NOT_BEFORE); + List excludedClaims = Arrays.asList(Claims.EXPIRATION_TIME, Claims.ISSUED_AT, Claims.NOT_BEFORE); for (String k : oldClaims.keySet() .stream() .filter(p -> !excludedClaims.contains(p)) diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/JwtClaims.java b/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/JwtClaims.java deleted file mode 100644 index 987210aba0..0000000000 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/JwtClaims.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017-2023 original 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 io.micronaut.security.token.jwt.generator.claims; - -import io.micronaut.security.token.Claims; -import java.util.Arrays; -import java.util.List; - -/** - * @see Registered Claims Names - */ -public interface JwtClaims extends Claims { - String ISSUER = "iss"; - - String SUBJECT = "sub"; - - String EXPIRATION_TIME = "exp"; - - String NOT_BEFORE = "nbf"; - - String ISSUED_AT = "iat"; - - String JWT_ID = "jti"; - - String AUDIENCE = "aud"; - - List ALL_CLAIMS = Arrays.asList(ISSUER, SUBJECT, EXPIRATION_TIME, NOT_BEFORE, ISSUED_AT, JWT_ID, AUDIENCE); - -} diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/JwtClaimsSetAdapter.java b/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/JwtClaimsSetAdapter.java index 07542c979f..b84b1d16ef 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/JwtClaimsSetAdapter.java +++ b/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/JwtClaimsSetAdapter.java @@ -18,15 +18,17 @@ import com.nimbusds.jwt.JWTClaimsSet; import io.micronaut.core.annotation.NonNull; import io.micronaut.core.annotation.Nullable; +import io.micronaut.security.token.Claims; + import java.util.Set; /** - * Adapts from {@link JWTClaimsSet} to {@link JwtClaims}. + * Adapts from {@link JWTClaimsSet} to {@link Claims}. * * @author Sergio del Amo * @since 1.1.0 */ -public class JwtClaimsSetAdapter implements JwtClaims { +public class JwtClaimsSetAdapter implements Claims { private final JWTClaimsSet jwtClaimsSet; diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/AudienceJwtClaimsValidator.java b/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/AudienceJwtClaimsValidator.java index dca79efa1c..7952d82fc5 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/AudienceJwtClaimsValidator.java +++ b/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/AudienceJwtClaimsValidator.java @@ -15,14 +15,14 @@ */ package io.micronaut.security.token.jwt.validator; +import java.util.List; +import io.micronaut.security.token.Claims; +import jakarta.inject.Singleton; import com.nimbusds.jwt.JWTClaimsSet; import io.micronaut.context.annotation.Requires; import io.micronaut.core.annotation.NonNull; import io.micronaut.core.annotation.Nullable; import io.micronaut.http.HttpRequest; -import io.micronaut.security.token.jwt.generator.claims.JwtClaims; -import jakarta.inject.Singleton; -import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -80,7 +80,7 @@ protected boolean validate(JWTClaimsSet claimsSet) { } @Override - public boolean validate(@NonNull JwtClaims claims, + public boolean validate(@NonNull Claims claims, @Nullable HttpRequest request) { return validate(JWTClaimsSetUtils.jwtClaimsSetFromClaims(claims)); } diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/DefaultJwtAuthenticationFactory.java b/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/DefaultJwtAuthenticationFactory.java index c05a683c82..f0831df267 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/DefaultJwtAuthenticationFactory.java +++ b/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/DefaultJwtAuthenticationFactory.java @@ -18,14 +18,15 @@ import com.nimbusds.jwt.JWT; import com.nimbusds.jwt.JWTClaimsSet; import io.micronaut.security.authentication.Authentication; +import io.micronaut.security.token.AbstractTokenAuthenticationFactory; +import io.micronaut.security.token.MapClaims; import io.micronaut.security.token.RolesFinder; import io.micronaut.security.token.config.TokenConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import jakarta.inject.Singleton; import java.text.ParseException; -import java.util.Map; import java.util.Optional; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Extracts the JWT claims and uses the {@link AuthenticationJWTClaimsSetAdapter} to construction an {@link Authentication} object. @@ -34,17 +35,18 @@ * @since 1.1.0 */ @Singleton -public class DefaultJwtAuthenticationFactory implements JwtAuthenticationFactory { +public class DefaultJwtAuthenticationFactory extends AbstractTokenAuthenticationFactory implements JwtAuthenticationFactory { private static final Logger LOG = LoggerFactory.getLogger(DefaultJwtAuthenticationFactory.class); - private final TokenConfiguration tokenConfiguration; - private final RolesFinder rolesFinder; - + /** + * + * @param tokenConfiguration Token Configuration + * @param rolesFinder Utility to retrieve roles from token claims + */ public DefaultJwtAuthenticationFactory(TokenConfiguration tokenConfiguration, RolesFinder rolesFinder) { - this.tokenConfiguration = tokenConfiguration; - this.rolesFinder = rolesFinder; + super(tokenConfiguration, rolesFinder); } @Override @@ -54,11 +56,7 @@ public Optional createAuthentication(JWT token) { if (claimSet == null) { return Optional.empty(); } - Map attributes = claimSet.getClaims(); - return usernameForClaims(claimSet).map(username -> - Authentication.build(username, - rolesFinder.resolveRoles(attributes), - attributes)); + return createAuthentication(claimSet.getClaims()); } catch (ParseException e) { if (LOG.isErrorEnabled()) { LOG.error("ParseException creating authentication", e); @@ -71,13 +69,11 @@ public Optional createAuthentication(JWT token) { * * @param claimSet JWT Claims * @return the username defined by {@link TokenConfiguration#getNameKey()} ()} or the sub claim. + * @deprecated Use {@link AbstractTokenAuthenticationFactory#usernameForClaims(io.micronaut.security.token.Claims)} instead. * @throws ParseException might be thrown parsing claims */ + @Deprecated protected Optional usernameForClaims(JWTClaimsSet claimSet) throws ParseException { - String username = claimSet.getStringClaim(tokenConfiguration.getNameKey()); - if (username == null) { - return Optional.ofNullable(claimSet.getSubject()); - } - return Optional.of(username); + return super.usernameForClaims(new MapClaims(claimSet.getClaims())); } } diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/ExpirationJwtClaimsValidator.java b/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/ExpirationJwtClaimsValidator.java index 55231250b2..aa1282cdd6 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/ExpirationJwtClaimsValidator.java +++ b/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/ExpirationJwtClaimsValidator.java @@ -21,11 +21,11 @@ import io.micronaut.core.annotation.Nullable; import io.micronaut.core.util.StringUtils; import io.micronaut.http.HttpRequest; -import io.micronaut.security.token.jwt.generator.claims.JwtClaims; -import jakarta.inject.Singleton; -import java.util.Date; +import io.micronaut.security.token.Claims; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jakarta.inject.Singleton; +import java.util.Date; /** * Validate JWT is not expired. @@ -59,7 +59,7 @@ protected boolean validate(@NonNull JWTClaimsSet claimsSet) { } @Override - public boolean validate(@NonNull JwtClaims claims, @Nullable HttpRequest request) { + public boolean validate(@NonNull Claims claims, @Nullable HttpRequest request) { return validate(JWTClaimsSetUtils.jwtClaimsSetFromClaims(claims)); } } diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/IssuerJwtClaimsValidator.java b/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/IssuerJwtClaimsValidator.java index 61acc8536b..145239106d 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/IssuerJwtClaimsValidator.java +++ b/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/IssuerJwtClaimsValidator.java @@ -19,7 +19,7 @@ import io.micronaut.core.annotation.NonNull; import io.micronaut.core.annotation.Nullable; import io.micronaut.http.HttpRequest; -import io.micronaut.security.token.jwt.generator.claims.JwtClaims; +import io.micronaut.security.token.Claims; import jakarta.inject.Singleton; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,11 +54,11 @@ public IssuerJwtClaimsValidator(JwtClaimsValidatorConfiguration jwtClaimsValidat } @Override - public boolean validate(@NonNull JwtClaims claims, @Nullable HttpRequest request) { + public boolean validate(@NonNull Claims claims, @Nullable HttpRequest request) { if (expectedIssuer == null) { return true; } - Object issuerObject = claims.get(JwtClaims.ISSUER); + Object issuerObject = claims.get(Claims.ISSUER); if (issuerObject == null) { if (LOG.isTraceEnabled()) { LOG.trace("Expected JWT issuer claim of '{}', but the token did not include an issuer.", expectedIssuer); diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/JWTClaimsSetUtils.java b/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/JWTClaimsSetUtils.java index 48644ca53f..3faa6fadf0 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/JWTClaimsSetUtils.java +++ b/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/JWTClaimsSetUtils.java @@ -16,7 +16,7 @@ package io.micronaut.security.token.jwt.validator; import com.nimbusds.jwt.JWTClaimsSet; -import io.micronaut.security.token.jwt.generator.claims.JwtClaims; +import io.micronaut.security.token.Claims; /** * Utils class to instantiate a JWClaimsSet give a map of claims. @@ -34,7 +34,7 @@ private JWTClaimsSetUtils() { * @param claims JWT claims * @return A JWTClaimsSet */ - public static JWTClaimsSet jwtClaimsSetFromClaims(JwtClaims claims) { + public static JWTClaimsSet jwtClaimsSetFromClaims(Claims claims) { JWTClaimsSet.Builder claimsSetBuilder = new JWTClaimsSet.Builder(); for (String k : claims.names()) { claimsSetBuilder.claim(k, claims.get(k)); diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/JwtClaimsValidator.java b/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/JwtClaimsValidator.java index d3b4c23ca9..1ac0f4e018 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/JwtClaimsValidator.java +++ b/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/JwtClaimsValidator.java @@ -18,7 +18,7 @@ import io.micronaut.core.annotation.NonNull; import io.micronaut.core.annotation.Nullable; import io.micronaut.http.HttpRequest; -import io.micronaut.security.token.jwt.generator.claims.JwtClaims; +import io.micronaut.security.token.Claims; /** * Provides a contract to create custom JWT claims validations. @@ -33,5 +33,5 @@ public interface JwtClaimsValidator { * @param request HTTP request * @return whether the JWT claims pass validation. */ - boolean validate(@NonNull JwtClaims claims, @Nullable HttpRequest request); + boolean validate(@NonNull Claims claims, @Nullable HttpRequest request); } diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/JwtValidator.java b/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/JwtValidator.java index 2f3e609a22..06417e2e34 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/JwtValidator.java +++ b/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/JwtValidator.java @@ -28,8 +28,8 @@ import io.micronaut.core.annotation.NonNull; import io.micronaut.core.annotation.Nullable; import io.micronaut.http.HttpRequest; +import io.micronaut.security.token.Claims; import io.micronaut.security.token.jwt.encryption.EncryptionConfiguration; -import io.micronaut.security.token.jwt.generator.claims.JwtClaims; import io.micronaut.security.token.jwt.generator.claims.JwtClaimsSetAdapter; import io.micronaut.security.token.jwt.signature.SignatureConfiguration; import io.micronaut.security.token.jwt.signature.jwks.JwksCache; @@ -125,7 +125,7 @@ public Optional validate(@NonNull JWT token, @Nullable HttpRequest reque } else { return validationResult.filter(jwt -> { try { - JwtClaims claims = new JwtClaimsSetAdapter(jwt.getJWTClaimsSet()); + Claims claims = new JwtClaimsSetAdapter(jwt.getJWTClaimsSet()); return claimsValidators.stream().allMatch(validator -> validator.validate(claims, request)); } catch (ParseException e) { if (LOG.isErrorEnabled()) { diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/NotBeforeJwtClaimsValidator.java b/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/NotBeforeJwtClaimsValidator.java index 7c8b72c239..e617d442c0 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/NotBeforeJwtClaimsValidator.java +++ b/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/NotBeforeJwtClaimsValidator.java @@ -21,12 +21,13 @@ import io.micronaut.core.annotation.Nullable; import io.micronaut.core.util.StringUtils; import io.micronaut.http.HttpRequest; -import io.micronaut.security.token.jwt.generator.claims.JwtClaims; +import io.micronaut.security.token.Claims; import jakarta.inject.Singleton; -import java.util.Date; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Date; + /** * Validate current time is not before the not-before claim of a JWT token. * @@ -72,7 +73,7 @@ protected boolean validate(@NonNull JWTClaimsSet claimsSet) { * @return true if the not-before claim denotes a date before now */ @Override - public boolean validate(@NonNull JwtClaims claims, @Nullable HttpRequest request) { + public boolean validate(@NonNull Claims claims, @Nullable HttpRequest request) { return validate(JWTClaimsSetUtils.jwtClaimsSetFromClaims(claims)); } diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/SubjectNotNullJwtClaimsValidator.java b/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/SubjectNotNullJwtClaimsValidator.java index 5a1e996b41..9a00ac1238 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/SubjectNotNullJwtClaimsValidator.java +++ b/security-jwt/src/main/java/io/micronaut/security/token/jwt/validator/SubjectNotNullJwtClaimsValidator.java @@ -21,7 +21,7 @@ import io.micronaut.core.annotation.Nullable; import io.micronaut.core.util.StringUtils; import io.micronaut.http.HttpRequest; -import io.micronaut.security.token.jwt.generator.claims.JwtClaims; +import io.micronaut.security.token.Claims; import jakarta.inject.Singleton; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,7 +53,7 @@ public boolean validate(JWTClaimsSet claimsSet) { } @Override - public boolean validate(@NonNull JwtClaims claims, @Nullable HttpRequest request) { + public boolean validate(@NonNull Claims claims, @Nullable HttpRequest request) { return validate(JWTClaimsSetUtils.jwtClaimsSetFromClaims(claims)); } } diff --git a/security-jwt/src/test/groovy/io/micronaut/docs/jwtclaimsoverride/JwtClaimsOverrideSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/docs/jwtclaimsoverride/JwtClaimsOverrideSpec.groovy index c288aed33a..e3f5cbf07c 100644 --- a/security-jwt/src/test/groovy/io/micronaut/docs/jwtclaimsoverride/JwtClaimsOverrideSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/docs/jwtclaimsoverride/JwtClaimsOverrideSpec.groovy @@ -8,7 +8,7 @@ import io.micronaut.http.MediaType import io.micronaut.security.authentication.Authentication import io.micronaut.security.authentication.UsernamePasswordCredentials import io.micronaut.security.testutils.EmbeddedServerSpecification -import io.micronaut.security.token.jwt.render.AccessRefreshToken +import io.micronaut.security.token.render.AccessRefreshToken import io.micronaut.security.token.jwt.validator.JwtTokenValidator import io.micronaut.security.token.validator.TokenValidator import reactor.core.publisher.Flux diff --git a/security-jwt/src/test/groovy/io/micronaut/security/endpoints/introspection/IntrospectionEndpointSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/endpoints/introspection/IntrospectionEndpointSpec.groovy index a9e7c513dc..d68531ceb6 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/endpoints/introspection/IntrospectionEndpointSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/endpoints/introspection/IntrospectionEndpointSpec.groovy @@ -10,7 +10,7 @@ import io.micronaut.security.authentication.UsernamePasswordCredentials import io.micronaut.security.testutils.EmbeddedServerSpecification import io.micronaut.security.testutils.authprovider.MockAuthenticationProvider import io.micronaut.security.testutils.authprovider.SuccessAuthenticationScenario -import io.micronaut.security.token.jwt.render.BearerAccessRefreshToken +import io.micronaut.security.token.render.BearerAccessRefreshToken import jakarta.inject.Singleton class IntrospectionEndpointSpec extends EmbeddedServerSpecification { diff --git a/security-jwt/src/test/groovy/io/micronaut/security/endpoints/introspection/RefreshTokenIntrospectionEndpointSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/endpoints/introspection/RefreshTokenIntrospectionEndpointSpec.groovy index 988ddc7cf0..f42868b96c 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/endpoints/introspection/RefreshTokenIntrospectionEndpointSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/endpoints/introspection/RefreshTokenIntrospectionEndpointSpec.groovy @@ -12,7 +12,7 @@ import io.micronaut.security.testutils.EmbeddedServerSpecification import io.micronaut.security.testutils.authprovider.MockAuthenticationProvider import io.micronaut.security.testutils.authprovider.SuccessAuthenticationScenario import io.micronaut.security.token.event.RefreshTokenGeneratedEvent -import io.micronaut.security.token.jwt.render.BearerAccessRefreshToken +import io.micronaut.security.token.render.BearerAccessRefreshToken import io.micronaut.security.token.refresh.RefreshTokenPersistence import jakarta.inject.Singleton import org.reactivestreams.Publisher diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/AuthorizationUtils.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/AuthorizationUtils.groovy index d0a39711ed..8c19b65ffc 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/AuthorizationUtils.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/AuthorizationUtils.groovy @@ -5,7 +5,7 @@ import io.micronaut.http.HttpRequest import io.micronaut.http.HttpResponse import io.micronaut.http.client.BlockingHttpClient import io.micronaut.security.authentication.UsernamePasswordCredentials -import io.micronaut.security.token.jwt.render.BearerAccessRefreshToken +import io.micronaut.security.token.render.BearerAccessRefreshToken @CompileStatic trait AuthorizationUtils { diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/SecurityJwtBeansWithSecurityDisabledSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/SecurityJwtBeansWithSecurityDisabledSpec.groovy index 9d7f232b74..45405f5f9a 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/SecurityJwtBeansWithSecurityDisabledSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/SecurityJwtBeansWithSecurityDisabledSpec.groovy @@ -3,33 +3,30 @@ package io.micronaut.security.token.jwt import io.micronaut.context.ApplicationContext import io.micronaut.context.exceptions.NoSuchBeanException import io.micronaut.runtime.server.EmbeddedServer -import io.micronaut.security.token.jwt.bearer.AccessRefreshTokenLoginHandler -import io.micronaut.security.token.jwt.bearer.BearerTokenConfigurationProperties -import io.micronaut.security.token.jwt.bearer.BearerTokenReader -import io.micronaut.security.token.jwt.config.JwtConfigurationProperties +import io.micronaut.security.endpoints.OauthController +import io.micronaut.security.endpoints.OauthControllerConfigurationProperties +import io.micronaut.security.token.bearer.AccessRefreshTokenLoginHandler +import io.micronaut.security.token.bearer.BearerTokenConfigurationProperties +import io.micronaut.security.token.bearer.BearerTokenReader +import io.micronaut.security.token.cookie.TokenCookieClearerLogoutHandler +import io.micronaut.security.token.cookie.TokenCookieLoginHandler +import io.micronaut.security.token.generator.AccessRefreshTokenGenerator +import io.micronaut.security.token.generator.AccessTokenConfigurationProperties import io.micronaut.security.token.jwt.converters.EncryptionMethodConverter import io.micronaut.security.token.jwt.converters.JWEAlgorithmConverter import io.micronaut.security.token.jwt.converters.JWSAlgorithmConverter -import io.micronaut.security.token.jwt.cookie.JwtCookieClearerLogoutHandler -import io.micronaut.security.token.jwt.cookie.JwtCookieConfigurationProperties -import io.micronaut.security.token.jwt.cookie.JwtCookieLoginHandler -import io.micronaut.security.token.jwt.cookie.JwtCookieTokenReader import io.micronaut.security.token.jwt.encryption.ec.ECEncryptionFactory import io.micronaut.security.token.jwt.encryption.rsa.RSAEncryptionFactory import io.micronaut.security.token.jwt.encryption.secret.SecretEncryptionConfiguration import io.micronaut.security.token.jwt.encryption.secret.SecretEncryptionFactory -import io.micronaut.security.token.jwt.endpoints.OauthController -import io.micronaut.security.token.jwt.endpoints.OauthControllerConfigurationProperties -import io.micronaut.security.token.jwt.generator.AccessRefreshTokenGenerator -import io.micronaut.security.token.jwt.generator.AccessTokenConfigurationProperties import io.micronaut.security.token.jwt.generator.JwtTokenGenerator import io.micronaut.security.token.jwt.generator.claims.JWTClaimsSetGenerator -import io.micronaut.security.token.jwt.render.BearerTokenRenderer import io.micronaut.security.token.jwt.signature.ec.ECSignatureFactory import io.micronaut.security.token.jwt.signature.ec.ECSignatureGeneratorFactory import io.micronaut.security.token.jwt.signature.rsa.RSASignatureFactory import io.micronaut.security.token.jwt.signature.rsa.RSASignatureGeneratorFactory import io.micronaut.security.token.jwt.validator.JwtTokenValidator +import io.micronaut.security.token.render.BearerTokenRenderer import spock.lang.AutoCleanup import spock.lang.Shared import spock.lang.Specification @@ -58,14 +55,11 @@ class SecurityJwtBeansWithSecurityDisabledSpec extends Specification { AccessRefreshTokenLoginHandler, BearerTokenConfigurationProperties, BearerTokenReader, - JwtConfigurationProperties, EncryptionMethodConverter, JWEAlgorithmConverter, JWSAlgorithmConverter, - JwtCookieClearerLogoutHandler, - JwtCookieConfigurationProperties, - JwtCookieLoginHandler, - JwtCookieTokenReader, + TokenCookieClearerLogoutHandler, + TokenCookieLoginHandler, ECEncryptionFactory, RSAEncryptionFactory, SecretEncryptionConfiguration, diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/SecurityJwtBeansWithSecurityJwtDisabledSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/SecurityJwtBeansWithSecurityJwtDisabledSpec.groovy index 7e985affca..d042f28886 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/SecurityJwtBeansWithSecurityJwtDisabledSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/SecurityJwtBeansWithSecurityJwtDisabledSpec.groovy @@ -3,33 +3,31 @@ package io.micronaut.security.token.jwt import io.micronaut.context.ApplicationContext import io.micronaut.context.exceptions.NoSuchBeanException import io.micronaut.runtime.server.EmbeddedServer -import io.micronaut.security.token.jwt.bearer.AccessRefreshTokenLoginHandler -import io.micronaut.security.token.jwt.bearer.BearerTokenConfigurationProperties -import io.micronaut.security.token.jwt.bearer.BearerTokenReader +import io.micronaut.security.endpoints.OauthController +import io.micronaut.security.endpoints.OauthControllerConfigurationProperties +import io.micronaut.security.token.bearer.AccessRefreshTokenLoginHandler +import io.micronaut.security.token.bearer.BearerTokenConfigurationProperties +import io.micronaut.security.token.bearer.BearerTokenReader +import io.micronaut.security.token.cookie.TokenCookieClearerLogoutHandler +import io.micronaut.security.token.cookie.TokenCookieLoginHandler +import io.micronaut.security.token.generator.AccessRefreshTokenGenerator +import io.micronaut.security.token.generator.AccessTokenConfigurationProperties import io.micronaut.security.token.jwt.config.JwtConfigurationProperties import io.micronaut.security.token.jwt.converters.EncryptionMethodConverter import io.micronaut.security.token.jwt.converters.JWEAlgorithmConverter import io.micronaut.security.token.jwt.converters.JWSAlgorithmConverter -import io.micronaut.security.token.jwt.cookie.JwtCookieClearerLogoutHandler -import io.micronaut.security.token.jwt.cookie.JwtCookieConfigurationProperties -import io.micronaut.security.token.jwt.cookie.JwtCookieLoginHandler -import io.micronaut.security.token.jwt.cookie.JwtCookieTokenReader import io.micronaut.security.token.jwt.encryption.ec.ECEncryptionFactory import io.micronaut.security.token.jwt.encryption.rsa.RSAEncryptionFactory import io.micronaut.security.token.jwt.encryption.secret.SecretEncryptionConfiguration import io.micronaut.security.token.jwt.encryption.secret.SecretEncryptionFactory -import io.micronaut.security.token.jwt.endpoints.OauthController -import io.micronaut.security.token.jwt.endpoints.OauthControllerConfigurationProperties -import io.micronaut.security.token.jwt.generator.AccessRefreshTokenGenerator -import io.micronaut.security.token.jwt.generator.AccessTokenConfigurationProperties import io.micronaut.security.token.jwt.generator.JwtTokenGenerator import io.micronaut.security.token.jwt.generator.claims.JWTClaimsSetGenerator -import io.micronaut.security.token.jwt.render.BearerTokenRenderer import io.micronaut.security.token.jwt.signature.ec.ECSignatureFactory import io.micronaut.security.token.jwt.signature.ec.ECSignatureGeneratorFactory import io.micronaut.security.token.jwt.signature.rsa.RSASignatureFactory import io.micronaut.security.token.jwt.signature.rsa.RSASignatureGeneratorFactory import io.micronaut.security.token.jwt.validator.JwtTokenValidator +import io.micronaut.security.token.render.BearerTokenRenderer import spock.lang.AutoCleanup import spock.lang.Shared import spock.lang.Specification @@ -50,33 +48,25 @@ class SecurityJwtBeansWithSecurityJwtDisabledSpec extends Specification { embeddedServer.applicationContext.getBean(clazz) then: - def e = thrown(NoSuchBeanException) + NoSuchBeanException e = thrown() e.message.contains('No bean of type ['+clazz.name+'] exists.') where: clazz << [ AccessRefreshTokenLoginHandler, - BearerTokenConfigurationProperties, - BearerTokenReader, JwtConfigurationProperties, EncryptionMethodConverter, JWEAlgorithmConverter, JWSAlgorithmConverter, - JwtCookieClearerLogoutHandler, - JwtCookieConfigurationProperties, - JwtCookieLoginHandler, - JwtCookieTokenReader, + TokenCookieClearerLogoutHandler, + TokenCookieLoginHandler, ECEncryptionFactory, RSAEncryptionFactory, SecretEncryptionConfiguration, SecretEncryptionFactory, OauthController, - OauthControllerConfigurationProperties, JWTClaimsSetGenerator, - AccessRefreshTokenGenerator, - AccessTokenConfigurationProperties, JwtTokenGenerator, - BearerTokenRenderer, ECSignatureFactory, ECSignatureGeneratorFactory, RSASignatureFactory, diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/accesstokenexpiration/AccessTokenExpirationSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/accesstokenexpiration/AccessTokenExpirationSpec.groovy index 2f2a823d52..0566f959b7 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/accesstokenexpiration/AccessTokenExpirationSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/accesstokenexpiration/AccessTokenExpirationSpec.groovy @@ -6,8 +6,8 @@ import io.micronaut.http.HttpResponse import io.micronaut.http.HttpStatus import io.micronaut.http.client.exceptions.HttpClientResponseException import io.micronaut.security.authentication.UsernamePasswordCredentials +import io.micronaut.security.token.render.BearerAccessRefreshToken import io.micronaut.security.testutils.EmbeddedServerSpecification -import io.micronaut.security.token.jwt.render.BearerAccessRefreshToken class AccessTokenExpirationSpec extends EmbeddedServerSpecification { @@ -21,7 +21,7 @@ class AccessTokenExpirationSpec extends EmbeddedServerSpecification { super.configuration + [ 'endpoints.beans.enabled': true, 'endpoints.beans.sensitive': true, - 'micronaut.security.token.jwt.generator.access-token.expiration': 5, + 'micronaut.security.token.generator.access-token.expiration': 5, 'micronaut.security.token.jwt.signatures.secret.generator.secret': 'pleaseChangeThisSecretForANewOne', 'micronaut.security.authentication' : 'bearer', ] diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/bearer/AccessRefreshTokenLoginHandlerValidSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/bearer/AccessRefreshTokenLoginHandlerValidSpec.groovy index 38d97ec5cc..93db4fbb0f 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/bearer/AccessRefreshTokenLoginHandlerValidSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/bearer/AccessRefreshTokenLoginHandlerValidSpec.groovy @@ -7,7 +7,7 @@ import io.micronaut.security.authentication.UsernamePasswordCredentials import io.micronaut.security.testutils.EmbeddedServerSpecification import io.micronaut.security.testutils.authprovider.MockAuthenticationProvider import io.micronaut.security.testutils.authprovider.SuccessAuthenticationScenario -import io.micronaut.security.token.jwt.render.BearerAccessRefreshToken +import io.micronaut.security.token.render.BearerAccessRefreshToken import jakarta.inject.Singleton class AccessRefreshTokenLoginHandlerValidSpec extends EmbeddedServerSpecification { diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/bearer/BearerEnabledSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/bearer/BearerEnabledSpec.groovy index a0ce86bc45..87fe26244b 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/bearer/BearerEnabledSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/bearer/BearerEnabledSpec.groovy @@ -2,6 +2,9 @@ package io.micronaut.security.token.jwt.bearer import io.micronaut.context.exceptions.NoSuchBeanException import io.micronaut.security.testutils.EmbeddedServerSpecification +import io.micronaut.security.token.bearer.AccessRefreshTokenLoginHandler +import io.micronaut.security.token.bearer.BearerTokenConfigurationProperties +import io.micronaut.security.token.bearer.BearerTokenReader import spock.lang.Unroll class BearerEnabledSpec extends EmbeddedServerSpecification { @@ -9,7 +12,7 @@ class BearerEnabledSpec extends EmbeddedServerSpecification { @Override Map getConfiguration() { super.configuration + [ - 'micronaut.security.token.jwt.bearer.enabled': false, + 'micronaut.security.token.bearer.enabled': false, ] } diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/bearer/BearerTokenReaderSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/bearer/BearerTokenReaderSpec.groovy index 4f8c6d2d24..fde4dd38dd 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/bearer/BearerTokenReaderSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/bearer/BearerTokenReaderSpec.groovy @@ -2,6 +2,8 @@ package io.micronaut.security.token.jwt.bearer import io.micronaut.http.HttpMethod import io.micronaut.http.HttpRequest +import io.micronaut.security.token.bearer.BearerTokenConfiguration +import io.micronaut.security.token.bearer.BearerTokenReader import spock.lang.Shared import spock.lang.Specification diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/CookieEnabledSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/CookieEnabledSpec.groovy index 52a4f27845..7226ac1942 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/CookieEnabledSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/CookieEnabledSpec.groovy @@ -2,19 +2,22 @@ package io.micronaut.security.token.jwt.cookie import io.micronaut.context.exceptions.NoSuchBeanException import io.micronaut.security.testutils.ApplicationContextSpecification +import io.micronaut.security.token.cookie.AccessTokenCookieConfiguration +import io.micronaut.security.token.cookie.CookieTokenReader +import io.micronaut.security.token.cookie.TokenCookieConfigurationProperties import spock.lang.Unroll class CookieEnabledSpec extends ApplicationContextSpecification { @Override Map getConfiguration() { Map conf = super.configuration + [ - 'micronaut.security.token.jwt.cookie.enabled': false, + 'micronaut.security.token.cookie.enabled': false, ] conf } - @Unroll("if micronaut.security.token.jwt.cookie.enabled=false bean [#description] is not loaded") - void "if micronaut.security.token.jwt.cookie.enabled=false security related beans are not loaded"(Class clazz, String description) { + @Unroll("if micronaut.security.token.cookie.enabled=false bean [#description] is not loaded") + void "if micronaut.security.token.cookie.enabled=false security related beans are not loaded"(Class clazz, String description) { when: applicationContext.getBean(clazz) @@ -25,8 +28,8 @@ class CookieEnabledSpec extends ApplicationContextSpecification { where: clazz << [ AccessTokenCookieConfiguration, - JwtCookieConfigurationProperties, - JwtCookieTokenReader, + TokenCookieConfigurationProperties, + CookieTokenReader, ] description = clazz.name diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieClearerLogoutHandlerContextPathSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieClearerLogoutHandlerContextPathSpec.groovy index fc4207269a..0dbfeb4c7d 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieClearerLogoutHandlerContextPathSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieClearerLogoutHandlerContextPathSpec.groovy @@ -2,6 +2,7 @@ package io.micronaut.security.token.jwt.cookie import io.micronaut.security.testutils.EmbeddedServerSpecification +import io.micronaut.security.token.cookie.TokenCookieClearerLogoutHandler class JwtCookieClearerLogoutHandlerContextPathSpec extends EmbeddedServerSpecification { @Override @@ -14,6 +15,6 @@ class JwtCookieClearerLogoutHandlerContextPathSpec extends EmbeddedServerSpecifi void "uses context path"() { expect: - '/foo/' == applicationContext.getBean(JwtCookieClearerLogoutHandler).logout + '/foo/' == applicationContext.getBean(TokenCookieClearerLogoutHandler).logout } } diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieClearerLogoutHandlerSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieClearerLogoutHandlerSpec.groovy index 9140441a46..8e92ce1a0f 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieClearerLogoutHandlerSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieClearerLogoutHandlerSpec.groovy @@ -7,6 +7,9 @@ import io.micronaut.security.authentication.AuthenticationMode import io.micronaut.security.config.RedirectConfiguration import io.micronaut.security.config.RedirectService import io.micronaut.security.handlers.LogoutHandler +import io.micronaut.security.token.cookie.AccessTokenCookieConfiguration +import io.micronaut.security.token.cookie.TokenCookieClearerLogoutHandler +import io.micronaut.security.token.cookie.RefreshTokenCookieConfiguration import spock.lang.Specification class JwtCookieClearerLogoutHandlerSpec extends Specification { @@ -16,7 +19,7 @@ class JwtCookieClearerLogoutHandlerSpec extends Specification { ApplicationContext ctx = ApplicationContext.run([:]) expect: - !ctx.containsBean(JwtCookieClearerLogoutHandler) + !ctx.containsBean(TokenCookieClearerLogoutHandler) !ctx.containsBean(LogoutHandler) cleanup: @@ -28,7 +31,7 @@ class JwtCookieClearerLogoutHandlerSpec extends Specification { ApplicationContext ctx = ApplicationContext.run(['micronaut.security.authentication':'cookie']) expect: - ctx.containsBean(JwtCookieClearerLogoutHandler) + ctx.containsBean(TokenCookieClearerLogoutHandler) ctx.containsBean(LogoutHandler) cleanup: @@ -40,7 +43,7 @@ class JwtCookieClearerLogoutHandlerSpec extends Specification { ApplicationContext ctx = ApplicationContext.run(['micronaut.security.authentication': mode]) expect: - ctx.containsBean(JwtCookieClearerLogoutHandler) == expected + ctx.containsBean(TokenCookieClearerLogoutHandler) == expected ctx.containsBean(LogoutHandler) == expected cleanup: @@ -74,7 +77,7 @@ class JwtCookieClearerLogoutHandlerSpec extends Specification { } HttpRequest request = Mock() - LogoutHandler handler = new JwtCookieClearerLogoutHandler(accessTokenCookieConfiguration, refreshTokenCookieConfiguration, redirectConfiguration, redirectService) + LogoutHandler handler = new TokenCookieClearerLogoutHandler(accessTokenCookieConfiguration, refreshTokenCookieConfiguration, redirectConfiguration, redirectService); MutableHttpResponse response = handler.logout(request) List cookieHeaders = response.getHeaders().getAll("Set-Cookie") diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieExpirationSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieExpirationSpec.groovy index bc308e9dc1..18081100f3 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieExpirationSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieExpirationSpec.groovy @@ -22,7 +22,7 @@ class JwtCookieExpirationSpec extends EmbeddedServerSpecification { 'micronaut.http.client.followRedirects': false, 'micronaut.security.authentication': 'cookie', 'micronaut.security.redirect.login-failure': '/login/authFailed', - 'micronaut.security.token.jwt.generator.access-token.expiration': '500', + 'micronaut.security.token.generator.access-token.expiration': '500', 'micronaut.security.token.jwt.signatures.secret.generator.secret': 'qrD6h8K6S9503Q06Y6Rfk21TErImPYqa', ] } diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieLoginHandlerContextPathSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieLoginHandlerContextPathSpec.groovy index 68a258e8a3..bcf1c2c2fd 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieLoginHandlerContextPathSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieLoginHandlerContextPathSpec.groovy @@ -1,6 +1,7 @@ package io.micronaut.security.token.jwt.cookie import io.micronaut.security.testutils.EmbeddedServerSpecification +import io.micronaut.security.token.cookie.TokenCookieLoginHandler class JwtCookieLoginHandlerContextPathSpec extends EmbeddedServerSpecification { @Override @@ -13,8 +14,8 @@ class JwtCookieLoginHandlerContextPathSpec extends EmbeddedServerSpecification { void "uses context path"() { expect: - '/foo/' == applicationContext.getBean(JwtCookieLoginHandler).loginFailure - '/foo/' == applicationContext.getBean(JwtCookieLoginHandler).loginSuccess - '/foo/' == applicationContext.getBean(JwtCookieLoginHandler).refresh + '/foo/' == applicationContext.getBean(TokenCookieLoginHandler).loginFailure + '/foo/' == applicationContext.getBean(TokenCookieLoginHandler).loginSuccess + '/foo/' == applicationContext.getBean(TokenCookieLoginHandler).refresh } } diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieMaxAgeSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieMaxAgeSpec.groovy index cefb179861..adfaf08168 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieMaxAgeSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieMaxAgeSpec.groovy @@ -21,7 +21,7 @@ class JwtCookieMaxAgeSpec extends EmbeddedServerSpecification { super.configuration + [ 'micronaut.http.client.followRedirects': false, 'micronaut.security.authentication': 'cookie', - 'micronaut.security.token.jwt.cookie.cookie-max-age': '5m', + 'micronaut.security.token.cookie.cookie-max-age': '5m', 'micronaut.security.redirect.login-failure': '/login/authFailed', 'micronaut.security.token.jwt.signatures.secret.generator.secret': 'qrD6h8K6S9503Q06Y6Rfk21TErImPYqa', ] diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookiePathAndDomainSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookiePathAndDomainSpec.groovy index a512e58a39..3b415f535f 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookiePathAndDomainSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookiePathAndDomainSpec.groovy @@ -28,8 +28,8 @@ class JwtCookiePathAndDomainSpec extends EmbeddedServerSpecification { super.configuration + [ 'micronaut.http.client.followRedirects': false, 'micronaut.security.authentication': 'cookie', - 'micronaut.security.token.jwt.cookie.cookie-path': "/path", - 'micronaut.security.token.jwt.cookie.cookie-domain': "example.com", + 'micronaut.security.token.cookie.cookie-path': "/path", + 'micronaut.security.token.cookie.cookie-domain': "example.com", 'micronaut.security.token.jwt.signatures.secret.generator.secret': 'qrD6h8K6S9503Q06Y6Rfk21TErImPYqa', ] } diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieSameSiteCaseSensitiveSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieSameSiteCaseSensitiveSpec.groovy index a973ec293d..fbe164e1e7 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieSameSiteCaseSensitiveSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieSameSiteCaseSensitiveSpec.groovy @@ -21,7 +21,7 @@ class JwtCookieSameSiteCaseSensitiveSpec extends Specification { 'spec.name': 'JwtCookieSameSiteCaseSensitiveSpec', 'micronaut.http.client.followRedirects': false, 'micronaut.security.authentication': 'cookie', - 'micronaut.security.token.jwt.cookie.cookie-same-site': sameSiteValue, + 'micronaut.security.token.cookie.cookie-same-site': sameSiteValue, ]) ApplicationContext applicationContext = embeddedServer.applicationContext HttpClient httpClient = applicationContext.createBean(HttpClient, embeddedServer.URL) diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieSameSiteInvalidSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieSameSiteInvalidSpec.groovy index 607baedda5..7d36819228 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieSameSiteInvalidSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieSameSiteInvalidSpec.groovy @@ -21,8 +21,8 @@ class JwtCookieSameSiteInvalidSpec extends EmbeddedServerSpecification { super.configuration + [ 'micronaut.http.client.followRedirects': false, 'micronaut.security.authentication': 'cookie', - 'micronaut.security.token.jwt.cookie.cookie-max-age': '5m', - 'micronaut.security.token.jwt.cookie.cookie-same-site': 'nonesense', + 'micronaut.security.token.cookie.cookie-max-age': '5m', + 'micronaut.security.token.cookie.cookie-same-site': 'nonesense', 'micronaut.security.redirect.login-failure': '/login/authFailed', 'micronaut.security.token.jwt.signatures.secret.generator.secret': 'qrD6h8K6S9503Q06Y6Rfk21TErImPYqa', ] diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieSameSiteNoDefaultSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieSameSiteNoDefaultSpec.groovy index f65660d1c9..b563e7f084 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieSameSiteNoDefaultSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieSameSiteNoDefaultSpec.groovy @@ -21,7 +21,7 @@ class JwtCookieSameSiteNoDefaultSpec extends EmbeddedServerSpecification { [ 'micronaut.http.client.followRedirects': false, 'micronaut.security.authentication': 'cookie', - 'micronaut.security.token.jwt.cookie.cookie-max-age': '5m', + 'micronaut.security.token.cookie.cookie-max-age': '5m', 'micronaut.security.redirect.login-failure': '/login/authFailed', 'micronaut.security.token.jwt.signatures.secret.generator.secret': 'qrD6h8K6S9503Q06Y6Rfk21TErImPYqa', ] diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieSameSiteSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieSameSiteSpec.groovy index 76f074fb8d..b051f17584 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieSameSiteSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieSameSiteSpec.groovy @@ -21,8 +21,8 @@ class JwtCookieSameSiteSpec extends EmbeddedServerSpecification { super.configuration + [ 'micronaut.http.client.followRedirects': false, 'micronaut.security.authentication': 'cookie', - 'micronaut.security.token.jwt.cookie.cookie-max-age': '5m', - 'micronaut.security.token.jwt.cookie.cookie-same-site': 'None', + 'micronaut.security.token.cookie.cookie-max-age': '5m', + 'micronaut.security.token.cookie.cookie-same-site': 'None', 'micronaut.security.redirect.login-failure': '/login/authFailed', 'micronaut.security.token.jwt.signatures.secret.generator.secret': 'qrD6h8K6S9503Q06Y6Rfk21TErImPYqa', ] diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/endpoints/LoginControllerSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/endpoints/LoginControllerSpec.groovy index 19b4aa7ab4..544362d6f8 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/endpoints/LoginControllerSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/endpoints/LoginControllerSpec.groovy @@ -12,7 +12,7 @@ import io.micronaut.security.testutils.EmbeddedServerSpecification import io.micronaut.security.testutils.authprovider.MockAuthenticationProvider import io.micronaut.security.testutils.authprovider.SuccessAuthenticationScenario import io.micronaut.security.token.jwt.encryption.EncryptionConfiguration -import io.micronaut.security.token.jwt.render.BearerAccessRefreshToken +import io.micronaut.security.token.render.BearerAccessRefreshToken import io.micronaut.security.token.jwt.signature.SignatureConfiguration import jakarta.inject.Singleton diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/endpoints/OauthControllerEnabledSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/endpoints/OauthControllerEnabledSpec.groovy index 6cf6f825b5..921a8c00f1 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/endpoints/OauthControllerEnabledSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/endpoints/OauthControllerEnabledSpec.groovy @@ -3,10 +3,15 @@ package io.micronaut.security.token.jwt.endpoints import io.micronaut.context.annotation.Requires import io.micronaut.context.exceptions.NoSuchBeanException import io.micronaut.security.authentication.Authentication +import io.micronaut.security.endpoints.OauthController +import io.micronaut.security.endpoints.OauthControllerConfiguration +import io.micronaut.security.endpoints.OauthControllerConfigurationProperties import io.micronaut.security.testutils.ApplicationContextSpecification import io.micronaut.security.token.event.RefreshTokenGeneratedEvent -import io.micronaut.security.token.jwt.generator.AccessRefreshTokenGenerator +import io.micronaut.security.token.generator.AccessRefreshTokenGenerator import io.micronaut.security.token.refresh.RefreshTokenPersistence + + import io.micronaut.security.token.validator.RefreshTokenValidator import jakarta.inject.Singleton import org.reactivestreams.Publisher diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/endpoints/OauthControllerPathConfigurableSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/endpoints/OauthControllerPathConfigurableSpec.groovy index 829ffbfdcd..40d113b4ec 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/endpoints/OauthControllerPathConfigurableSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/endpoints/OauthControllerPathConfigurableSpec.groovy @@ -6,6 +6,7 @@ import io.micronaut.http.HttpRequest import io.micronaut.http.HttpStatus import io.micronaut.http.client.exceptions.HttpClientResponseException import io.micronaut.security.authentication.Authentication +import io.micronaut.security.endpoints.OauthController import io.micronaut.security.testutils.EmbeddedServerSpecification import io.micronaut.security.token.event.RefreshTokenGeneratedEvent import io.micronaut.security.token.refresh.RefreshTokenPersistence diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/endpoints/OauthControllerSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/endpoints/OauthControllerSpec.groovy index 5653af0391..ae9b91956c 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/endpoints/OauthControllerSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/endpoints/OauthControllerSpec.groovy @@ -18,15 +18,16 @@ import io.micronaut.inject.qualifiers.Qualifiers import io.micronaut.security.annotation.Secured import io.micronaut.security.authentication.Authentication import io.micronaut.security.authentication.UsernamePasswordCredentials +import io.micronaut.security.endpoints.TokenRefreshRequest import io.micronaut.security.rules.SecurityRule import io.micronaut.security.testutils.EmbeddedServerSpecification import io.micronaut.security.testutils.authprovider.MockAuthenticationProvider import io.micronaut.security.testutils.authprovider.SuccessAuthenticationScenario +import io.micronaut.security.token.Claims import io.micronaut.security.token.event.RefreshTokenGeneratedEvent import io.micronaut.security.token.jwt.encryption.EncryptionConfiguration -import io.micronaut.security.token.jwt.generator.claims.JwtClaims -import io.micronaut.security.token.jwt.render.AccessRefreshToken -import io.micronaut.security.token.jwt.render.BearerAccessRefreshToken +import io.micronaut.security.token.render.AccessRefreshToken +import io.micronaut.security.token.render.BearerAccessRefreshToken import io.micronaut.security.token.jwt.signature.SignatureConfiguration import io.micronaut.security.token.jwt.validator.JwtTokenValidator import io.micronaut.security.token.refresh.RefreshTokenPersistence @@ -123,21 +124,21 @@ class OauthControllerSpec extends EmbeddedServerSpecification { TokenValidator tokenValidator = applicationContext.getBean(JwtTokenValidator.class) Map newAccessTokenClaims = Flux.from(tokenValidator.validateToken(refreshRsp.body().accessToken, null)).blockFirst().getAttributes() Map originalAccessTokenClaims = Flux.from(tokenValidator.validateToken(originalAccessToken, null)).blockFirst().getAttributes() - List expectedClaims = [JwtClaims.SUBJECT, - JwtClaims.ISSUED_AT, - JwtClaims.EXPIRATION_TIME, - JwtClaims.NOT_BEFORE, + List expectedClaims = [Claims.SUBJECT, + Claims.ISSUED_AT, + Claims.EXPIRATION_TIME, + Claims.NOT_BEFORE, "roles"] then: expectedClaims.each { String claimName -> assert newAccessTokenClaims.containsKey(claimName) assert originalAccessTokenClaims.containsKey(claimName) } - originalAccessTokenClaims.get(JwtClaims.SUBJECT) == newAccessTokenClaims.get(JwtClaims.SUBJECT) + originalAccessTokenClaims.get(Claims.SUBJECT) == newAccessTokenClaims.get(Claims.SUBJECT) originalAccessTokenClaims.get("roles") == newAccessTokenClaims.get("roles") - originalAccessTokenClaims.get(JwtClaims.ISSUED_AT) != newAccessTokenClaims.get(JwtClaims.ISSUED_AT) - originalAccessTokenClaims.get(JwtClaims.EXPIRATION_TIME) != newAccessTokenClaims.get(JwtClaims.EXPIRATION_TIME) - originalAccessTokenClaims.get(JwtClaims.NOT_BEFORE) != newAccessTokenClaims.get(JwtClaims.NOT_BEFORE) + originalAccessTokenClaims.get(Claims.ISSUED_AT) != newAccessTokenClaims.get(Claims.ISSUED_AT) + originalAccessTokenClaims.get(Claims.EXPIRATION_TIME) != newAccessTokenClaims.get(Claims.EXPIRATION_TIME) + originalAccessTokenClaims.get(Claims.NOT_BEFORE) != newAccessTokenClaims.get(Claims.NOT_BEFORE) cleanup: applicationContext.getBean(InMemoryRefreshTokenPersistence).tokens.clear() diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/generator/claims/JWTClaimsSetGeneratorSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/generator/claims/JWTClaimsSetGeneratorSpec.groovy index 3e55d668d6..5b1512234b 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/generator/claims/JWTClaimsSetGeneratorSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/generator/claims/JWTClaimsSetGeneratorSpec.groovy @@ -1,6 +1,7 @@ package io.micronaut.security.token.jwt.generator.claims import io.micronaut.security.authentication.Authentication +import io.micronaut.security.token.Claims import io.micronaut.security.token.config.TokenConfiguration import spock.lang.Specification @@ -12,11 +13,11 @@ class JWTClaimsSetGeneratorSpec extends Specification { when: Map claims = generator.generateClaims(Authentication.build('admin', ['ROLE_USER', 'ROLE_ADMIN']), 3600) - List expectedClaimsNames = [JwtClaims.SUBJECT, - JwtClaims.ISSUED_AT, - JwtClaims.EXPIRATION_TIME, - JwtClaims.NOT_BEFORE, - JwtClaims.ISSUER, + List expectedClaimsNames = [Claims.SUBJECT, + Claims.ISSUED_AT, + Claims.EXPIRATION_TIME, + Claims.NOT_BEFORE, + Claims.ISSUER, "roles"] then: claims diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/refreshtokenexpiration/AccessTokenExpirationSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/refreshtokenexpiration/AccessTokenExpirationSpec.groovy index de4e0c0f1e..bfdaa3e694 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/refreshtokenexpiration/AccessTokenExpirationSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/refreshtokenexpiration/AccessTokenExpirationSpec.groovy @@ -11,7 +11,8 @@ import io.micronaut.security.annotation.Secured import io.micronaut.security.authentication.UsernamePasswordCredentials import io.micronaut.security.rules.SecurityRule import io.micronaut.security.testutils.EmbeddedServerSpecification -import io.micronaut.security.token.jwt.render.BearerAccessRefreshToken +import io.micronaut.security.token.render.BearerAccessRefreshToken + class AccessTokenExpirationSpec extends EmbeddedServerSpecification { @@ -25,7 +26,7 @@ class AccessTokenExpirationSpec extends EmbeddedServerSpecification { 'endpoints.beans.enabled': true, 'endpoints.beans.sensitive': true, 'micronaut.security.authentication': 'bearer', - 'micronaut.security.token.jwt.generator.access-token.expiration': 5, + 'micronaut.security.token.generator.access-token.expiration': 5, 'micronaut.security.token.jwt.signatures.secret.generator.secret': 'pleaseChangeThisSecretForANewOne', ] } diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/render/AccessRefreshTokenSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/render/AccessRefreshTokenSpec.groovy index e54c209c89..d00f951506 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/render/AccessRefreshTokenSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/render/AccessRefreshTokenSpec.groovy @@ -3,6 +3,7 @@ package io.micronaut.security.token.jwt.render import io.micronaut.core.beans.BeanIntrospection import io.micronaut.core.type.Argument import io.micronaut.security.testutils.ApplicationContextSpecification +import io.micronaut.security.token.render.AccessRefreshToken import io.micronaut.serde.ObjectMapper import io.micronaut.serde.SerdeIntrospections @@ -12,7 +13,7 @@ class AccessRefreshTokenSpec extends ApplicationContextSpecification { ObjectMapper mapper = ObjectMapper.getDefault() and : "a fully populated token" - AccessRefreshToken token = new AccessRefreshToken("1234", "abcd", "Bearer", null) + AccessRefreshToken token = new AccessRefreshToken("1234", "abcd", "Bearer", null) when: "we serialize the object to json" def rawJsonString = mapper.writeValueAsString(token) diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/render/BearerAccessRefreshTokenSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/render/BearerAccessRefreshTokenSpec.groovy index fc8a8fa1cf..9ca7d0c991 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/render/BearerAccessRefreshTokenSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/render/BearerAccessRefreshTokenSpec.groovy @@ -1,5 +1,6 @@ package io.micronaut.security.token.jwt.render +import io.micronaut.security.token.render.BearerAccessRefreshToken import groovy.json.JsonSlurper import io.micronaut.core.beans.BeanIntrospection import io.micronaut.core.type.Argument diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/roles/RolesNameCustomSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/roles/RolesNameCustomSpec.groovy index 7f534b6cfc..21f4606b92 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/roles/RolesNameCustomSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/roles/RolesNameCustomSpec.groovy @@ -28,7 +28,7 @@ import io.micronaut.security.annotation.Secured import io.micronaut.security.testutils.EmbeddedServerSpecification import io.micronaut.security.testutils.authprovider.MockAuthenticationProvider import io.micronaut.security.testutils.authprovider.SuccessAuthenticationScenario -import io.micronaut.security.token.jwt.render.BearerAccessRefreshToken +import io.micronaut.security.token.render.BearerAccessRefreshToken import jakarta.inject.Singleton class RolesNameCustomSpec extends EmbeddedServerSpecification { diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/signature/jwks/JwksCacheSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/signature/jwks/JwksCacheSpec.groovy index dfc7342c9a..4350b3e054 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/signature/jwks/JwksCacheSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/signature/jwks/JwksCacheSpec.groovy @@ -36,14 +36,14 @@ import io.micronaut.security.annotation.Secured import io.micronaut.security.rules.SecurityRule import io.micronaut.security.testutils.authprovider.MockAuthenticationProvider import io.micronaut.security.testutils.authprovider.SuccessAuthenticationScenario +import io.micronaut.security.token.claims.ClaimsGenerator import io.micronaut.security.token.generator.TokenGenerator import io.micronaut.security.token.jwt.endpoints.JwkProvider import io.micronaut.security.token.jwt.endpoints.KeysController import io.micronaut.security.token.jwt.generator.JwtTokenGenerator -import io.micronaut.security.token.jwt.generator.claims.ClaimsGenerator -import io.micronaut.security.token.jwt.render.BearerAccessRefreshToken import io.micronaut.security.token.jwt.signature.rsa.RSASignatureGenerator import io.micronaut.security.token.jwt.signature.rsa.RSASignatureGeneratorConfiguration +import io.micronaut.security.token.render.BearerAccessRefreshToken import jakarta.inject.Named import jakarta.inject.Singleton import org.reactivestreams.Publisher diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/signature/jwks/JwksSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/signature/jwks/JwksSpec.groovy index cd88a46022..d277971b11 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/signature/jwks/JwksSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/signature/jwks/JwksSpec.groovy @@ -24,7 +24,7 @@ import io.micronaut.security.rules.SecurityRule import io.micronaut.security.testutils.authprovider.MockAuthenticationProvider import io.micronaut.security.testutils.authprovider.SuccessAuthenticationScenario import io.micronaut.security.token.jwt.endpoints.JwkProvider -import io.micronaut.security.token.jwt.render.BearerAccessRefreshToken +import io.micronaut.security.token.render.BearerAccessRefreshToken import io.micronaut.security.token.jwt.signature.SignatureConfiguration import io.micronaut.security.token.jwt.signature.rsa.RSASignatureGeneratorConfiguration import jakarta.inject.Named diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/signature/jwks/NotAvailableRemoteJwksSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/signature/jwks/NotAvailableRemoteJwksSpec.groovy index e55efe4b65..863a30bc4d 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/signature/jwks/NotAvailableRemoteJwksSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/signature/jwks/NotAvailableRemoteJwksSpec.groovy @@ -24,7 +24,7 @@ import io.micronaut.security.rules.SecurityRule import io.micronaut.security.testutils.authprovider.MockAuthenticationProvider import io.micronaut.security.testutils.authprovider.SuccessAuthenticationScenario import io.micronaut.security.token.jwt.endpoints.JwkProvider -import io.micronaut.security.token.jwt.render.AccessRefreshToken +import io.micronaut.security.token.render.AccessRefreshToken import io.micronaut.security.token.jwt.signature.SignatureConfiguration import io.micronaut.security.token.jwt.signature.rsa.RSASignatureGeneratorConfiguration import jakarta.inject.Named diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/validator/AudienceJwtClaimsValidatorSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/validator/AudienceJwtClaimsValidatorSpec.groovy index 731445ad50..6a98e82753 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/validator/AudienceJwtClaimsValidatorSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/validator/AudienceJwtClaimsValidatorSpec.groovy @@ -4,8 +4,8 @@ import io.micronaut.context.ApplicationContext import io.micronaut.core.annotation.NonNull import io.micronaut.core.annotation.Nullable import io.micronaut.security.authentication.Authentication +import io.micronaut.security.token.Claims import io.micronaut.security.token.jwt.generator.JwtTokenGenerator -import io.micronaut.security.token.jwt.generator.claims.JwtClaims import reactor.core.publisher.Flux import spock.lang.Shared import spock.lang.Specification @@ -36,7 +36,7 @@ class AudienceJwtClaimsValidatorSpec extends Specification { then: result - result.attributes[JwtClaims.SUBJECT] == "alice" + result.attributes[Claims.SUBJECT] == "alice" cleanup: context.close() @@ -55,7 +55,7 @@ class AudienceJwtClaimsValidatorSpec extends Specification { then: result - result.attributes[JwtClaims.SUBJECT] == "alice" + result.attributes[Claims.SUBJECT] == "alice" cleanup: context.close() @@ -73,7 +73,7 @@ class AudienceJwtClaimsValidatorSpec extends Specification { then: result - result.attributes[JwtClaims.SUBJECT] == "alice" + result.attributes[Claims.SUBJECT] == "alice" cleanup: context.close() } @@ -139,9 +139,9 @@ class AudienceJwtClaimsValidatorSpec extends Specification { private static String generateJwtWithAudiences(ApplicationContext context, @Nullable Object audience) { JwtTokenGenerator jwtGenerator = context.getBean(JwtTokenGenerator.class) Map claims = [:] - claims[JwtClaims.SUBJECT] = 'alice' + claims[Claims.SUBJECT] = 'alice' if (audience != null) { - claims[JwtClaims.AUDIENCE] = audience + claims[Claims.AUDIENCE] = audience } jwtGenerator.generateToken(claims).get() } diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/validator/IssuerJwtClaimsValidatorSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/validator/IssuerJwtClaimsValidatorSpec.groovy index 77d9c82b99..ec3aa729c5 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/validator/IssuerJwtClaimsValidatorSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/validator/IssuerJwtClaimsValidatorSpec.groovy @@ -4,8 +4,8 @@ import io.micronaut.context.ApplicationContext import io.micronaut.core.annotation.NonNull import io.micronaut.core.annotation.Nullable import io.micronaut.security.authentication.Authentication +import io.micronaut.security.token.Claims import io.micronaut.security.token.jwt.generator.JwtTokenGenerator -import io.micronaut.security.token.jwt.generator.claims.JwtClaims import reactor.core.publisher.Flux import spock.lang.Shared import spock.lang.Specification @@ -32,7 +32,7 @@ class IssuerJwtClaimsValidatorSpec extends Specification { Authentication result = authenticate(context, jwt) then: result - result.attributes[JwtClaims.SUBJECT] == "alice" + result.attributes[Claims.SUBJECT] == "alice" cleanup: context.close() @@ -50,7 +50,7 @@ class IssuerJwtClaimsValidatorSpec extends Specification { then: result - result.attributes[JwtClaims.SUBJECT] == "alice" + result.attributes[Claims.SUBJECT] == "alice" cleanup: context.close() @@ -100,9 +100,9 @@ class IssuerJwtClaimsValidatorSpec extends Specification { private static String generateJwtWithIssuer(ApplicationContext context, @Nullable Object issuer) { JwtTokenGenerator jwtGenerator = context.getBean(JwtTokenGenerator.class) Map claims = [:] - claims[JwtClaims.SUBJECT] = 'alice' + claims[Claims.SUBJECT] = 'alice' if (issuer != null) { - claims[JwtClaims.ISSUER] = issuer + claims[Claims.ISSUER] = issuer } jwtGenerator.generateToken(claims).get() } diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/validator/JwtClaimsValidatorRequestPassedSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/validator/JwtClaimsValidatorRequestPassedSpec.groovy index 74fb8ff34b..4d4efb796f 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/validator/JwtClaimsValidatorRequestPassedSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/validator/JwtClaimsValidatorRequestPassedSpec.groovy @@ -15,9 +15,9 @@ import io.micronaut.security.rules.SecurityRule import io.micronaut.security.testutils.EmbeddedServerSpecification import io.micronaut.security.testutils.authprovider.MockAuthenticationProvider import io.micronaut.security.testutils.authprovider.SuccessAuthenticationScenario +import io.micronaut.security.token.Claims import io.micronaut.security.token.jwt.encryption.EncryptionConfiguration -import io.micronaut.security.token.jwt.generator.claims.JwtClaims -import io.micronaut.security.token.jwt.render.BearerAccessRefreshToken +import io.micronaut.security.token.render.BearerAccessRefreshToken import io.micronaut.security.token.jwt.signature.SignatureConfiguration import jakarta.inject.Singleton import org.reactivestreams.Publisher @@ -85,7 +85,7 @@ class JwtClaimsValidatorRequestPassedSpec extends EmbeddedServerSpecification { static class HttpRequestClaimsValidator implements GenericJwtClaimsValidator { @Override - boolean validate(@NonNull JwtClaims claims, @Nullable HttpRequest request) { + boolean validate(@NonNull Claims claims, @Nullable HttpRequest request) { request != null } } diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/validator/NotBeforeJwtClaimsValidatorSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/validator/NotBeforeJwtClaimsValidatorSpec.groovy index 7dd2003a6d..a99ebf9408 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/validator/NotBeforeJwtClaimsValidatorSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/jwt/validator/NotBeforeJwtClaimsValidatorSpec.groovy @@ -4,8 +4,8 @@ import io.micronaut.context.ApplicationContext import io.micronaut.core.annotation.NonNull import io.micronaut.core.annotation.Nullable import io.micronaut.security.authentication.Authentication +import io.micronaut.security.token.Claims import io.micronaut.security.token.jwt.generator.JwtTokenGenerator -import io.micronaut.security.token.jwt.generator.claims.JwtClaims import reactor.core.publisher.Flux import spock.lang.Shared import spock.lang.Specification @@ -44,7 +44,7 @@ class NotBeforeJwtClaimsValidatorSpec extends Specification { then: result - result.attributes[JwtClaims.SUBJECT] == "alice" + result.attributes[Claims.SUBJECT] == "alice" cleanup: context.close() @@ -79,7 +79,7 @@ class NotBeforeJwtClaimsValidatorSpec extends Specification { then: result - result.attributes[JwtClaims.SUBJECT] == "alice" + result.attributes[Claims.SUBJECT] == "alice" cleanup: context.close() @@ -95,7 +95,7 @@ class NotBeforeJwtClaimsValidatorSpec extends Specification { then: result - result.attributes[JwtClaims.SUBJECT] == "alice" + result.attributes[Claims.SUBJECT] == "alice" cleanup: context.close() @@ -111,9 +111,9 @@ class NotBeforeJwtClaimsValidatorSpec extends Specification { private static String generateJwtWithNotBefore(ApplicationContext context, @Nullable Object notBefore) { JwtTokenGenerator jwtGenerator = context.getBean(JwtTokenGenerator.class) Map claims = [:] - claims[JwtClaims.SUBJECT] = 'alice' + claims[Claims.SUBJECT] = 'alice' if (notBefore != null) { - claims[JwtClaims.NOT_BEFORE] = notBefore + claims[Claims.NOT_BEFORE] = notBefore } jwtGenerator.generateToken(claims).get() } diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/multitenancy/principal/PrincipalTenantResolverSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/multitenancy/principal/PrincipalTenantResolverSpec.groovy index 14331cc8da..441a9bb2d6 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/multitenancy/principal/PrincipalTenantResolverSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/multitenancy/principal/PrincipalTenantResolverSpec.groovy @@ -9,7 +9,7 @@ import io.micronaut.http.HttpStatus import io.micronaut.http.client.HttpClient import io.micronaut.http.client.exceptions.HttpClientResponseException import io.micronaut.runtime.server.EmbeddedServer -import io.micronaut.security.token.jwt.render.BearerAccessRefreshToken +import io.micronaut.security.token.render.BearerAccessRefreshToken import spock.lang.AutoCleanup import spock.lang.Shared import spock.lang.Specification diff --git a/security-jwt/src/test/groovy/io/micronaut/security/token/propagation/TokenPropagationSpec.groovy b/security-jwt/src/test/groovy/io/micronaut/security/token/propagation/TokenPropagationSpec.groovy index cc568631e6..9cd14dd9dc 100644 --- a/security-jwt/src/test/groovy/io/micronaut/security/token/propagation/TokenPropagationSpec.groovy +++ b/security-jwt/src/test/groovy/io/micronaut/security/token/propagation/TokenPropagationSpec.groovy @@ -21,7 +21,7 @@ import io.micronaut.security.authentication.UsernamePasswordCredentials import io.micronaut.security.rules.SecurityRule import io.micronaut.security.testutils.authprovider.MockAuthenticationProvider import io.micronaut.security.testutils.authprovider.SuccessAuthenticationScenario -import io.micronaut.security.token.jwt.render.BearerAccessRefreshToken +import io.micronaut.security.token.render.BearerAccessRefreshToken import io.micronaut.serde.annotation.Serdeable import jakarta.inject.Singleton import org.reactivestreams.Publisher diff --git a/security-oauth2/src/main/java/io/micronaut/security/oauth2/DefaultProviderResolver.java b/security-oauth2/src/main/java/io/micronaut/security/oauth2/DefaultProviderResolver.java index 344ef24d6e..424c2e1926 100644 --- a/security-oauth2/src/main/java/io/micronaut/security/oauth2/DefaultProviderResolver.java +++ b/security-oauth2/src/main/java/io/micronaut/security/oauth2/DefaultProviderResolver.java @@ -20,7 +20,7 @@ import io.micronaut.security.authentication.Authentication; import io.micronaut.security.oauth2.configuration.OpenIdClientConfiguration; import io.micronaut.security.oauth2.endpoint.token.response.OauthAuthenticationMapper; -import io.micronaut.security.token.jwt.generator.claims.JwtClaims; +import io.micronaut.security.token.Claims; import jakarta.inject.Singleton; import java.util.List; import java.util.Optional; @@ -54,7 +54,7 @@ public Optional resolveProvider(Authentication authentication) { * @return {@literal Optional#empty()} if iss claim not found, or if the iss claim does not match the issuer of any open id client. If it matches, the open id client is returned wrapped in an optional */ protected Optional openIdClientNameWhichMatchesIssClaim(Authentication authentication) { - Object issuer = authentication.getAttributes().get(JwtClaims.ISSUER); + Object issuer = authentication.getAttributes().get(Claims.ISSUER); return issuer != null ? openIdClientNameWhichMatchesIssuer(issuer.toString()) : Optional.empty(); } diff --git a/security-oauth2/src/main/java/io/micronaut/security/oauth2/client/IdTokenClaimsValidator.java b/security-oauth2/src/main/java/io/micronaut/security/oauth2/client/IdTokenClaimsValidator.java index 272480805f..d111195a08 100644 --- a/security-oauth2/src/main/java/io/micronaut/security/oauth2/client/IdTokenClaimsValidator.java +++ b/security-oauth2/src/main/java/io/micronaut/security/oauth2/client/IdTokenClaimsValidator.java @@ -23,7 +23,7 @@ import io.micronaut.security.config.SecurityConfigurationProperties; import io.micronaut.security.oauth2.configuration.OauthClientConfiguration; import io.micronaut.security.oauth2.configuration.OpenIdClientConfiguration; -import io.micronaut.security.token.jwt.generator.claims.JwtClaims; +import io.micronaut.security.token.Claims; import io.micronaut.security.token.jwt.validator.GenericJwtClaimsValidator; import io.micronaut.security.token.jwt.validator.JwtClaimsValidatorConfigurationProperties; import jakarta.inject.Singleton; @@ -65,7 +65,7 @@ public IdTokenClaimsValidator(Collection oauthClientCo } @Override - public boolean validate(@NonNull JwtClaims claims, @Nullable HttpRequest request) { + public boolean validate(@NonNull Claims claims, @Nullable HttpRequest request) { Optional claimIssuerOptional = parseIssuerClaim(claims); if (!claimIssuerOptional.isPresent()) { return false; @@ -85,8 +85,8 @@ public boolean validate(@NonNull JwtClaims claims, @Nullable HttpRequest requ * @param claims JWT Claims * @return the iss claim value wrapped in an {@link Optional}. If not found, an empty {@link Optional} is returned. */ - protected Optional parseIssuerClaim(JwtClaims claims) { - return parseClaimString(claims, JwtClaims.ISSUER); + protected Optional parseIssuerClaim(Claims claims) { + return parseClaimString(claims, Claims.ISSUER); } /** @@ -95,7 +95,7 @@ protected Optional parseIssuerClaim(JwtClaims claims) { * @param claimName Claim Name * @return the claim value wrapped in an {@link Optional}. If not found, an empty {@link Optional} is returned. */ - protected Optional parseClaim(JwtClaims claims, String claimName) { + protected Optional parseClaim(Claims claims, String claimName) { Object obj = claims.get(claimName); if (obj == null) { if (LOG.isTraceEnabled()) { @@ -112,7 +112,7 @@ protected Optional parseClaim(JwtClaims claims, String claimName) { * @param claimName Claim Name * @return the claim value as a String wrapped in an {@link Optional}. If not found, an empty {@link Optional} is returned. */ - protected Optional parseClaimString(JwtClaims claims, String claimName) { + protected Optional parseClaimString(Claims claims, String claimName) { return parseClaim(claims, claimName).map(Object::toString); } @@ -122,7 +122,7 @@ protected Optional parseClaimString(JwtClaims claims, String claimName) * @param claimName Claim Name * @return the claim value as a list of Strings wrapped in an {@link Optional}. If not found, an empty {@link Optional} is returned. */ - protected Optional> parseClaimList(JwtClaims claims, String claimName) { + protected Optional> parseClaimList(Claims claims, String claimName) { Optional objectOptional = parseClaim(claims, claimName); if (!objectOptional.isPresent()) { return Optional.empty(); @@ -144,8 +144,8 @@ protected Optional> parseClaimList(JwtClaims claims, String claimNa * @param claims JWT Claims * @return the aud claim value a list of strings wrapped in an {@link Optional}. If not found, an empty {@link Optional} is returned. */ - protected Optional> parseAudiences(JwtClaims claims) { - return parseClaimList(claims, JwtClaims.AUDIENCE); + protected Optional> parseAudiences(Claims claims) { + return parseClaimList(claims, Claims.AUDIENCE); } /** @@ -155,7 +155,7 @@ protected Optional> parseAudiences(JwtClaims claims) { * @param audiences aud claim as a list of string * @return true if an OAuth 2.0 client issuer matches the iss claim, any of the audiences in the aud claim matches the OAuth 2.0 client_id and for multiple audiencies the azp claim is present and matches OAuth 2.0 client_id */ - protected boolean validateIssuerAudienceAndAzp(@NonNull JwtClaims claims, + protected boolean validateIssuerAudienceAndAzp(@NonNull Claims claims, @NonNull String iss, @NonNull List audiences) { return oauthClientConfigurations.stream().anyMatch(oauthClientConfiguration -> validateIssuerAudienceAndAzp(claims, iss, audiences, oauthClientConfiguration)); @@ -169,7 +169,7 @@ protected boolean validateIssuerAudienceAndAzp(@NonNull JwtClaims claims, * @param oauthClientConfiguration OAuth 2.0 client configuration * @return true if the OAuth 2.0 client OpenID issuer matches the iss claim, any of the audiences in the aud claim matches the OAuth 2.0 client_id and for multiple audiencies the azp claim is present and matches OAuth 2.0 client_id */ - protected boolean validateIssuerAudienceAndAzp(@NonNull JwtClaims claims, + protected boolean validateIssuerAudienceAndAzp(@NonNull Claims claims, @NonNull String iss, @NonNull List audiences, @NonNull OauthClientConfiguration oauthClientConfiguration) { @@ -190,7 +190,7 @@ protected boolean validateIssuerAudienceAndAzp(@NonNull JwtClaims claims, * @param openIdClientConfiguration OpenID OAuth 2.0 client configuration * @return true if the OAuth 2.0 client OpenID issuer matches the iss claim, any of the audiences in the aud claim matches the OAuth 2.0 client_id and for multiple audiencies the azp claim is present and matches OAuth 2.0 client_id */ - protected boolean validateIssuerAudienceAndAzp(@NonNull JwtClaims claims, + protected boolean validateIssuerAudienceAndAzp(@NonNull Claims claims, @NonNull String iss, @NonNull List audiences, @NonNull String clientId, @@ -212,7 +212,7 @@ protected boolean validateIssuerAudienceAndAzp(@NonNull JwtClaims claims, * @param claims JWT Claims * @return the azp claim value wrapped in an {@link Optional}. If not found, an empty {@link Optional} is returned. */ - protected Optional parseAzpClaim(JwtClaims claims) { + protected Optional parseAzpClaim(Claims claims) { return parseClaimString(claims, AUTHORIZED_PARTY); } @@ -223,7 +223,7 @@ protected Optional parseAzpClaim(JwtClaims claims) { * @param audiences audiences specified in the JWT Claims * @return true for single audiences, for multiple audiences returns true azp claim is present and matches OAuth 2.0 client_id */ - protected boolean validateAzp(@NonNull JwtClaims claims, + protected boolean validateAzp(@NonNull Claims claims, @NonNull String clientId, @NonNull List audiences) { if (audiences.size() < 2) { diff --git a/security-oauth2/src/main/java/io/micronaut/security/oauth2/endpoint/token/response/DefaultOpenIdAuthenticationMapper.java b/security-oauth2/src/main/java/io/micronaut/security/oauth2/endpoint/token/response/DefaultOpenIdAuthenticationMapper.java index 9f8af3fe95..a90857b05b 100644 --- a/security-oauth2/src/main/java/io/micronaut/security/oauth2/endpoint/token/response/DefaultOpenIdAuthenticationMapper.java +++ b/security-oauth2/src/main/java/io/micronaut/security/oauth2/endpoint/token/response/DefaultOpenIdAuthenticationMapper.java @@ -23,7 +23,7 @@ import io.micronaut.security.config.AuthenticationModeConfiguration; import io.micronaut.security.oauth2.configuration.OpenIdAdditionalClaimsConfiguration; import io.micronaut.security.oauth2.endpoint.authorization.state.State; -import io.micronaut.security.token.jwt.generator.claims.JwtClaims; +import io.micronaut.security.token.Claims; import jakarta.inject.Singleton; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; @@ -81,7 +81,7 @@ public Publisher createAuthenticationResponse(String pro */ protected Map buildAttributes(String providerName, OpenIdTokenResponse tokenResponse, OpenIdClaims openIdClaims) { Map claims = new HashMap<>(openIdClaims.getClaims()); - JwtClaims.ALL_CLAIMS.forEach(claims::remove); + Claims.ALL_CLAIMS.forEach(claims::remove); claims.put(OauthAuthenticationMapper.PROVIDER_KEY, providerName); boolean idtokenAuthentication = authenticationModeConfiguration.getAuthentication() != null && authenticationModeConfiguration.getAuthentication() == AuthenticationMode.IDTOKEN; if (idtokenAuthentication || openIdAdditionalClaimsConfiguration.isJwt()) { diff --git a/security-oauth2/src/main/java/io/micronaut/security/oauth2/endpoint/token/response/IdTokenLoginHandler.java b/security-oauth2/src/main/java/io/micronaut/security/oauth2/endpoint/token/response/IdTokenLoginHandler.java index 41154de264..2dc50fbb89 100644 --- a/security-oauth2/src/main/java/io/micronaut/security/oauth2/endpoint/token/response/IdTokenLoginHandler.java +++ b/security-oauth2/src/main/java/io/micronaut/security/oauth2/endpoint/token/response/IdTokenLoginHandler.java @@ -29,15 +29,19 @@ import io.micronaut.security.errors.OauthErrorResponseException; import io.micronaut.security.errors.ObtainingAuthorizationErrorCode; import io.micronaut.security.errors.PriorToLoginPersistence; -import io.micronaut.security.token.jwt.cookie.AccessTokenCookieConfiguration; -import io.micronaut.security.token.jwt.cookie.CookieLoginHandler; +import io.micronaut.security.token.cookie.AccessTokenCookieConfiguration; +import io.micronaut.security.token.cookie.CookieLoginHandler; import jakarta.inject.Singleton; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.text.ParseException; import java.time.Duration; -import java.util.*; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Optional; /** * Sets {@link CookieLoginHandler}`s cookie value to the idtoken received from an authentication provider. diff --git a/security-oauth2/src/main/java/io/micronaut/security/oauth2/endpoint/token/response/OpenIdClaims.java b/security-oauth2/src/main/java/io/micronaut/security/oauth2/endpoint/token/response/OpenIdClaims.java index 08747dd933..3497f8b7c2 100644 --- a/security-oauth2/src/main/java/io/micronaut/security/oauth2/endpoint/token/response/OpenIdClaims.java +++ b/security-oauth2/src/main/java/io/micronaut/security/oauth2/endpoint/token/response/OpenIdClaims.java @@ -17,7 +17,7 @@ import io.micronaut.core.annotation.NonNull; import io.micronaut.core.annotation.Nullable; -import io.micronaut.security.token.jwt.generator.claims.JwtClaims; +import io.micronaut.security.token.Claims; import java.util.Date; import java.util.List; import java.util.Map; @@ -30,7 +30,7 @@ * @author Sergio del Amo * @version 1.1.0 */ -public interface OpenIdClaims extends JwtClaims { +public interface OpenIdClaims extends Claims { String CLAIMS_NAME = "name"; String CLAIMS_GIVEN_NAME = "given_name"; diff --git a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/client/JwksUriSignatureSpec.groovy b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/client/JwksUriSignatureSpec.groovy index a3e74e588b..1155663f4f 100644 --- a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/client/JwksUriSignatureSpec.groovy +++ b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/client/JwksUriSignatureSpec.groovy @@ -33,10 +33,10 @@ import io.micronaut.security.testutils.authprovider.SuccessAuthenticationScenari import io.micronaut.security.token.config.TokenConfiguration import io.micronaut.security.token.jwt.endpoints.JwkProvider import io.micronaut.security.token.jwt.endpoints.KeysController -import io.micronaut.security.token.jwt.generator.claims.ClaimsAudienceProvider +import io.micronaut.security.token.claims.ClaimsAudienceProvider import io.micronaut.security.token.jwt.generator.claims.JWTClaimsSetGenerator -import io.micronaut.security.token.jwt.generator.claims.JwtIdGenerator -import io.micronaut.security.token.jwt.render.AccessRefreshToken +import io.micronaut.security.token.claims.JtiGenerator +import io.micronaut.security.token.render.AccessRefreshToken import io.micronaut.security.token.jwt.signature.SignatureConfiguration import io.micronaut.security.token.jwt.signature.rsa.RSASignatureGeneratorConfiguration import jakarta.inject.Named @@ -185,10 +185,10 @@ class JwksUriSignatureSpec extends Specification { static class AuthServerACustomJWTClaimsSetGenerator extends JWTClaimsSetGenerator { Integer port AuthServerACustomJWTClaimsSetGenerator(TokenConfiguration tokenConfiguration, - @Nullable JwtIdGenerator jwtIdGenerator, - @Nullable ClaimsAudienceProvider claimsAudienceProvider, - @Nullable ApplicationConfiguration applicationConfiguration, - @Value('${micronaut.server.port}') Integer port) { + @Nullable JtiGenerator jwtIdGenerator, + @Nullable ClaimsAudienceProvider claimsAudienceProvider, + @Nullable ApplicationConfiguration applicationConfiguration, + @Value('${micronaut.server.port}') Integer port) { super(tokenConfiguration, jwtIdGenerator, claimsAudienceProvider, applicationConfiguration) this.port = port } @@ -210,10 +210,10 @@ class JwksUriSignatureSpec extends Specification { static class AuthServerBCustomJWTClaimsSetGenerator extends JWTClaimsSetGenerator { Integer port AuthServerBCustomJWTClaimsSetGenerator(TokenConfiguration tokenConfiguration, - @Nullable JwtIdGenerator jwtIdGenerator, - @Nullable ClaimsAudienceProvider claimsAudienceProvider, - @Nullable ApplicationConfiguration applicationConfiguration, - @Value('${micronaut.server.port}') Integer port) { + @Nullable JtiGenerator jwtIdGenerator, + @Nullable ClaimsAudienceProvider claimsAudienceProvider, + @Nullable ApplicationConfiguration applicationConfiguration, + @Value('${micronaut.server.port}') Integer port) { super(tokenConfiguration, jwtIdGenerator, claimsAudienceProvider, applicationConfiguration) this.port = port } diff --git a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/client/clientcredentials/ClientCredentialsConcurrentSpec.groovy b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/client/clientcredentials/ClientCredentialsConcurrentSpec.groovy index efdb185b3d..f75780fc90 100644 --- a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/client/clientcredentials/ClientCredentialsConcurrentSpec.groovy +++ b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/client/clientcredentials/ClientCredentialsConcurrentSpec.groovy @@ -31,9 +31,9 @@ import io.micronaut.security.oauth2.endpoint.token.response.TokenResponse import io.micronaut.security.oauth2.grants.GrantType import io.micronaut.security.rules.SecurityRule import io.micronaut.security.token.jwt.endpoints.JwkProvider -import io.micronaut.security.token.jwt.generator.AccessTokenConfiguration +import io.micronaut.security.token.generator.AccessTokenConfiguration import io.micronaut.security.token.jwt.generator.JwtTokenGenerator -import io.micronaut.security.token.jwt.generator.claims.JwtIdGenerator +import io.micronaut.security.token.claims.JtiGenerator import io.micronaut.security.token.jwt.signature.rsa.RSASignatureGeneratorConfiguration import jakarta.inject.Named import jakarta.inject.Singleton @@ -60,7 +60,7 @@ class ClientCredentialsConcurrentSpec extends Specification { @AutoCleanup EmbeddedServer authServer = ApplicationContext.run(EmbeddedServer, [ 'spec.name' : 'ClientCredentialsConcurrentSpecAuthServer', - 'micronaut.security.token.jwt.generator.access-token.expiration': 5, + 'micronaut.security.token.generator.access-token.expiration' : 5, 'authserver.config.jwk' : jwkJsonString(), 'micronaut.server.port' : authServerPort, 'sample.client-id' : '3ljrgej68ggm7i720o9u12t7lm', @@ -152,7 +152,7 @@ class ClientCredentialsConcurrentSpec extends Specification { TokenController(JwtTokenGenerator jwtTokenGenerator, SampleClientConfiguration sampleClientConfiguration, AccessTokenConfiguration accessTokenConfiguration, - @Property(name = 'micronaut.security.token.jwt.generator.access-token.expiration') Integer tokenExpiration) { + @Property(name = 'micronaut.security.token.generator.access-token.expiration') Integer tokenExpiration) { this.jwtTokenGenerator = jwtTokenGenerator this.sampleClientConfiguration = sampleClientConfiguration this.accessTokenConfiguration = accessTokenConfiguration @@ -285,7 +285,7 @@ class ClientCredentialsConcurrentSpec extends Specification { @Requires(property = 'spec.name', value = 'ClientCredentialsConcurrentSpecAuthServer') @Singleton - static class CustomJwtIdGenerator implements JwtIdGenerator { + static class CustomJwtIdGenerator implements JtiGenerator { @Override String generateJtiClaim() { diff --git a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/client/clientcredentials/ClientCredentialsSpec.groovy b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/client/clientcredentials/ClientCredentialsSpec.groovy index eb380d3261..e11da47f83 100644 --- a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/client/clientcredentials/ClientCredentialsSpec.groovy +++ b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/client/clientcredentials/ClientCredentialsSpec.groovy @@ -38,9 +38,9 @@ import io.micronaut.security.oauth2.grants.ClientCredentialsGrant import io.micronaut.security.oauth2.grants.GrantType import io.micronaut.security.rules.SecurityRule import io.micronaut.security.token.jwt.endpoints.JwkProvider -import io.micronaut.security.token.jwt.generator.AccessTokenConfiguration +import io.micronaut.security.token.generator.AccessTokenConfiguration import io.micronaut.security.token.jwt.generator.JwtTokenGenerator -import io.micronaut.security.token.jwt.generator.claims.JwtIdGenerator +import io.micronaut.security.token.claims.JtiGenerator import io.micronaut.security.token.jwt.signature.rsa.RSASignatureGeneratorConfiguration import jakarta.inject.Named import jakarta.inject.Singleton @@ -89,7 +89,7 @@ class ClientCredentialsSpec extends Specification { @AutoCleanup EmbeddedServer authServer = ApplicationContext.run(EmbeddedServer, [ 'spec.name' : 'ClientCredentialsSpecAuthServer', - 'micronaut.security.token.jwt.generator.access-token.expiration': 5, + 'micronaut.security.token.generator.access-token.expiration' : 5, 'authserver.config.jwk' : jwkJsonString(), 'micronaut.server.port' : authServerPort, 'sample.client-id' : '3ljrgej68ggm7i720o9u12t7lm', @@ -100,7 +100,7 @@ class ClientCredentialsSpec extends Specification { @AutoCleanup EmbeddedServer authServerDown = ApplicationContext.run(EmbeddedServer, [ 'spec.name' : 'ClientCredentialsSpecAuthServerDown', - 'micronaut.security.token.jwt.generator.access-token.expiration': 5, + 'micronaut.security.token.generator.access-token.expiration' : 5, 'authserver.config.jwk' : secondaryJwkJsonString, 'micronaut.server.port' : authServerDownPort, ]) @@ -518,7 +518,7 @@ class ClientCredentialsSpec extends Specification { TokenController(JwtTokenGenerator jwtTokenGenerator, SampleClientConfiguration sampleClientConfiguration, AccessTokenConfiguration accessTokenConfiguration, - @Property(name = 'micronaut.security.token.jwt.generator.access-token.expiration') Integer tokenExpiration) { + @Property(name = 'micronaut.security.token.generator.access-token.expiration') Integer tokenExpiration) { this.jwtTokenGenerator = jwtTokenGenerator this.sampleClientConfiguration = sampleClientConfiguration this.accessTokenConfiguration = accessTokenConfiguration @@ -649,7 +649,7 @@ class ClientCredentialsSpec extends Specification { @Requires(property = 'spec.name', value = 'ClientCredentialsSpecAuthServer') @Singleton - static class CustomJwtIdGenerator implements JwtIdGenerator { + static class CustomJwtIdGenerator implements JtiGenerator { @Override String generateJtiClaim() { diff --git a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieCodeChallengeNotSupportedSpec.groovy b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieCodeChallengeNotSupportedSpec.groovy index 08a4178187..050f383a46 100644 --- a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieCodeChallengeNotSupportedSpec.groovy +++ b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieCodeChallengeNotSupportedSpec.groovy @@ -37,16 +37,15 @@ import io.micronaut.security.handlers.RedirectingLoginHandler import io.micronaut.security.oauth2.client.OauthClient import io.micronaut.security.oauth2.endpoint.token.response.OpenIdClaims import io.micronaut.security.oauth2.endpoint.token.response.OpenIdTokenResponse -import io.micronaut.security.oauth2.endpoint.token.response.TokenResponse import io.micronaut.security.oauth2.grants.AuthorizationCodeGrant import io.micronaut.security.rules.SecurityRule import io.micronaut.security.testutils.BrowserHttpRequest +import io.micronaut.security.token.Claims +import io.micronaut.security.token.generator.AccessRefreshTokenGenerator import io.micronaut.security.token.jwt.endpoints.JwkProvider -import io.micronaut.security.token.jwt.generator.AccessRefreshTokenGenerator -import io.micronaut.security.token.jwt.generator.claims.JwtClaims -import io.micronaut.security.token.jwt.render.AccessRefreshToken import io.micronaut.security.token.jwt.signature.rsa.RSASignatureConfiguration import io.micronaut.security.token.jwt.signature.rsa.RSASignatureGeneratorConfiguration +import io.micronaut.security.token.render.AccessRefreshToken import jakarta.inject.Named import jakarta.inject.Singleton import org.slf4j.Logger @@ -203,8 +202,8 @@ class PkceCookieCodeChallengeNotSupportedSpec extends Specification { return HttpResponse.unprocessableEntity() } AccessRefreshToken accessRefreshToken = accessRefreshTokenGenerator.generate(Authentication.build("john", Collections.emptyList(), - CollectionUtils.mapOf(JwtClaims.ISSUER, host + "/oauth2/default", - JwtClaims.AUDIENCE, Collections.singletonList("xxx"), + CollectionUtils.mapOf(Claims.ISSUER, host + "/oauth2/default", + Claims.AUDIENCE, Collections.singletonList("xxx"), OpenIdClaims.CLAIMS_NONCE, nonce))).get() OpenIdTokenResponse openIdTokenResponse = new OpenIdTokenResponse() openIdTokenResponse.setIdToken(accessRefreshToken.getAccessToken()) diff --git a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieWithPlainSpec.groovy b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieWithPlainSpec.groovy index 5ce67ee5fd..ca48ac9b38 100644 --- a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieWithPlainSpec.groovy +++ b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieWithPlainSpec.groovy @@ -37,16 +37,15 @@ import io.micronaut.security.handlers.RedirectingLoginHandler import io.micronaut.security.oauth2.client.OauthClient import io.micronaut.security.oauth2.endpoint.token.response.OpenIdClaims import io.micronaut.security.oauth2.endpoint.token.response.OpenIdTokenResponse -import io.micronaut.security.oauth2.endpoint.token.response.TokenResponse import io.micronaut.security.oauth2.grants.AuthorizationCodeGrant import io.micronaut.security.rules.SecurityRule import io.micronaut.security.testutils.BrowserHttpRequest +import io.micronaut.security.token.Claims +import io.micronaut.security.token.generator.AccessRefreshTokenGenerator import io.micronaut.security.token.jwt.endpoints.JwkProvider -import io.micronaut.security.token.jwt.generator.AccessRefreshTokenGenerator -import io.micronaut.security.token.jwt.generator.claims.JwtClaims -import io.micronaut.security.token.jwt.render.AccessRefreshToken import io.micronaut.security.token.jwt.signature.rsa.RSASignatureConfiguration import io.micronaut.security.token.jwt.signature.rsa.RSASignatureGeneratorConfiguration +import io.micronaut.security.token.render.AccessRefreshToken import jakarta.inject.Named import jakarta.inject.Singleton import org.slf4j.Logger @@ -210,8 +209,8 @@ class PkceCookieWithPlainSpec extends Specification { return HttpResponse.unprocessableEntity() } AccessRefreshToken accessRefreshToken = accessRefreshTokenGenerator.generate(Authentication.build("john", Collections.emptyList(), - CollectionUtils.mapOf(JwtClaims.ISSUER, host + "/oauth2/default", - JwtClaims.AUDIENCE, Collections.singletonList("xxx"), + CollectionUtils.mapOf(Claims.ISSUER, host + "/oauth2/default", + Claims.AUDIENCE, Collections.singletonList("xxx"), OpenIdClaims.CLAIMS_NONCE, nonce))).get() OpenIdTokenResponse openIdTokenResponse = new OpenIdTokenResponse() openIdTokenResponse.setIdToken(accessRefreshToken.getAccessToken()) diff --git a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieWithS256NoOpenIdNoChallengeMethodSpec.groovy b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieWithS256NoOpenIdNoChallengeMethodSpec.groovy index 858ca845e4..39ec13cd07 100644 --- a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieWithS256NoOpenIdNoChallengeMethodSpec.groovy +++ b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieWithS256NoOpenIdNoChallengeMethodSpec.groovy @@ -46,12 +46,12 @@ import io.micronaut.security.oauth2.endpoint.token.response.TokenResponse import io.micronaut.security.oauth2.grants.AuthorizationCodeGrant import io.micronaut.security.rules.SecurityRule import io.micronaut.security.testutils.BrowserHttpRequest +import io.micronaut.security.token.Claims +import io.micronaut.security.token.generator.AccessRefreshTokenGenerator import io.micronaut.security.token.jwt.endpoints.JwkProvider -import io.micronaut.security.token.jwt.generator.AccessRefreshTokenGenerator -import io.micronaut.security.token.jwt.generator.claims.JwtClaims -import io.micronaut.security.token.jwt.render.AccessRefreshToken import io.micronaut.security.token.jwt.signature.rsa.RSASignatureConfiguration import io.micronaut.security.token.jwt.signature.rsa.RSASignatureGeneratorConfiguration +import io.micronaut.security.token.render.AccessRefreshToken import jakarta.inject.Named import jakarta.inject.Singleton import org.reactivestreams.Publisher @@ -228,8 +228,8 @@ class PkceCookieWithS256NoOpenIdNoChallengeMethodSpec extends Specification { return HttpResponse.unprocessableEntity() } AccessRefreshToken accessRefreshToken = accessRefreshTokenGenerator.generate(Authentication.build("john", Collections.emptyList(), - CollectionUtils.mapOf(JwtClaims.ISSUER, host + "/oauth2/default", - JwtClaims.AUDIENCE, Collections.singletonList("xxx"), + CollectionUtils.mapOf(Claims.ISSUER, host + "/oauth2/default", + Claims.AUDIENCE, Collections.singletonList("xxx"), OpenIdClaims.CLAIMS_NONCE, nonce))).get() OpenIdTokenResponse openIdTokenResponse = new OpenIdTokenResponse() openIdTokenResponse.setIdToken(accessRefreshToken.getAccessToken()) diff --git a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieWithS256NoOpenIdSpec.groovy b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieWithS256NoOpenIdSpec.groovy index 9cba04a119..2a08dfb7e9 100644 --- a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieWithS256NoOpenIdSpec.groovy +++ b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieWithS256NoOpenIdSpec.groovy @@ -46,12 +46,12 @@ import io.micronaut.security.oauth2.endpoint.token.response.TokenResponse import io.micronaut.security.oauth2.grants.AuthorizationCodeGrant import io.micronaut.security.rules.SecurityRule import io.micronaut.security.testutils.BrowserHttpRequest +import io.micronaut.security.token.Claims +import io.micronaut.security.token.generator.AccessRefreshTokenGenerator import io.micronaut.security.token.jwt.endpoints.JwkProvider -import io.micronaut.security.token.jwt.generator.AccessRefreshTokenGenerator -import io.micronaut.security.token.jwt.generator.claims.JwtClaims -import io.micronaut.security.token.jwt.render.AccessRefreshToken import io.micronaut.security.token.jwt.signature.rsa.RSASignatureConfiguration import io.micronaut.security.token.jwt.signature.rsa.RSASignatureGeneratorConfiguration +import io.micronaut.security.token.render.AccessRefreshToken import jakarta.inject.Named import jakarta.inject.Singleton import org.reactivestreams.Publisher @@ -234,8 +234,8 @@ class PkceCookieWithS256NoOpenIdSpec extends Specification { return HttpResponse.unprocessableEntity() } AccessRefreshToken accessRefreshToken = accessRefreshTokenGenerator.generate(Authentication.build("john", Collections.emptyList(), - CollectionUtils.mapOf(JwtClaims.ISSUER, host + "/oauth2/default", - JwtClaims.AUDIENCE, Collections.singletonList("xxx"), + CollectionUtils.mapOf(Claims.ISSUER, host + "/oauth2/default", + Claims.AUDIENCE, Collections.singletonList("xxx"), OpenIdClaims.CLAIMS_NONCE, nonce))).get() OpenIdTokenResponse openIdTokenResponse = new OpenIdTokenResponse() openIdTokenResponse.setIdToken(accessRefreshToken.getAccessToken()) diff --git a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieWithS256Spec.groovy b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieWithS256Spec.groovy index ffba0c6a82..750b486edc 100644 --- a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieWithS256Spec.groovy +++ b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceCookieWithS256Spec.groovy @@ -37,16 +37,15 @@ import io.micronaut.security.handlers.RedirectingLoginHandler import io.micronaut.security.oauth2.client.OauthClient import io.micronaut.security.oauth2.endpoint.token.response.OpenIdClaims import io.micronaut.security.oauth2.endpoint.token.response.OpenIdTokenResponse -import io.micronaut.security.oauth2.endpoint.token.response.TokenResponse import io.micronaut.security.oauth2.grants.AuthorizationCodeGrant import io.micronaut.security.rules.SecurityRule import io.micronaut.security.testutils.BrowserHttpRequest +import io.micronaut.security.token.Claims +import io.micronaut.security.token.generator.AccessRefreshTokenGenerator import io.micronaut.security.token.jwt.endpoints.JwkProvider -import io.micronaut.security.token.jwt.generator.AccessRefreshTokenGenerator -import io.micronaut.security.token.jwt.generator.claims.JwtClaims -import io.micronaut.security.token.jwt.render.AccessRefreshToken import io.micronaut.security.token.jwt.signature.rsa.RSASignatureConfiguration import io.micronaut.security.token.jwt.signature.rsa.RSASignatureGeneratorConfiguration +import io.micronaut.security.token.render.AccessRefreshToken import jakarta.inject.Named import jakarta.inject.Singleton import org.slf4j.Logger @@ -208,8 +207,8 @@ class PkceCookieWithS256Spec extends Specification { return HttpResponse.unprocessableEntity() } AccessRefreshToken accessRefreshToken = accessRefreshTokenGenerator.generate(Authentication.build("john", Collections.emptyList(), - CollectionUtils.mapOf(JwtClaims.ISSUER, host + "/oauth2/default", - JwtClaims.AUDIENCE, Collections.singletonList("xxx"), + CollectionUtils.mapOf(Claims.ISSUER, host + "/oauth2/default", + Claims.AUDIENCE, Collections.singletonList("xxx"), OpenIdClaims.CLAIMS_NONCE, nonce))).get() OpenIdTokenResponse openIdTokenResponse = new OpenIdTokenResponse() openIdTokenResponse.setIdToken(accessRefreshToken.getAccessToken()) diff --git a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceSessionWithS256Spec.groovy b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceSessionWithS256Spec.groovy index 952f2c2d54..0b3283c606 100644 --- a/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceSessionWithS256Spec.groovy +++ b/security-oauth2/src/test/groovy/io/micronaut/security/oauth2/endpoint/authorization/pkce/PkceSessionWithS256Spec.groovy @@ -37,16 +37,15 @@ import io.micronaut.security.handlers.RedirectingLoginHandler import io.micronaut.security.oauth2.client.OauthClient import io.micronaut.security.oauth2.endpoint.token.response.OpenIdClaims import io.micronaut.security.oauth2.endpoint.token.response.OpenIdTokenResponse -import io.micronaut.security.oauth2.endpoint.token.response.TokenResponse import io.micronaut.security.oauth2.grants.AuthorizationCodeGrant import io.micronaut.security.rules.SecurityRule import io.micronaut.security.testutils.BrowserHttpRequest +import io.micronaut.security.token.Claims +import io.micronaut.security.token.generator.AccessRefreshTokenGenerator import io.micronaut.security.token.jwt.endpoints.JwkProvider -import io.micronaut.security.token.jwt.generator.AccessRefreshTokenGenerator -import io.micronaut.security.token.jwt.generator.claims.JwtClaims -import io.micronaut.security.token.jwt.render.AccessRefreshToken import io.micronaut.security.token.jwt.signature.rsa.RSASignatureConfiguration import io.micronaut.security.token.jwt.signature.rsa.RSASignatureGeneratorConfiguration +import io.micronaut.security.token.render.AccessRefreshToken import jakarta.inject.Named import jakarta.inject.Singleton import org.slf4j.Logger @@ -209,8 +208,8 @@ class PkceSessionWithS256Spec extends Specification { return HttpResponse.unprocessableEntity() } AccessRefreshToken accessRefreshToken = accessRefreshTokenGenerator.generate(Authentication.build("john", Collections.emptyList(), - CollectionUtils.mapOf(JwtClaims.ISSUER, host + "/oauth2/default", - JwtClaims.AUDIENCE, Collections.singletonList("xxx"), + CollectionUtils.mapOf(Claims.ISSUER, host + "/oauth2/default", + Claims.AUDIENCE, Collections.singletonList("xxx"), OpenIdClaims.CLAIMS_NONCE, nonce))).get() OpenIdTokenResponse openIdTokenResponse = new OpenIdTokenResponse() openIdTokenResponse.setIdToken(accessRefreshToken.getAccessToken()) diff --git a/security-paseto/build.gradle.kts b/security-paseto/build.gradle.kts new file mode 100644 index 0000000000..72ca72929e --- /dev/null +++ b/security-paseto/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + id("io.micronaut.build.internal.security-module") +} +dependencies { + api(mn.micronaut.http) + api(mn.micronaut.http.server) + api(projects.micronautSecurity) + api(libs.managed.jpaseto.api) + implementation(libs.managed.jpaseto.bouncy.castle) + implementation(libs.managed.jpaseto.impl) + implementation(libs.managed.jpaseto.jackson) + implementation(mnReactor.micronaut.reactor) + testImplementation(mn.micronaut.http.client) + testAnnotationProcessor(mn.micronaut.inject.java) + testImplementation(mn.micronaut.http.server.netty) + testImplementation(projects.testSuiteUtils) + testImplementation(projects.testSuiteUtilsSecurity) +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/LocalGeneratorConfigurationProperties.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/LocalGeneratorConfigurationProperties.java new file mode 100644 index 0000000000..7fa4d8d191 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/LocalGeneratorConfigurationProperties.java @@ -0,0 +1,73 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.config; + +import dev.paseto.jpaseto.Version; +import io.micronaut.context.annotation.ConfigurationProperties; +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.annotation.NonNull; +import jakarta.inject.Named; + +import javax.crypto.SecretKey; +import jakarta.validation.constraints.NotNull; + +/** + * @author Sergio del Amo + * @since 3.1.0 + */ +@Requires(property = PasetoConfigurationProperties.PREFIX + ".local-generator.base64-shared-secret") +@ConfigurationProperties(PasetoConfigurationProperties.PREFIX + ".local-generator") +@Named("local-generator") +public class LocalGeneratorConfigurationProperties implements VersionedSharedSecretConfiguration { + + public static final Version DEFAULT_VERSION = Version.V1; + + @NotNull + @NonNull + private Version version = DEFAULT_VERSION; + + @NonNull + @NotNull + private SecretKey base64SharedSecret; + + @Override + @NonNull + public Version getVersion() { + return version; + } + + /** + * Paseto version. Defaults to v1. + * @param version Paseto version + */ + public void setVersion(@NonNull Version version) { + this.version = version; + } + + @Override + @NonNull + public SecretKey getSharedSecret() { + return base64SharedSecret; + } + + /** + * shared secret used for Paseto token base64 encoded. + * @param base64SharedSecret shared secret used for Paseto token base64 encoded. + */ + public void setBase64SharedSecret(@NonNull SecretKey base64SharedSecret) { + this.base64SharedSecret = base64SharedSecret; + } +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/LocalValidators.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/LocalValidators.java new file mode 100644 index 0000000000..9199217796 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/LocalValidators.java @@ -0,0 +1,136 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.config; + +import io.micronaut.context.annotation.EachProperty; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.core.annotation.Nullable; +import jakarta.inject.Singleton; + +import javax.crypto.SecretKey; +import jakarta.validation.constraints.NotNull; + +/** + * {@link EachProperty} implementation of {@link SharedSecretConfiguration}. + */ +@Singleton +@EachProperty(PasetoConfigurationProperties.PREFIX + ".local-validators") +public class LocalValidators implements SharedSecretConfiguration { + + @NonNull + @NotNull + private SecretKey base64SharedSecret; + + @Nullable + private String requiredAudience; + + @Nullable + private String requiredKeyId; + + @Nullable + private String requiredIssuer; + + @Nullable + private String requiredSubject; + + @Nullable + private String requiredTokenId; + + @Override + @NonNull + public SecretKey getSharedSecret() { + return base64SharedSecret; + } + + @Nullable + @Override + public String getRequiredAudience() { + return this.requiredAudience; + } + + @Override + @Nullable + public String getRequiredKeyId() { + return requiredKeyId; + } + + @Override + @Nullable + public String getRequiredIssuer() { + return requiredIssuer; + } + + @Override + @Nullable + public String getRequiredSubject() { + return requiredSubject; + } + + @Override + @Nullable + public String getRequiredTokenId() { + return requiredTokenId; + } + + /** + * The required value for the required token id. + * @param requiredTokenId The required value for the token id + */ + public void setRequiredTokenId(@Nullable String requiredTokenId) { + this.requiredTokenId = requiredTokenId; + } + + /** + * Shared Secret. + * @param base64SharedSecret Shared Secret + */ + public void setBase64SharedSecret(@NonNull SecretKey base64SharedSecret) { + this.base64SharedSecret = base64SharedSecret; + } + + /** + * The required value for the audience claim. + * @param requiredAudience The required value for the audience claim. + */ + public void setRequiredAudience(@Nullable String requiredAudience) { + this.requiredAudience = requiredAudience; + } + + /** + * The required value for the keyId. + * @param requiredKeyId The required value of the keyId + */ + public void setRequiredKeyId(@Nullable String requiredKeyId) { + this.requiredKeyId = requiredKeyId; + } + + /** + * The required issuer value. + * @param requiredIssuer The required issuer value + */ + public void setRequiredIssuer(@Nullable String requiredIssuer) { + this.requiredIssuer = requiredIssuer; + } + + /** + * The required subject value. + * @param requiredSubject The required subject value + */ + public void setRequiredSubject(@Nullable String requiredSubject) { + this.requiredSubject = requiredSubject; + } + +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/PasetoConfiguration.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/PasetoConfiguration.java new file mode 100644 index 0000000000..5753ad2ed1 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/PasetoConfiguration.java @@ -0,0 +1,28 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.config; + +import io.micronaut.core.util.Toggleable; + +/** + * Represents configuration of the PASETO token. + * + * @author Utsav Varia + * @since 3.0 + */ +public interface PasetoConfiguration extends Toggleable { + +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/PasetoConfigurationProperties.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/PasetoConfigurationProperties.java new file mode 100644 index 0000000000..7f3cd47cb9 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/PasetoConfigurationProperties.java @@ -0,0 +1,57 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.config; + +import io.micronaut.context.annotation.ConfigurationProperties; +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.util.StringUtils; +import io.micronaut.security.token.config.TokenConfigurationProperties; + +/** + * {@link PasetoConfiguration} implementation. + * + * @author Utsav Varia + * @since 3.0 + */ +@Requires(property = TokenConfigurationProperties.PREFIX + ".enabled", notEquals = StringUtils.FALSE) +@ConfigurationProperties(PasetoConfigurationProperties.PREFIX) +public class PasetoConfigurationProperties implements PasetoConfiguration { + + public static final String PREFIX = TokenConfigurationProperties.PREFIX + ".paseto"; + /** + * The default enable value. + */ + public static final boolean DEFAULT_ENABLED = true; + + private boolean enabled = DEFAULT_ENABLED; + + /** + * + * @return a boolean flag indicating whether PASETO beans should be enabled or not + */ + @Override + public boolean isEnabled() { + return enabled; + } + + /** + * Sets whether Paseto security is enabled. Default value ({@value #DEFAULT_ENABLED}). + * @param enabled True if it is + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/PrivateKeyConfiguration.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/PrivateKeyConfiguration.java new file mode 100644 index 0000000000..719559918b --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/PrivateKeyConfiguration.java @@ -0,0 +1,42 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.config; + +import dev.paseto.jpaseto.Version; +import io.micronaut.core.annotation.NonNull; +import java.security.PrivateKey; + +/** + * Private Key Configuration used to sign a Paseto token. + * @author Sergio del Amo + * @since 3.2.0 + */ +public interface PrivateKeyConfiguration { + + /** + * + * @return The Paseto Version + */ + @NonNull + Version getVersion(); + + /** + * + * @return Private Key used to sign the Paseto token + */ + @NonNull + PrivateKey getPrivateKey(); +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/PublicKeyConfiguration.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/PublicKeyConfiguration.java new file mode 100644 index 0000000000..ecee0c48e1 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/PublicKeyConfiguration.java @@ -0,0 +1,34 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.config; + +import io.micronaut.core.annotation.NonNull; +import java.security.PublicKey; + +/** + * Public Key Configuration used to parse/verify a Paseto token. + * @author Sergio del Amo + * @since 3.2.0 + */ +public interface PublicKeyConfiguration extends RequiredConfiguration { + + /** + * + * @return Private Key used to sign the Paseto token + */ + @NonNull + PublicKey getPublicKey(); +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/RequiredConfiguration.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/RequiredConfiguration.java new file mode 100644 index 0000000000..3d16988ce8 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/RequiredConfiguration.java @@ -0,0 +1,83 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.config; + +import io.micronaut.core.annotation.Nullable; +import java.time.Instant; +import java.util.Map; +import java.util.function.Predicate; + +/** + * Encapsulates required configuration. + * @author Sergio del Amo + * @since 3.2.0 + */ +public interface RequiredConfiguration { + @Nullable + default String getRequiredAudience() { + return null; + } + + @Nullable + default String getRequiredKeyId() { + return null; + } + + @Nullable + default String getRequiredIssuer() { + return null; + } + + @Nullable + default String getRequiredSubject() { + return null; + } + + @Nullable + default String getRequiredTokenId() { + return null; + } + + @Nullable + default Instant getRequiredExpiration() { + return null; + } + + @Nullable + default Instant getRequiredIssuedAt() { + return null; + } + + @Nullable + default Instant getRequiredNotBefore() { + return null; + } + + @Nullable + default Map> getRequiredClaimsPredicate() { + return null; + } + + @Nullable + default Map getRequiredClaimsValue() { + return null; + } + + @Nullable + default Map> getRequiredFooterPredicate() { + return null; + } +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/SharedSecretConfiguration.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/SharedSecretConfiguration.java new file mode 100644 index 0000000000..3480afe180 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/SharedSecretConfiguration.java @@ -0,0 +1,29 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.config; + +import io.micronaut.core.annotation.NonNull; +import javax.crypto.SecretKey; + +/** + * @author Sergio del Amo + * @since 3.2.0 + */ +public interface SharedSecretConfiguration extends RequiredConfiguration { + + @NonNull + SecretKey getSharedSecret(); +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/VersionedSharedSecretConfiguration.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/VersionedSharedSecretConfiguration.java new file mode 100644 index 0000000000..5418d2ef8a --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/VersionedSharedSecretConfiguration.java @@ -0,0 +1,34 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.config; + +import dev.paseto.jpaseto.Version; +import io.micronaut.core.annotation.NonNull; + +/** + * Extension of {@link SharedSecretConfiguration} adding the Paseto Version. + * @author Sergio del Amo + * @since 3.2.0 + */ +public interface VersionedSharedSecretConfiguration extends SharedSecretConfiguration { + + /** + * + * @return The Paseto Version + */ + @NonNull + Version getVersion(); +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/package-info.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/package-info.java new file mode 100644 index 0000000000..e8c6de1686 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/config/package-info.java @@ -0,0 +1,22 @@ +/* + * Copyright 2017-2021 original 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. + */ +/** + * PASETO configuration. + * + * @author Utsav Varia + * @since 3.0 + */ +package io.micronaut.security.token.paseto.config; diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/converters/PurposeConveter.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/converters/PurposeConveter.java new file mode 100644 index 0000000000..62851da570 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/converters/PurposeConveter.java @@ -0,0 +1,53 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.converters; + +import dev.paseto.jpaseto.Purpose; +import dev.paseto.jpaseto.UnsupportedPasetoException; +import io.micronaut.core.convert.ConversionContext; +import io.micronaut.core.convert.TypeConverter; +import jakarta.inject.Singleton; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Optional; + +/** + * Converts a {@link Purpose} to a {@link javax.crypto.SecretKey}. + * + * @author Sergio del Amo + * @since 3.2.0 + */ +@Singleton +public class PurposeConveter implements TypeConverter { + private static final Logger LOG = LoggerFactory.getLogger(PurposeConveter.class); + + @Override + public Optional convert(CharSequence object, Class targetType, ConversionContext context) { + if (object == null) { + return Optional.empty(); + } + String value = object.toString(); + try { + return Optional.of(Purpose.from(value)); + } catch (UnsupportedPasetoException e) { + if (LOG.isWarnEnabled()) { + LOG.warn("could not parse purpose", e); + } + } + return Optional.empty(); + } +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/converters/SecretKeyConverter.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/converters/SecretKeyConverter.java new file mode 100644 index 0000000000..6f75a2dae2 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/converters/SecretKeyConverter.java @@ -0,0 +1,44 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.converters; + +import dev.paseto.jpaseto.lang.Keys; +import io.micronaut.core.convert.ConversionContext; +import io.micronaut.core.convert.TypeConverter; +import jakarta.inject.Singleton; +import javax.crypto.SecretKey; +import java.util.Base64; +import java.util.Optional; + +/** + * Converts a {@link CharSequence} base64 encoded to a {@link javax.crypto.SecretKey}. + * + * @author Sergio del Amo + * @since 3.2.0 + */ +@Singleton +public class SecretKeyConverter implements TypeConverter { + + @Override + public Optional convert(CharSequence object, Class targetType, ConversionContext context) { + if (object == null) { + return Optional.empty(); + } + String value = object.toString(); + byte[] decodedBytes = Base64.getDecoder().decode(value); + return Optional.of(Keys.secretKey(decodedBytes)); + } +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/converters/VersionConverter.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/converters/VersionConverter.java new file mode 100644 index 0000000000..14eee8a7aa --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/converters/VersionConverter.java @@ -0,0 +1,53 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.converters; + +import dev.paseto.jpaseto.Version; +import dev.paseto.jpaseto.UnsupportedPasetoException; +import io.micronaut.core.convert.ConversionContext; +import io.micronaut.core.convert.TypeConverter; +import jakarta.inject.Singleton; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.Optional; + +/** + * Converts a {@link CharSequence} to a {@link Version}. + * + * @author Sergio del Amo + * @since 3.2.0 + */ +@Singleton +public class VersionConverter implements TypeConverter { + + private static final Logger LOG = LoggerFactory.getLogger(VersionConverter.class); + + @Override + public Optional convert(CharSequence object, Class targetType, ConversionContext context) { + if (object == null) { + return Optional.empty(); + } + String value = object.toString(); + try { + return Optional.of(Version.from(value)); + } catch (UnsupportedPasetoException e) { + if (LOG.isWarnEnabled()) { + LOG.warn("could not parse version", e); + } + } + return Optional.empty(); + } +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/converters/package-info.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/converters/package-info.java new file mode 100644 index 0000000000..aaee19c4f9 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/converters/package-info.java @@ -0,0 +1,22 @@ +/* + * Copyright 2017-2021 original 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. + */ +/** + * Security related Converters. + * + * @author Sergio del Amo Caballero + * @since 1.0 + */ +package io.micronaut.security.token.paseto.converters; diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/LocalPasetoBuilderGenerator.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/LocalPasetoBuilderGenerator.java new file mode 100644 index 0000000000..6821bf8fd9 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/LocalPasetoBuilderGenerator.java @@ -0,0 +1,52 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.generator; + +import dev.paseto.jpaseto.PasetoBuilder; +import dev.paseto.jpaseto.Pasetos; +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.security.token.paseto.config.VersionedSharedSecretConfiguration; +import jakarta.inject.Singleton; + +/** + * Implementation of {@link PasetoBuilderGenerator} for Paseto Purpose {@link dev.paseto.jpaseto.Purpose#LOCAL}. + * @author Sergio del Amo + * @since 3.2.0 + */ +@Requires(beans = VersionedSharedSecretConfiguration.class) +@Singleton +public class LocalPasetoBuilderGenerator implements PasetoBuilderGenerator { + + private final VersionedSharedSecretConfiguration configuration; + + public LocalPasetoBuilderGenerator(VersionedSharedSecretConfiguration configuration) { + this.configuration = configuration; + } + + @Override + @NonNull + public PasetoBuilder builder() { + switch (configuration.getVersion()) { + case V2: + return Pasetos.V2.LOCAL.builder().setSharedSecret(configuration.getSharedSecret()); + case V1: + default: + return Pasetos.V1.LOCAL.builder().setSharedSecret(configuration.getSharedSecret()); + + } + } +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/PasetoBuilderGenerator.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/PasetoBuilderGenerator.java new file mode 100644 index 0000000000..ebc1298581 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/PasetoBuilderGenerator.java @@ -0,0 +1,35 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.generator; + +import dev.paseto.jpaseto.PasetoBuilder; +import io.micronaut.core.annotation.NonNull; + +/** + * Returns a {@link PasetoBuilder} which will be used to sign a Paseto token. + * @author Sergio del Amo + * @since 3.2.0 + */ +@FunctionalInterface +public interface PasetoBuilderGenerator { + + /** + * + * @return a {@link PasetoBuilder} which will be used to sign a Paseto token. + */ + @NonNull + PasetoBuilder builder(); +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/PasetoTokenGenerator.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/PasetoTokenGenerator.java new file mode 100644 index 0000000000..95b589e2bb --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/PasetoTokenGenerator.java @@ -0,0 +1,70 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.generator; + +import dev.paseto.jpaseto.PasetoBuilder; +import io.micronaut.security.authentication.Authentication; +import io.micronaut.security.token.generator.TokenGenerator; +import io.micronaut.security.token.claims.ClaimsGenerator; +import jakarta.inject.Singleton; +import java.util.Map; +import java.util.Optional; + +/** + * @author Utsav Varia + * @since 3.0 + */ +@Singleton +public class PasetoTokenGenerator implements TokenGenerator { + + protected final ClaimsGenerator claimsGenerator; + private final PasetoBuilderGenerator pasetoBuilderGenerator; + + /** + * + * @param claimsGenerator Claims Generator + * @param pasetoBuilderGenerator Paseto Builder Generator + */ + public PasetoTokenGenerator(ClaimsGenerator claimsGenerator, + PasetoBuilderGenerator pasetoBuilderGenerator) { + this.claimsGenerator = claimsGenerator; + this.pasetoBuilderGenerator = pasetoBuilderGenerator; + } + + /** + * Generate a Paseto from a map of claims. + * + * @param claims the map of claims + * @return the created Paseto + */ + private String generate(final Map claims) { + final PasetoBuilder builder = pasetoBuilderGenerator.builder(); + for (final Map.Entry entry : claims.entrySet()) { + builder.claim(entry.getKey(), entry.getValue()); + } + return builder.compact(); + } + + @Override + public Optional generateToken(Authentication authentication, Integer expiration) { + return generateToken(claimsGenerator.generateClaims(authentication, expiration)); + } + + @Override + public Optional generateToken(Map claims) { + return Optional.of(generate(claims)); + } +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/PublicPasetoBuilderGenerator.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/PublicPasetoBuilderGenerator.java new file mode 100644 index 0000000000..ff72d3d853 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/PublicPasetoBuilderGenerator.java @@ -0,0 +1,50 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.generator; + +import dev.paseto.jpaseto.PasetoBuilder; +import dev.paseto.jpaseto.Pasetos; +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.security.token.paseto.config.PrivateKeyConfiguration; +import jakarta.inject.Singleton; + +/** + * Implementation of {@link PasetoBuilderGenerator} for Paseto Purpose {@link dev.paseto.jpaseto.Purpose#PUBLIC}. + * @author Sergio del Amo + * @since 3.2.0 + */ +@Requires(beans = PrivateKeyConfiguration.class) +@Singleton +public class PublicPasetoBuilderGenerator implements PasetoBuilderGenerator { + private final PrivateKeyConfiguration configuration; + + public PublicPasetoBuilderGenerator(PrivateKeyConfiguration configuration) { + this.configuration = configuration; + } + + @Override + @NonNull + public PasetoBuilder builder() { + switch (configuration.getVersion()) { + case V2: + return Pasetos.V2.PUBLIC.builder().setPrivateKey(configuration.getPrivateKey()); + case V1: + default: + return Pasetos.V1.PUBLIC.builder().setPrivateKey(configuration.getPrivateKey()); + } + } +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/claims/ClaimsAudienceProvider.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/claims/ClaimsAudienceProvider.java new file mode 100644 index 0000000000..a757bf35e2 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/claims/ClaimsAudienceProvider.java @@ -0,0 +1,30 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.generator.claims; + +import io.micronaut.core.annotation.NonNull; + +/** + * @author Utsav Varia + * @since 3.0 + */ +public interface ClaimsAudienceProvider { + /** + * @return a List of Paseto recipients identifiers. + */ + @NonNull + String audience(); +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/claims/PasetoClaimsGenerator.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/claims/PasetoClaimsGenerator.java new file mode 100644 index 0000000000..e4421a4e94 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/claims/PasetoClaimsGenerator.java @@ -0,0 +1,199 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.generator.claims; + +import io.micronaut.context.env.Environment; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.core.annotation.Nullable; +import io.micronaut.runtime.ApplicationConfiguration; +import io.micronaut.security.authentication.Authentication; +import io.micronaut.security.token.Claims; +import io.micronaut.security.token.claims.ClaimsGenerator; +import io.micronaut.security.token.config.TokenConfiguration; +import jakarta.inject.Singleton; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author Utsav Varia + * @since 3.0 + */ +@Singleton +public class PasetoClaimsGenerator implements ClaimsGenerator { + + private static final Logger LOG = LoggerFactory.getLogger(PasetoClaimsGenerator.class); + private static final String ROLES_KEY = "rolesKey"; + + private final TokenConfiguration tokenConfiguration; + private final ClaimsAudienceProvider claimsAudienceProvider; + private final PasetoIdGenerator pasetoIdGenerator; + private final String appName; + + /** + * @param tokenConfiguration Token Configuration + * @param claimsAudienceProvider Generator which creates unique JWT ID + * @param pasetoIdGenerator Provider which identifies the recipients that the JWT is intended for. + * @param applicationConfiguration The application configuration + */ + public PasetoClaimsGenerator(TokenConfiguration tokenConfiguration, + @Nullable ClaimsAudienceProvider claimsAudienceProvider, + @Nullable PasetoIdGenerator pasetoIdGenerator, + @Nullable ApplicationConfiguration applicationConfiguration) { + this.tokenConfiguration = tokenConfiguration; + this.claimsAudienceProvider = claimsAudienceProvider; + this.pasetoIdGenerator = pasetoIdGenerator; + this.appName = applicationConfiguration != null ? applicationConfiguration.getName().orElse(Environment.MICRONAUT) : Environment.MICRONAUT; + } + + /** + * Populates sub claim. + * + * @param builder The Paseto Claims Builder + * @param authentication Authenticated user's representation. + */ + protected void populateSub(PasetoClaimsSet.Builder builder, Authentication authentication) { + builder.subject(authentication.getName()); // sub + } + + /** + * Populates iss claim. + * + * @param builder The Claims Builder + */ + protected void populateIss(PasetoClaimsSet.Builder builder) { + if (appName != null) { + builder.issuer(appName); // iss + } + } + + /** + * Populates aud claim. + * + * @param builder The Claims Builder + */ + protected void populateAud(PasetoClaimsSet.Builder builder) { + if (claimsAudienceProvider != null) { + builder.audience(claimsAudienceProvider.audience()); // aud + } + } + + /** + * Populates exp claim. + * + * @param builder The Claims Builder + * @param expiration expiration time in seconds + */ + protected void populateExp(PasetoClaimsSet.Builder builder, @Nullable Integer expiration) { + if (expiration != null) { + LOG.debug("Setting expiration to {}", expiration); + builder.expiration(Instant.now().plus(expiration, ChronoUnit.SECONDS)); // exp + } + } + + /** + * Populates nbf claim. + * + * @param builder The Claims Builder + */ + protected void populateNbf(PasetoClaimsSet.Builder builder) { + builder.notBefore(Instant.now()); // nbf + } + + /** + * Populates iat claim. + * + * @param builder The Claims Builder + */ + protected void populateIat(PasetoClaimsSet.Builder builder) { + builder.issuedAt(Instant.now()); // iat + } + + /** + * Populates jti claim. + * + * @param builder The Claims Builder + */ + protected void populateJti(PasetoClaimsSet.Builder builder) { + if (pasetoIdGenerator != null) { + builder.tokenId(pasetoIdGenerator.generateJtiClaim()); // jti + } + } + + @Override + @NonNull + public Map generateClaims(Authentication authentication, Integer expiration) { + PasetoClaimsSet.Builder builder = new PasetoClaimsSet.Builder(); + + populateIat(builder); + populateExp(builder, expiration); + populateJti(builder); + populateIss(builder); + populateAud(builder); + populateNbf(builder); + populateWithAuthentication(builder, authentication); + + //TODO: Add Support for footer in token + + PasetoClaimsSet claimsSet = builder.build(); + if (LOG.isDebugEnabled()) { + LOG.debug("Generated claim set:"); + LOG.debug("{"); + claimsSet.getClaims().forEach((key, value) -> LOG.debug("\t{} : {}", key, value)); + LOG.debug("}"); + } + return claimsSet.getClaims(); + } + + @Override + public Map generateClaimsSet(Map oldClaims, Integer expiration) { + + PasetoClaimsSet.Builder builder = new PasetoClaimsSet.Builder(); + List excludedClaims = Arrays.asList(Claims.EXPIRATION_TIME, Claims.ISSUED_AT, Claims.NOT_BEFORE); + for (String k : oldClaims.keySet() + .stream() + .filter(p -> !excludedClaims.contains(p)) + .collect(Collectors.toList())) { + builder.claim(k, oldClaims.get(k)); + } + populateExp(builder, expiration); + populateIat(builder); + populateNbf(builder); + return builder.build().getClaims(); + } + + /** + * Populates Claims with Authentication object. + * + * @param builder the Claims Builder + * @param authentication Authenticated user's representation. + */ + protected void populateWithAuthentication(PasetoClaimsSet.Builder builder, Authentication authentication) { + populateSub(builder, authentication); + authentication.getAttributes().forEach(builder::claim); + String rolesKey = tokenConfiguration.getRolesName(); + if (!rolesKey.equalsIgnoreCase(TokenConfiguration.DEFAULT_ROLES_NAME)) { + builder.claim(ROLES_KEY, rolesKey); + } + builder.claim(rolesKey, authentication.getRoles()); + } +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/claims/PasetoClaimsSet.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/claims/PasetoClaimsSet.java new file mode 100644 index 0000000000..10f5d1840a --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/claims/PasetoClaimsSet.java @@ -0,0 +1,166 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.generator.claims; + +import dev.paseto.jpaseto.Claims; + +import java.time.Instant; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author Utsav Varia + * @since 3.0 + */ +public final class PasetoClaimsSet { + + private final Map claims = new LinkedHashMap<>(); + + private PasetoClaimsSet(Map claims) { + this.claims.putAll(claims); + } + + public Map getClaims() { + return claims; + } + + public Instant getNotBeforeTime() { + Object value = claims.get(Claims.NOT_BEFORE); + + if (value == null) { + return null; + } else if (value instanceof Instant) { + return (Instant) value; + } else if (value instanceof Number) { + return Instant.ofEpochSecond(((Number) value).longValue()); + } else { + return null; + } + } + + /** + * Builder for creating Paseto Claims builder. + */ + public static class Builder { + + /** + * The claims. + */ + private final Map claims = new LinkedHashMap<>(); + + /** + * Gives the claims map. + * + * @return claims map + */ + public PasetoClaimsSet build() { + return new PasetoClaimsSet(claims); + } + + /** + * Sets the issuer(iss) claim. + * + * @param iss The issuer claim. + * @return This builder. + */ + public Builder issuer(String iss) { + claim(Claims.ISSUER, iss); + return this; + } + + /** + * Sets the subject(sub) claim. + * + * @param sub The subject claim. + * @return This builder. + */ + public Builder subject(String sub) { + claim(Claims.SUBJECT, sub); + return this; + } + + /** + * Sets the audience(aud) claim. + * + * @param aud The audience claim. + * @return This builder. + */ + public Builder audience(String aud) { + claim(Claims.AUDIENCE, aud); + return this; + } + + /** + * Sets the expiery(exp) claim. + * + * @param exp The expiry claim. + * @return This builder. + */ + public Builder expiration(Instant exp) { + claim(Claims.EXPIRATION, exp); + return this; + } + + /** + * Sets not before(nbf) claim. + * + * @param nbf not before claim. + * @return This builder. + */ + public Builder notBefore(Instant nbf) { + claim(Claims.NOT_BEFORE, nbf); + return this; + } + + /** + * Sets the issued at(iat) claim. + * + * @param iat The issued at claim. + * @return This builder. + */ + public Builder issuedAt(Instant iat) { + claim(Claims.ISSUED_AT, iat); + return this; + } + + /** + * Sets the token id(jti) claim. + * + * @param jti The token id claim. + * @return This builder. + */ + public Builder tokenId(String jti) { + claim(Claims.TOKEN_ID, jti); + return this; + } + + //TODO: Add Support for footer in token + + /** + * Set the specified claim. + * + * @param name The name of the claim to set. Must not be {@code null} + * @param value The value of the claim to set, {@code null} jf not specified + * @return this builder. + */ + public Builder claim(final String name, final Object value) { + claims.put(name, value); + return this; + } + + } + +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/claims/PasetoIdGenerator.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/claims/PasetoIdGenerator.java new file mode 100644 index 0000000000..47c2fa1c78 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/generator/claims/PasetoIdGenerator.java @@ -0,0 +1,30 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.generator.claims; + +import io.micronaut.core.annotation.NonNull; + +/** + * @author Utsav Varia + * @since 3.0 + */ +public interface PasetoIdGenerator { + /** + * @return a case-sensitive String which is used as a unique identifier for the Paseto. + */ + @NonNull + String generateJtiClaim(); +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/package-info.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/package-info.java new file mode 100644 index 0000000000..10a0ecbd97 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/package-info.java @@ -0,0 +1,30 @@ +/* + * Copyright 2017-2021 original 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. + */ +/** + * Contains classes specific to Platform-Agnostic Security Tokens (PASETO) Authentication within Micronaut. + * + * @author Utsav Varia + * @since 3.0 + */ + +@Configuration +@Requires(property = PasetoConfigurationProperties.PREFIX + ".enabled", notEquals = StringUtils.FALSE) +package io.micronaut.security.token.paseto; + +import io.micronaut.context.annotation.Configuration; +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.util.StringUtils; +import io.micronaut.security.token.paseto.config.PasetoConfigurationProperties; diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/validator/DefaultPasetoAuthenticationFactory.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/validator/DefaultPasetoAuthenticationFactory.java new file mode 100644 index 0000000000..c4f85cf517 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/validator/DefaultPasetoAuthenticationFactory.java @@ -0,0 +1,54 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.validator; + +import dev.paseto.jpaseto.Claims; +import dev.paseto.jpaseto.Paseto; +import io.micronaut.security.authentication.Authentication; +import io.micronaut.security.token.AbstractTokenAuthenticationFactory; +import io.micronaut.security.token.RolesFinder; +import io.micronaut.security.token.config.TokenConfiguration; +import jakarta.inject.Singleton; + +import java.util.Optional; + +/** + * Default implementation of {@link PasetoAuthenticationFactory}. + * + * @author Utsav Varia + * @since 3.0 + */ +@Singleton +public class DefaultPasetoAuthenticationFactory extends AbstractTokenAuthenticationFactory implements PasetoAuthenticationFactory { + + /** + * + * @param tokenConfiguration Token Configuration + * @param rolesFinder Utility to retrieve roles from token claims + */ + public DefaultPasetoAuthenticationFactory(TokenConfiguration tokenConfiguration, RolesFinder rolesFinder) { + super(tokenConfiguration, rolesFinder); + } + + @Override + public Optional createAuthentication(Paseto token) { + final Claims attributes = token.getClaims(); + if (attributes == null) { + return Optional.empty(); + } + return super.createAuthentication(attributes); + } +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/validator/PasetoAuthenticationFactory.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/validator/PasetoAuthenticationFactory.java new file mode 100644 index 0000000000..978cdd63e7 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/validator/PasetoAuthenticationFactory.java @@ -0,0 +1,28 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.validator; + +import dev.paseto.jpaseto.Paseto; +import io.micronaut.security.token.TokenAuthenticationFactory; + +/** + * Marker interface for implementations of {@link TokenAuthenticationFactory}, which handle {@link Paseto} tokens. + * + * @author Utsav Varia + * @since 3.0 + */ +public interface PasetoAuthenticationFactory extends TokenAuthenticationFactory { +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/validator/PasetoParserBuilderGenerator.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/validator/PasetoParserBuilderGenerator.java new file mode 100644 index 0000000000..93bd87f3ac --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/validator/PasetoParserBuilderGenerator.java @@ -0,0 +1,35 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.validator; + +import dev.paseto.jpaseto.PasetoParserBuilder; +import io.micronaut.core.annotation.NonNull; + +/** + * Returns a {@link PasetoParserBuilder} which will be used to parse/verify a Paseto token. + * @author Sergio del Amo + * @since 3.2.0 + */ +@FunctionalInterface +public interface PasetoParserBuilderGenerator { + + /** + * + * @return a {@link PasetoParserBuilder} which will be used to sign a Paseto token. + */ + @NonNull + PasetoParserBuilder builder(); +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/validator/PasetoParserFactory.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/validator/PasetoParserFactory.java new file mode 100644 index 0000000000..33518be24c --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/validator/PasetoParserFactory.java @@ -0,0 +1,108 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.validator; + +import dev.paseto.jpaseto.PasetoParser; +import dev.paseto.jpaseto.PasetoParserBuilder; +import dev.paseto.jpaseto.Pasetos; +import io.micronaut.context.annotation.EachBean; +import io.micronaut.context.annotation.Factory; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.security.token.paseto.config.PublicKeyConfiguration; +import io.micronaut.security.token.paseto.config.RequiredConfiguration; +import io.micronaut.security.token.paseto.config.SharedSecretConfiguration; +import jakarta.inject.Singleton; + +/** + * {@link Factory} to generate beans of type {@link PasetoParser} for beans of type {@link PublicKeyConfiguration} or {@link SharedSecretConfiguration}. + * @author Sergio del Amo + * @since 3.2.0 + */ +@Factory +public class PasetoParserFactory { + /** + * + * @param configuration Paseto Public Key Configuration + * @return A Paseto Parser + */ + @EachBean(PublicKeyConfiguration.class) + @Singleton + PasetoParser pasetoParserWithPublicKey(PublicKeyConfiguration configuration) { + PasetoParserBuilder builder = Pasetos.parserBuilder() + .setPublicKey(configuration.getPublicKey()); + builder = populateBuilder(builder, configuration); + return builder.build(); + } + + /** + * + * @param configuration Shared Key configuration + * @return A Paseto Parser + */ + @EachBean(SharedSecretConfiguration.class) + @Singleton + PasetoParser pasetoParserWithSharedSecretConfiguration(SharedSecretConfiguration configuration) { + PasetoParserBuilder builder = Pasetos.parserBuilder() + .setSharedSecret(configuration.getSharedSecret()); + builder = populateBuilder(builder, configuration); + return builder.build(); + } + + @NonNull + private PasetoParserBuilder populateBuilder(@NonNull PasetoParserBuilder builder, + @NonNull RequiredConfiguration configuration) { + if (configuration.getRequiredAudience() != null) { + builder = builder.requireAudience(configuration.getRequiredAudience()); + } + if (configuration.getRequiredIssuer() != null) { + builder = builder.requireIssuer(configuration.getRequiredIssuer()); + } + if (configuration.getRequiredKeyId() != null) { + builder = builder.requireKeyId(configuration.getRequiredKeyId()); + } + if (configuration.getRequiredSubject() != null) { + builder = builder.requireSubject(configuration.getRequiredSubject()); + } + if (configuration.getRequiredTokenId() != null) { + builder = builder.requireTokenId(configuration.getRequiredTokenId()); + } + if (configuration.getRequiredExpiration() != null) { + builder = builder.requireExpiration(configuration.getRequiredExpiration()); + } + if (configuration.getRequiredIssuedAt() != null) { + builder = builder.requireIssuedAt(configuration.getRequiredIssuedAt()); + } + if (configuration.getRequiredNotBefore() != null) { + builder = builder.requireNotBefore(configuration.getRequiredNotBefore()); + } + if (configuration.getRequiredClaimsPredicate() != null) { + for (String claimName : configuration.getRequiredClaimsPredicate().keySet()) { + builder = builder.require(claimName, configuration.getRequiredClaimsPredicate().get(claimName)); + } + } + if (configuration.getRequiredClaimsValue() != null) { + for (String claimName : configuration.getRequiredClaimsValue().keySet()) { + builder = builder.require(claimName, configuration.getRequiredClaimsValue().get(claimName)); + } + } + if (configuration.getRequiredFooterPredicate() != null) { + for (String claimName : configuration.getRequiredFooterPredicate().keySet()) { + builder = builder.requireFooter(claimName, configuration.getRequiredFooterPredicate().get(claimName)); + } + } + return builder; + } +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/validator/PasetoTokenValidator.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/validator/PasetoTokenValidator.java new file mode 100644 index 0000000000..0a48b5fc8b --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/validator/PasetoTokenValidator.java @@ -0,0 +1,72 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.validator; + +import dev.paseto.jpaseto.ClaimPasetoException; +import dev.paseto.jpaseto.Paseto; +import dev.paseto.jpaseto.PasetoParser; +import io.micronaut.core.annotation.Nullable; +import io.micronaut.http.HttpRequest; +import io.micronaut.security.authentication.Authentication; +import io.micronaut.security.token.validator.TokenValidator; +import org.reactivestreams.Publisher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; +import java.util.Optional; + +/** + * {@link TokenValidator} for Paseto tokens. + * @author Utsav Varia + * @since 3.0 + */ +public class PasetoTokenValidator implements TokenValidator { + private static final Logger LOG = LoggerFactory.getLogger(PasetoTokenValidator.class); + protected PasetoAuthenticationFactory pasetoAuthenticationFactory; + protected PasetoParser pasetoParser; + + public PasetoTokenValidator(PasetoAuthenticationFactory pasetoAuthenticationFactory, + PasetoParser pasetoParser) { + this.pasetoAuthenticationFactory = pasetoAuthenticationFactory; + this.pasetoParser = pasetoParser; + } + + @Override + public Publisher validateToken(String token, HttpRequest request) { + return parse(token, request) + .flatMap(pasetoAuthenticationFactory::createAuthentication) + .map(Mono::just) + .orElse(Mono.empty()); + } + + /** + * Validates the supplied token with any configurations and claim validators present. + * + * @param token The Paseto string + * @param request HTTP request + * @return An optional Paseto token if validation succeeds + */ + private Optional parse(String token, @Nullable HttpRequest request) { + try { + return Optional.of(pasetoParser.parse(token)); + } catch (ClaimPasetoException e) { + if (LOG.isTraceEnabled()) { + LOG.trace("Failed to parse Paseto token: {}", e.getMessage()); + } + } + return Optional.empty(); + } +} diff --git a/security-paseto/src/main/java/io/micronaut/security/token/paseto/validator/PasetoTokenValidatorFactory.java b/security-paseto/src/main/java/io/micronaut/security/token/paseto/validator/PasetoTokenValidatorFactory.java new file mode 100644 index 0000000000..26e9a70544 --- /dev/null +++ b/security-paseto/src/main/java/io/micronaut/security/token/paseto/validator/PasetoTokenValidatorFactory.java @@ -0,0 +1,46 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token.paseto.validator; + +import dev.paseto.jpaseto.PasetoParser; +import io.micronaut.context.annotation.EachBean; +import io.micronaut.context.annotation.Factory; +import jakarta.inject.Singleton; + +/** + * {@link Factory} to create multiple {@link PasetoTokenValidator} for beans of type {@link PasetoParser}. + * @author Sergio del Amo + * @since 3.2.0 + */ +@Factory +public class PasetoTokenValidatorFactory { + private final PasetoAuthenticationFactory pasetoAuthenticationFactory; + + public PasetoTokenValidatorFactory(PasetoAuthenticationFactory pasetoAuthenticationFactory) { + this.pasetoAuthenticationFactory = pasetoAuthenticationFactory; + } + + /** + * + * @param pasetoParser Paseto Parser + * @return A PastetoTokenValidator + */ + @EachBean(PasetoParser.class) + @Singleton + PasetoTokenValidator createPasetoTokenValidator(PasetoParser pasetoParser) { + return new PasetoTokenValidator(pasetoAuthenticationFactory, pasetoParser); + } +} diff --git a/security-paseto/src/test/groovy/io/micronaut/security/token/paseto/PasetoLoginSpec.groovy b/security-paseto/src/test/groovy/io/micronaut/security/token/paseto/PasetoLoginSpec.groovy new file mode 100644 index 0000000000..9a2b2a5fa2 --- /dev/null +++ b/security-paseto/src/test/groovy/io/micronaut/security/token/paseto/PasetoLoginSpec.groovy @@ -0,0 +1,91 @@ +package io.micronaut.security.token.paseto + +import dev.paseto.jpaseto.Version +import dev.paseto.jpaseto.lang.Keys +import io.micronaut.context.annotation.Requires +import io.micronaut.http.HttpRequest +import io.micronaut.http.MediaType +import io.micronaut.http.annotation.Controller +import io.micronaut.http.annotation.Get +import io.micronaut.http.annotation.Produces +import io.micronaut.security.testutils.EmbeddedServerSpecification +import io.micronaut.security.testutils.authprovider.MockAuthenticationProvider +import io.micronaut.security.testutils.authprovider.SuccessAuthenticationScenario +import io.micronaut.security.token.generator.TokenGenerator +import io.micronaut.security.token.paseto.config.RequiredConfiguration +import io.micronaut.security.token.paseto.config.VersionedSharedSecretConfiguration +import io.micronaut.security.token.paseto.generator.PasetoTokenGenerator +import io.micronaut.security.token.paseto.validator.PasetoTokenValidator +import io.micronaut.security.token.render.BearerAccessRefreshToken +import io.micronaut.security.token.validator.TokenValidator +import jakarta.inject.Singleton + +import jakarta.annotation.security.RolesAllowed +import java.security.Principal + +class PasetoLoginSpec extends EmbeddedServerSpecification { + @Override + String getSpecName() { + 'PasetoLoginSpec' + } + + @Override + Map getConfiguration() { + super.configuration + [ + 'micronaut.security.authentication': 'bearer', + 'micronaut.security.token.paseto.local-generator.version': 'v1', + 'micronaut.security.token.paseto.local-generator.base64-shared-secret': generateSharedSecret() + ] + } + + void "you can login and obtain a paseto token"() { + expect: 'a token generator of type PasetoTokenGenerator exists' + containsBean(RequiredConfiguration) + getBean(VersionedSharedSecretConfiguration).version == Version.V1 + containsBean(TokenGenerator) + containsBean(TokenValidator) + getBean(TokenGenerator) instanceof PasetoTokenGenerator + getBean(TokenValidator) instanceof PasetoTokenValidator + + when: + HttpRequest request = HttpRequest.POST('/login', [username: 'sherlock', password: 'elementary']) + BearerAccessRefreshToken bearerAccessRefreshToken = client.retrieve(request, BearerAccessRefreshToken) + + then: + noExceptionThrown() + 'sherlock' == bearerAccessRefreshToken.username + + when: + String username = client.retrieve(HttpRequest.GET('/username') + .bearerAuth(bearerAccessRefreshToken.accessToken) + .accept(MediaType.TEXT_PLAIN)) + + then: + noExceptionThrown() + 'sherlock' == username + } + + private static String generateSharedSecret() { + Base64.getEncoder().encodeToString(Keys.secretKey().getEncoded()) + } + + @Requires(property = 'spec.name', value = 'PasetoLoginSpec') + @Controller + static class EchoUserNameController { + + @RolesAllowed("ROLE_DETECTIVE") + @Get("/username") + @Produces(MediaType.TEXT_PLAIN) + String index(Principal principal) { + principal.name + } + } + + @Singleton + @Requires(property = 'spec.name', value = 'PasetoLoginSpec') + static class AuthenticationProviderUserPassword extends MockAuthenticationProvider { + AuthenticationProviderUserPassword() { + super([new SuccessAuthenticationScenario('sherlock', ['ROLE_DETECTIVE'])]) + } + } +} diff --git a/security-paseto/src/test/groovy/io/micronaut/security/token/paseto/config/LocalValidatorsSpec.groovy b/security-paseto/src/test/groovy/io/micronaut/security/token/paseto/config/LocalValidatorsSpec.groovy new file mode 100644 index 0000000000..8b1c3c310b --- /dev/null +++ b/security-paseto/src/test/groovy/io/micronaut/security/token/paseto/config/LocalValidatorsSpec.groovy @@ -0,0 +1,26 @@ +package io.micronaut.security.token.paseto.config + +import dev.paseto.jpaseto.lang.Keys +import io.micronaut.security.testutils.ApplicationContextSpecification +import java.nio.charset.StandardCharsets + +class LocalValidatorsSpec extends ApplicationContextSpecification { + @Override + Map getConfiguration() { + super.configuration + [ + 'micronaut.security.token.paseto.local-validators.one.shared-secret': generateSharedSecret(), + 'micronaut.security.token.paseto.local-validators.two.shared-secret': generateSharedSecret(), + ] + } + + void "you can generate multiple local token validators"() { + expect: + !containsBean(VersionedSharedSecretConfiguration) + containsBean(SharedSecretConfiguration) + getBeansOfType(SharedSecretConfiguration).size() == 2 + } + + private static String generateSharedSecret() { + new String(Keys.secretKey().getEncoded(), StandardCharsets.UTF_8) + } +} diff --git a/security-paseto/src/test/groovy/io/micronaut/security/token/paseto/config/VersionedSharedSecretConfigurationPropertiesSpec.groovy b/security-paseto/src/test/groovy/io/micronaut/security/token/paseto/config/VersionedSharedSecretConfigurationPropertiesSpec.groovy new file mode 100644 index 0000000000..6e77a20859 --- /dev/null +++ b/security-paseto/src/test/groovy/io/micronaut/security/token/paseto/config/VersionedSharedSecretConfigurationPropertiesSpec.groovy @@ -0,0 +1,27 @@ +package io.micronaut.security.token.paseto.config + +import dev.paseto.jpaseto.Version +import dev.paseto.jpaseto.lang.Keys +import io.micronaut.security.testutils.ApplicationContextSpecification + +class VersionedSharedSecretConfigurationPropertiesSpec extends ApplicationContextSpecification { + + @Override + Map getConfiguration() { + super.configuration + [ + 'micronaut.security.token.paseto.local-generator.base64-shared-secret': generateSharedSecret() + ] + } + + void "micronaut.security.token.paseto.shared-key-generator defaults to version v1"() { + expect: + containsBean(VersionedSharedSecretConfiguration) + containsBean(SharedSecretConfiguration) + containsBean(RequiredConfiguration) + getBean(VersionedSharedSecretConfiguration).version == Version.V1 + } + + private static String generateSharedSecret() { + Base64.getEncoder().encodeToString(Keys.secretKey().getEncoded()) + } +} diff --git a/security-paseto/src/test/groovy/io/micronaut/security/token/paseto/generator/claims/PasetoClaimsGeneratorSpec.groovy b/security-paseto/src/test/groovy/io/micronaut/security/token/paseto/generator/claims/PasetoClaimsGeneratorSpec.groovy new file mode 100644 index 0000000000..afe08d1c85 --- /dev/null +++ b/security-paseto/src/test/groovy/io/micronaut/security/token/paseto/generator/claims/PasetoClaimsGeneratorSpec.groovy @@ -0,0 +1,28 @@ +package io.micronaut.security.token.paseto.generator.claims + +import dev.paseto.jpaseto.Claims +import io.micronaut.security.authentication.Authentication +import io.micronaut.security.token.config.TokenConfiguration +import spock.lang.Specification + +class PasetoClaimsGeneratorSpec extends Specification { + def "generateClaims includes sub and exp claims"() { + given: + PasetoClaimsGenerator generator = new PasetoClaimsGenerator(new TokenConfiguration() {}, null, null, null) + + when: + Map claims = generator.generateClaims(Authentication.build('admin', ['ROLE_USER', 'ROLE_ADMIN']), 3600) + List expectedClaimsNames = [Claims.SUBJECT, + Claims.ISSUED_AT, + Claims.EXPIRATION, + Claims.NOT_BEFORE, + Claims.ISSUER, + "roles"] + then: + claims + claims.keySet().size() == expectedClaimsNames.size() + expectedClaimsNames.each { String claimName -> + assert claims.get(claimName) + } + } +} diff --git a/security-paseto/src/test/resources/logback.xml b/security-paseto/src/test/resources/logback.xml new file mode 100644 index 0000000000..432f5aa24e --- /dev/null +++ b/security-paseto/src/test/resources/logback.xml @@ -0,0 +1,10 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + \ No newline at end of file diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/endpoints/OauthController.java b/security/src/main/java/io/micronaut/security/endpoints/OauthController.java similarity index 98% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/endpoints/OauthController.java rename to security/src/main/java/io/micronaut/security/endpoints/OauthController.java index df1ac311a1..c10981e3df 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/endpoints/OauthController.java +++ b/security/src/main/java/io/micronaut/security/endpoints/OauthController.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.endpoints; +package io.micronaut.security.endpoints; import io.micronaut.context.annotation.Requires; import io.micronaut.core.annotation.NonNull; @@ -45,7 +45,8 @@ import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; -import static io.micronaut.security.token.jwt.endpoints.TokenRefreshRequest.GRANT_TYPE; +import static io.micronaut.security.endpoints.TokenRefreshRequest.GRANT_TYPE; + /** * diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/endpoints/OauthControllerConfiguration.java b/security/src/main/java/io/micronaut/security/endpoints/OauthControllerConfiguration.java similarity index 95% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/endpoints/OauthControllerConfiguration.java rename to security/src/main/java/io/micronaut/security/endpoints/OauthControllerConfiguration.java index c295104217..e1f445c765 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/endpoints/OauthControllerConfiguration.java +++ b/security/src/main/java/io/micronaut/security/endpoints/OauthControllerConfiguration.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.endpoints; +package io.micronaut.security.endpoints; import io.micronaut.security.endpoints.ControllerConfiguration; diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/endpoints/OauthControllerConfigurationProperties.java b/security/src/main/java/io/micronaut/security/endpoints/OauthControllerConfigurationProperties.java similarity index 90% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/endpoints/OauthControllerConfigurationProperties.java rename to security/src/main/java/io/micronaut/security/endpoints/OauthControllerConfigurationProperties.java index 9d96ec137d..ad3b540d83 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/endpoints/OauthControllerConfigurationProperties.java +++ b/security/src/main/java/io/micronaut/security/endpoints/OauthControllerConfigurationProperties.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.endpoints; +package io.micronaut.security.endpoints; import io.micronaut.context.annotation.ConfigurationProperties; import io.micronaut.context.annotation.Requires; @@ -70,7 +70,7 @@ public String getPath() { } /** - * Sets whether the {@link io.micronaut.security.token.jwt.endpoints.OauthController} is enabled. Default value ({@value #DEFAULT_ENABLED}). + * Sets whether the {@link OauthController} is enabled. Default value ({@value #DEFAULT_ENABLED}). * * @param enabled True if is enabled */ @@ -79,7 +79,7 @@ public void setEnabled(boolean enabled) { } /** - * Sets the path to map the {@link io.micronaut.security.token.jwt.endpoints.OauthController} to. Default value ({@value #DEFAULT_PATH}). + * Sets the path to map the {@link OauthController} to. Default value ({@value #DEFAULT_PATH}). * * @param path The path */ diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/endpoints/TokenRefreshRequest.java b/security/src/main/java/io/micronaut/security/endpoints/TokenRefreshRequest.java similarity index 97% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/endpoints/TokenRefreshRequest.java rename to security/src/main/java/io/micronaut/security/endpoints/TokenRefreshRequest.java index f2d2577682..0945818f41 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/endpoints/TokenRefreshRequest.java +++ b/security/src/main/java/io/micronaut/security/endpoints/TokenRefreshRequest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.endpoints; +package io.micronaut.security.endpoints; import com.fasterxml.jackson.annotation.JsonProperty; import io.micronaut.core.annotation.NonNull; diff --git a/security/src/main/java/io/micronaut/security/token/AbstractTokenAuthenticationFactory.java b/security/src/main/java/io/micronaut/security/token/AbstractTokenAuthenticationFactory.java new file mode 100644 index 0000000000..5f702963cd --- /dev/null +++ b/security/src/main/java/io/micronaut/security/token/AbstractTokenAuthenticationFactory.java @@ -0,0 +1,79 @@ +/* + * Copyright 2017-2021 original 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 io.micronaut.security.token; + +import io.micronaut.core.annotation.NonNull; +import io.micronaut.security.authentication.Authentication; +import io.micronaut.security.token.config.TokenConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.Optional; + +/** + * Abstract implementation of {@link TokenAuthenticationFactory} which creates an authentication for a set of claims. + * @author Sergio del Amo + * @since 3.2.0 + * @param The Token type. e.g JWT or Paseto + */ +public abstract class AbstractTokenAuthenticationFactory implements TokenAuthenticationFactory { + private static final Logger LOG = LoggerFactory.getLogger(AbstractTokenAuthenticationFactory.class); + + private final TokenConfiguration tokenConfiguration; + private final RolesFinder rolesFinder; + + /** + * + * @param tokenConfiguration Token Configuration + * @param rolesFinder Utility to retrieve roles from token claims + */ + public AbstractTokenAuthenticationFactory(TokenConfiguration tokenConfiguration, + RolesFinder rolesFinder) { + this.tokenConfiguration = tokenConfiguration; + this.rolesFinder = rolesFinder; + } + + /** + * + * @param claims Claims + * @return the username defined by {@link TokenConfiguration#getNameKey()} ()} or the sub claim. + */ + @NonNull + protected Optional usernameForClaims(@NonNull Claims claims) { + Object username = claims.get(tokenConfiguration.getNameKey()); + if (username != null) { + return Optional.of(username.toString()); + } + Object sub = claims.get(TokenConfiguration.DEFAULT_NAME_KEY); + if (sub == null) { + return Optional.empty(); + } + return Optional.ofNullable(sub.toString()); + } + + /** + * + * @param attributes Claims + * @return {@link Authentication} object based on the user claims + */ + protected Optional createAuthentication(@NonNull Map attributes) { + return usernameForClaims(new MapClaims(attributes)).map(username -> + Authentication.build(username, + rolesFinder.resolveRoles(attributes), + attributes)); + } +} diff --git a/security/src/main/java/io/micronaut/security/token/Claims.java b/security/src/main/java/io/micronaut/security/token/Claims.java index 61a29a1a26..ae91ed18aa 100644 --- a/security/src/main/java/io/micronaut/security/token/Claims.java +++ b/security/src/main/java/io/micronaut/security/token/Claims.java @@ -17,6 +17,9 @@ import io.micronaut.core.annotation.NonNull; import io.micronaut.core.annotation.Nullable; + +import java.util.Arrays; +import java.util.List; import java.util.Set; /** @@ -26,6 +29,23 @@ * @author Sergio del Amo */ public interface Claims { + String ISSUER = "iss"; + + String SUBJECT = "sub"; + + String EXPIRATION_TIME = "exp"; + + String NOT_BEFORE = "nbf"; + + String ISSUED_AT = "iat"; + + String TOKEN_ID = "jti"; + + String KEY_ID = "kid"; + + String AUDIENCE = "aud"; + + List ALL_CLAIMS = Arrays.asList(ISSUER, SUBJECT, EXPIRATION_TIME, NOT_BEFORE, ISSUED_AT, TOKEN_ID, AUDIENCE); /** * Retrieves a value from the claims for the given name. diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/bearer/AccessRefreshTokenLoginHandler.java b/security/src/main/java/io/micronaut/security/token/bearer/AccessRefreshTokenLoginHandler.java similarity index 93% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/bearer/AccessRefreshTokenLoginHandler.java rename to security/src/main/java/io/micronaut/security/token/bearer/AccessRefreshTokenLoginHandler.java index 5664deb83d..93efe25c6a 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/bearer/AccessRefreshTokenLoginHandler.java +++ b/security/src/main/java/io/micronaut/security/token/bearer/AccessRefreshTokenLoginHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.bearer; +package io.micronaut.security.token.bearer; import io.micronaut.context.annotation.Requires; import io.micronaut.http.HttpRequest; @@ -24,9 +24,10 @@ import io.micronaut.security.authentication.AuthenticationResponse; import io.micronaut.security.config.SecurityConfigurationProperties; import io.micronaut.security.handlers.LoginHandler; -import io.micronaut.security.token.jwt.generator.AccessRefreshTokenGenerator; -import io.micronaut.security.token.jwt.render.AccessRefreshToken; +import io.micronaut.security.token.generator.AccessRefreshTokenGenerator; +import io.micronaut.security.token.render.AccessRefreshToken; import jakarta.inject.Singleton; + import java.util.Optional; /** diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/bearer/BearerTokenConfiguration.java b/security/src/main/java/io/micronaut/security/token/bearer/BearerTokenConfiguration.java similarity index 96% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/bearer/BearerTokenConfiguration.java rename to security/src/main/java/io/micronaut/security/token/bearer/BearerTokenConfiguration.java index 2a1b28337b..bec08b1819 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/bearer/BearerTokenConfiguration.java +++ b/security/src/main/java/io/micronaut/security/token/bearer/BearerTokenConfiguration.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.bearer; +package io.micronaut.security.token.bearer; import io.micronaut.core.util.Toggleable; diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/bearer/BearerTokenConfigurationProperties.java b/security/src/main/java/io/micronaut/security/token/bearer/BearerTokenConfigurationProperties.java similarity index 92% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/bearer/BearerTokenConfigurationProperties.java rename to security/src/main/java/io/micronaut/security/token/bearer/BearerTokenConfigurationProperties.java index 6c5b72bddc..a3bc9e4dfb 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/bearer/BearerTokenConfigurationProperties.java +++ b/security/src/main/java/io/micronaut/security/token/bearer/BearerTokenConfigurationProperties.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.bearer; +package io.micronaut.security.token.bearer; import io.micronaut.context.annotation.ConfigurationProperties; import io.micronaut.context.annotation.Requires; import io.micronaut.core.util.StringUtils; import io.micronaut.http.HttpHeaderValues; import io.micronaut.http.HttpHeaders; -import io.micronaut.security.token.jwt.config.JwtConfigurationProperties; +import io.micronaut.security.token.config.TokenConfigurationProperties; /** * Default implementation of {@link BearerTokenConfiguration}. @@ -32,7 +32,7 @@ @ConfigurationProperties(BearerTokenConfigurationProperties.PREFIX) public class BearerTokenConfigurationProperties implements BearerTokenConfiguration { - public static final String PREFIX = JwtConfigurationProperties.PREFIX + ".bearer"; + public static final String PREFIX = TokenConfigurationProperties.PREFIX + ".bearer"; public static final boolean DEFAULT_ENABLED = true; diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/bearer/BearerTokenReader.java b/security/src/main/java/io/micronaut/security/token/bearer/BearerTokenReader.java similarity index 90% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/bearer/BearerTokenReader.java rename to security/src/main/java/io/micronaut/security/token/bearer/BearerTokenReader.java index 6c4ca98ab0..65ac787cba 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/bearer/BearerTokenReader.java +++ b/security/src/main/java/io/micronaut/security/token/bearer/BearerTokenReader.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.bearer; +package io.micronaut.security.token.bearer; import io.micronaut.context.annotation.Requires; import io.micronaut.core.util.StringUtils; -import io.micronaut.security.token.jwt.cookie.JwtCookieTokenReader; +import io.micronaut.security.token.cookie.CookieTokenReader; import io.micronaut.security.token.reader.HttpHeaderTokenReader; import io.micronaut.security.token.reader.TokenReader; import jakarta.inject.Singleton; @@ -36,7 +36,7 @@ public class BearerTokenReader extends HttpHeaderTokenReader implements TokenRea * * The order of the TokenReader. */ - public static final Integer ORDER = JwtCookieTokenReader.ORDER - 100; + public static final Integer ORDER = CookieTokenReader.ORDER - 100; protected final BearerTokenConfiguration bearerTokenConfiguration; diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/bearer/package-info.java b/security/src/main/java/io/micronaut/security/token/bearer/package-info.java similarity index 93% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/bearer/package-info.java rename to security/src/main/java/io/micronaut/security/token/bearer/package-info.java index 545ffb48b0..ec37ae13c8 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/bearer/package-info.java +++ b/security/src/main/java/io/micronaut/security/token/bearer/package-info.java @@ -20,4 +20,4 @@ * @since 1.0 */ -package io.micronaut.security.token.jwt.bearer; +package io.micronaut.security.token.bearer; diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/ClaimsAudienceProvider.java b/security/src/main/java/io/micronaut/security/token/claims/ClaimsAudienceProvider.java similarity index 94% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/ClaimsAudienceProvider.java rename to security/src/main/java/io/micronaut/security/token/claims/ClaimsAudienceProvider.java index 3c6d8b267b..f1ace38bcc 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/ClaimsAudienceProvider.java +++ b/security/src/main/java/io/micronaut/security/token/claims/ClaimsAudienceProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.generator.claims; +package io.micronaut.security.token.claims; import java.util.List; diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/ClaimsGenerator.java b/security/src/main/java/io/micronaut/security/token/claims/ClaimsGenerator.java similarity index 94% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/ClaimsGenerator.java rename to security/src/main/java/io/micronaut/security/token/claims/ClaimsGenerator.java index 6690bbdab9..03296de1bd 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/ClaimsGenerator.java +++ b/security/src/main/java/io/micronaut/security/token/claims/ClaimsGenerator.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.generator.claims; +package io.micronaut.security.token.claims; import io.micronaut.security.authentication.Authentication; import java.util.Map; @@ -21,7 +21,7 @@ /** * * @author Sergio del Amo - * @since 1.0 + * @since 3.2.0 */ public interface ClaimsGenerator { diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/JwtIdGenerator.java b/security/src/main/java/io/micronaut/security/token/claims/JtiGenerator.java similarity index 79% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/JwtIdGenerator.java rename to security/src/main/java/io/micronaut/security/token/claims/JtiGenerator.java index 57c0312409..4a1c22b9a6 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/claims/JwtIdGenerator.java +++ b/security/src/main/java/io/micronaut/security/token/claims/JtiGenerator.java @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.generator.claims; +package io.micronaut.security.token.claims; /** - * Generates the "jti" (JWT ID) claim, which provides a unique identifier for the JWT. - * @see 4.1.7. "jti" (JWT ID) Claim + * Generates the "jti" (Token ID) claim, which provides a unique identifier for the token. + * @see 4.1.7. "jti" (JWT ID) Claim for JWT tokens. * @author Sergio del Amo * @version 1.0 */ -public interface JwtIdGenerator { +public interface JtiGenerator { /** * diff --git a/security/src/main/java/io/micronaut/security/token/claims/package-info.java b/security/src/main/java/io/micronaut/security/token/claims/package-info.java new file mode 100644 index 0000000000..cd6c318fa7 --- /dev/null +++ b/security/src/main/java/io/micronaut/security/token/claims/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright 2017-2020 original 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. + */ +/** + * Claims related classes. + * + * @author Sergio del Amo + * @since 3.2.0 + */ +package io.micronaut.security.token.claims; + diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/AbstractAccessTokenCookieConfigurationProperties.java b/security/src/main/java/io/micronaut/security/token/cookie/AbstractAccessTokenCookieConfigurationProperties.java similarity index 100% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/AbstractAccessTokenCookieConfigurationProperties.java rename to security/src/main/java/io/micronaut/security/token/cookie/AbstractAccessTokenCookieConfigurationProperties.java diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/AccessTokenCookieConfiguration.java b/security/src/main/java/io/micronaut/security/token/cookie/AccessTokenCookieConfiguration.java similarity index 92% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/AccessTokenCookieConfiguration.java rename to security/src/main/java/io/micronaut/security/token/cookie/AccessTokenCookieConfiguration.java index e746094e1f..5216796cf6 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/AccessTokenCookieConfiguration.java +++ b/security/src/main/java/io/micronaut/security/token/cookie/AccessTokenCookieConfiguration.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.cookie; +package io.micronaut.security.token.cookie; import io.micronaut.security.config.TokenCookieConfiguration; @@ -21,7 +21,7 @@ * Configuration for the access token cookie. * * @author James Kleeh - * @since 2.1.0 + * @since 3.2.0 */ public interface AccessTokenCookieConfiguration extends TokenCookieConfiguration { } diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/CookieLoginHandler.java b/security/src/main/java/io/micronaut/security/token/cookie/CookieLoginHandler.java similarity index 99% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/CookieLoginHandler.java rename to security/src/main/java/io/micronaut/security/token/cookie/CookieLoginHandler.java index 4917e53b7f..7043ae43b7 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/CookieLoginHandler.java +++ b/security/src/main/java/io/micronaut/security/token/cookie/CookieLoginHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.cookie; +package io.micronaut.security.token.cookie; import io.micronaut.core.annotation.Nullable; import io.micronaut.core.util.functional.ThrowingSupplier; @@ -39,7 +39,7 @@ * For a successful login a cookie is added to the response with a token. * * @author Sergio del Amo - * @since 2.0.0 + * @since 3.2.0 */ public abstract class CookieLoginHandler implements RedirectingLoginHandler { diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/JwtCookieTokenReader.java b/security/src/main/java/io/micronaut/security/token/cookie/CookieTokenReader.java similarity index 84% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/JwtCookieTokenReader.java rename to security/src/main/java/io/micronaut/security/token/cookie/CookieTokenReader.java index 4a1cf899de..d7f074b7be 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/JwtCookieTokenReader.java +++ b/security/src/main/java/io/micronaut/security/token/cookie/CookieTokenReader.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.cookie; +package io.micronaut.security.token.cookie; import io.micronaut.context.annotation.Requires; import io.micronaut.core.util.StringUtils; @@ -22,6 +22,7 @@ import io.micronaut.security.authentication.CookieBasedAuthenticationModeCondition; import io.micronaut.security.token.reader.TokenReader; import jakarta.inject.Singleton; + import java.util.Optional; /** @@ -31,9 +32,9 @@ * @since 1.0 */ @Requires(condition = CookieBasedAuthenticationModeCondition.class) -@Requires(property = JwtCookieConfigurationProperties.PREFIX + ".enabled", notEquals = StringUtils.FALSE, defaultValue = StringUtils.TRUE) +@Requires(property = TokenCookieConfigurationProperties.PREFIX + ".enabled", notEquals = StringUtils.FALSE, defaultValue = StringUtils.TRUE) @Singleton -public class JwtCookieTokenReader implements TokenReader { +public class CookieTokenReader implements TokenReader { /* * @@ -47,7 +48,7 @@ public class JwtCookieTokenReader implements TokenReader { * * @param accessTokenCookieConfiguration Configuration properties for JWT Cookie support */ - public JwtCookieTokenReader(AccessTokenCookieConfiguration accessTokenCookieConfiguration) { + public CookieTokenReader(AccessTokenCookieConfiguration accessTokenCookieConfiguration) { this.accessTokenCookieConfiguration = accessTokenCookieConfiguration; } diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/RefreshTokenCookieConfiguration.java b/security/src/main/java/io/micronaut/security/token/cookie/RefreshTokenCookieConfiguration.java similarity index 94% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/RefreshTokenCookieConfiguration.java rename to security/src/main/java/io/micronaut/security/token/cookie/RefreshTokenCookieConfiguration.java index 18ed7c2755..dff45c0de6 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/RefreshTokenCookieConfiguration.java +++ b/security/src/main/java/io/micronaut/security/token/cookie/RefreshTokenCookieConfiguration.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.cookie; +package io.micronaut.security.token.cookie; import io.micronaut.security.config.TokenCookieConfiguration; diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/RefreshTokenCookieConfigurationProperties.java b/security/src/main/java/io/micronaut/security/token/cookie/RefreshTokenCookieConfigurationProperties.java similarity index 94% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/RefreshTokenCookieConfigurationProperties.java rename to security/src/main/java/io/micronaut/security/token/cookie/RefreshTokenCookieConfigurationProperties.java index d22546cbbc..77b8bce2af 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/RefreshTokenCookieConfigurationProperties.java +++ b/security/src/main/java/io/micronaut/security/token/cookie/RefreshTokenCookieConfigurationProperties.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.cookie; +package io.micronaut.security.token.cookie; import io.micronaut.context.annotation.ConfigurationProperties; import io.micronaut.context.annotation.Property; @@ -24,7 +24,8 @@ import io.micronaut.core.util.StringUtils; import io.micronaut.security.authentication.CookieBasedAuthenticationModeCondition; import io.micronaut.security.token.config.TokenConfigurationProperties; -import io.micronaut.security.token.jwt.endpoints.OauthControllerConfigurationProperties; +import io.micronaut.security.endpoints.OauthControllerConfigurationProperties; +import io.micronaut.security.token.jwt.cookie.AbstractAccessTokenCookieConfigurationProperties; import java.util.Optional; diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/JwtCookieClearerLogoutHandler.java b/security/src/main/java/io/micronaut/security/token/cookie/TokenCookieClearerLogoutHandler.java similarity index 93% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/JwtCookieClearerLogoutHandler.java rename to security/src/main/java/io/micronaut/security/token/cookie/TokenCookieClearerLogoutHandler.java index 43b45afc25..3493f7040f 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/JwtCookieClearerLogoutHandler.java +++ b/security/src/main/java/io/micronaut/security/token/cookie/TokenCookieClearerLogoutHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.cookie; +package io.micronaut.security.token.cookie; import io.micronaut.context.annotation.Requires; import io.micronaut.core.annotation.Nullable; @@ -37,7 +37,7 @@ */ @Requires(condition = CookieBasedAuthenticationModeCondition.class) @Singleton -public class JwtCookieClearerLogoutHandler implements LogoutHandler { +public class TokenCookieClearerLogoutHandler implements LogoutHandler { @Nullable protected final String logout; @@ -50,7 +50,7 @@ public class JwtCookieClearerLogoutHandler implements LogoutHandler { * @param redirectConfiguration Redirect configuration * @param redirectService Redirection Service */ - public JwtCookieClearerLogoutHandler(AccessTokenCookieConfiguration accessTokenCookieConfiguration, + public TokenCookieClearerLogoutHandler(AccessTokenCookieConfiguration accessTokenCookieConfiguration, RefreshTokenCookieConfiguration refreshTokenCookieConfiguration, RedirectConfiguration redirectConfiguration, RedirectService redirectService) { diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/JwtCookieConfigurationProperties.java b/security/src/main/java/io/micronaut/security/token/cookie/TokenCookieConfigurationProperties.java similarity index 81% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/JwtCookieConfigurationProperties.java rename to security/src/main/java/io/micronaut/security/token/cookie/TokenCookieConfigurationProperties.java index bbc392c046..bc4b226f66 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/JwtCookieConfigurationProperties.java +++ b/security/src/main/java/io/micronaut/security/token/cookie/TokenCookieConfigurationProperties.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.cookie; +package io.micronaut.security.token.cookie; import io.micronaut.context.annotation.ConfigurationProperties; import io.micronaut.context.annotation.Requires; @@ -21,7 +21,8 @@ import io.micronaut.core.annotation.Nullable; import io.micronaut.core.util.StringUtils; import io.micronaut.security.authentication.CookieBasedAuthenticationModeCondition; -import io.micronaut.security.token.jwt.config.JwtConfigurationProperties; +import io.micronaut.security.token.config.TokenConfigurationProperties; +import io.micronaut.security.token.jwt.cookie.AbstractAccessTokenCookieConfigurationProperties; import java.util.Optional; @@ -31,11 +32,11 @@ * @since 1.0 */ @Requires(condition = CookieBasedAuthenticationModeCondition.class) -@Requires(property = JwtCookieConfigurationProperties.PREFIX + ".enabled", notEquals = StringUtils.FALSE, defaultValue = StringUtils.TRUE) -@ConfigurationProperties(JwtCookieConfigurationProperties.PREFIX) -public class JwtCookieConfigurationProperties extends AbstractAccessTokenCookieConfigurationProperties implements AccessTokenCookieConfiguration { +@Requires(property = TokenCookieConfigurationProperties.PREFIX + ".enabled", notEquals = StringUtils.FALSE, defaultValue = StringUtils.TRUE) +@ConfigurationProperties(TokenCookieConfigurationProperties.PREFIX) +public class TokenCookieConfigurationProperties extends AbstractAccessTokenCookieConfigurationProperties implements AccessTokenCookieConfiguration { - public static final String PREFIX = JwtConfigurationProperties.PREFIX + ".cookie"; + public static final String PREFIX = TokenConfigurationProperties.PREFIX + ".cookie"; /** * The default enable value. diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/JwtCookieLoginHandler.java b/security/src/main/java/io/micronaut/security/token/cookie/TokenCookieLoginHandler.java similarity index 93% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/JwtCookieLoginHandler.java rename to security/src/main/java/io/micronaut/security/token/cookie/TokenCookieLoginHandler.java index debcc3de29..652a61fed3 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/JwtCookieLoginHandler.java +++ b/security/src/main/java/io/micronaut/security/token/cookie/TokenCookieLoginHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.cookie; +package io.micronaut.security.token.cookie; import io.micronaut.context.annotation.Requires; import io.micronaut.core.annotation.Nullable; @@ -27,9 +27,9 @@ import io.micronaut.security.errors.OauthErrorResponseException; import io.micronaut.security.errors.ObtainingAuthorizationErrorCode; import io.micronaut.security.errors.PriorToLoginPersistence; -import io.micronaut.security.token.jwt.generator.AccessRefreshTokenGenerator; -import io.micronaut.security.token.jwt.generator.AccessTokenConfiguration; -import io.micronaut.security.token.jwt.render.AccessRefreshToken; +import io.micronaut.security.token.generator.AccessRefreshTokenGenerator; +import io.micronaut.security.token.generator.AccessTokenConfiguration; +import io.micronaut.security.token.render.AccessRefreshToken; import jakarta.inject.Singleton; import java.time.Duration; import java.time.temporal.TemporalAmount; @@ -43,7 +43,7 @@ */ @Requires(property = SecurityConfigurationProperties.PREFIX + ".authentication", value = "cookie") @Singleton -public class JwtCookieLoginHandler extends CookieLoginHandler { +public class TokenCookieLoginHandler extends CookieLoginHandler { protected final AccessRefreshTokenGenerator accessRefreshTokenGenerator; protected final RefreshTokenCookieConfiguration refreshTokenCookieConfiguration; @@ -58,7 +58,7 @@ public class JwtCookieLoginHandler extends CookieLoginHandler { * @param accessRefreshTokenGenerator Access Refresh Token Generator * @param priorToLoginPersistence Prior To Login Persistence Mechanism */ - public JwtCookieLoginHandler(RedirectService redirectService, + public TokenCookieLoginHandler(RedirectService redirectService, RedirectConfiguration redirectConfiguration, AccessTokenCookieConfiguration accessTokenCookieConfiguration, RefreshTokenCookieConfiguration refreshTokenCookieConfiguration, diff --git a/security/src/main/java/io/micronaut/security/token/cookie/TokenCookieTokenReader.java b/security/src/main/java/io/micronaut/security/token/cookie/TokenCookieTokenReader.java new file mode 100644 index 0000000000..d59aac34fc --- /dev/null +++ b/security/src/main/java/io/micronaut/security/token/cookie/TokenCookieTokenReader.java @@ -0,0 +1,64 @@ +/* + * Copyright 2017-2020 original 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 io.micronaut.security.token.cookie; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.util.StringUtils; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.cookie.Cookie; +import io.micronaut.security.authentication.CookieBasedAuthenticationModeCondition; +import io.micronaut.security.token.reader.TokenReader; +import jakarta.inject.Singleton; +import java.util.Optional; + +/** + * Reads the token from the configured io.micronaut.security.token.jwt.cookie. + * + * @author Sergio del Amo + * @since 1.0 + */ +@Requires(condition = CookieBasedAuthenticationModeCondition.class) +@Requires(property = TokenCookieConfigurationProperties.PREFIX + ".enabled", notEquals = StringUtils.FALSE, defaultValue = StringUtils.TRUE) +@Singleton +public class TokenCookieTokenReader implements TokenReader { + + /* + * + * The order of the TokenReader. + */ + public static final Integer ORDER = 0; + + protected final AccessTokenCookieConfiguration accessTokenCookieConfiguration; + + /** + * + * @param accessTokenCookieConfiguration Configuration properties for JWT Cookie support + */ + public TokenCookieTokenReader(AccessTokenCookieConfiguration accessTokenCookieConfiguration) { + this.accessTokenCookieConfiguration = accessTokenCookieConfiguration; + } + + @Override + public Optional findToken(HttpRequest request) { + Optional optionalCookie = request.getCookies().findCookie(accessTokenCookieConfiguration.getCookieName()); + return optionalCookie.map(Cookie::getValue); + } + + @Override + public int getOrder() { + return ORDER; + } +} diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/package-info.java b/security/src/main/java/io/micronaut/security/token/cookie/package-info.java similarity index 93% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/package-info.java rename to security/src/main/java/io/micronaut/security/token/cookie/package-info.java index b7adeeb49d..d22336728b 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/cookie/package-info.java +++ b/security/src/main/java/io/micronaut/security/token/cookie/package-info.java @@ -19,4 +19,4 @@ * @author Sergio del Amo * @since 1.0 */ -package io.micronaut.security.token.jwt.cookie; +package io.micronaut.security.token.cookie; diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/AccessRefreshTokenGenerator.java b/security/src/main/java/io/micronaut/security/token/generator/AccessRefreshTokenGenerator.java similarity index 94% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/AccessRefreshTokenGenerator.java rename to security/src/main/java/io/micronaut/security/token/generator/AccessRefreshTokenGenerator.java index d9326912fb..a2d2fe80af 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/AccessRefreshTokenGenerator.java +++ b/security/src/main/java/io/micronaut/security/token/generator/AccessRefreshTokenGenerator.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.generator; +package io.micronaut.security.token.generator; import io.micronaut.context.annotation.DefaultImplementation; import io.micronaut.core.annotation.NonNull; import io.micronaut.security.authentication.Authentication; -import io.micronaut.security.token.jwt.render.AccessRefreshToken; +import io.micronaut.security.token.render.AccessRefreshToken; import java.util.Map; import java.util.Optional; diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/AccessTokenConfiguration.java b/security/src/main/java/io/micronaut/security/token/generator/AccessTokenConfiguration.java similarity index 90% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/AccessTokenConfiguration.java rename to security/src/main/java/io/micronaut/security/token/generator/AccessTokenConfiguration.java index c5f8422829..05cf212575 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/AccessTokenConfiguration.java +++ b/security/src/main/java/io/micronaut/security/token/generator/AccessTokenConfiguration.java @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.generator; +package io.micronaut.security.token.generator; import io.micronaut.core.annotation.NonNull; /** * Configuration for access tokens. * - * @author James Kleeh - * @since 2.0.0 + * @author Sergio del Amo + * @since 3.2.0 */ @FunctionalInterface public interface AccessTokenConfiguration { diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/AccessTokenConfigurationProperties.java b/security/src/main/java/io/micronaut/security/token/generator/AccessTokenConfigurationProperties.java similarity index 85% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/AccessTokenConfigurationProperties.java rename to security/src/main/java/io/micronaut/security/token/generator/AccessTokenConfigurationProperties.java index cf6b80e447..27445c9e8d 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/AccessTokenConfigurationProperties.java +++ b/security/src/main/java/io/micronaut/security/token/generator/AccessTokenConfigurationProperties.java @@ -13,23 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.generator; +package io.micronaut.security.token.generator; import io.micronaut.context.annotation.ConfigurationProperties; import io.micronaut.core.annotation.NonNull; import io.micronaut.core.util.ArgumentUtils; -import io.micronaut.security.token.jwt.config.JwtConfigurationProperties; +import io.micronaut.security.token.config.TokenConfigurationProperties; /** * Access token configuration. * - * @author James Kleeh - * @since 2.0.0 + * @author Sergio del Amo + * @since 3.2.0 */ @ConfigurationProperties(AccessTokenConfigurationProperties.PREFIX) public class AccessTokenConfigurationProperties implements AccessTokenConfiguration { - public static final String PREFIX = JwtConfigurationProperties.PREFIX + ".generator.access-token"; + public static final String PREFIX = TokenConfigurationProperties.PREFIX + ".generator.access-token"; /** * The default expiration. diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/DefaultAccessRefreshTokenGenerator.java b/security/src/main/java/io/micronaut/security/token/generator/DefaultAccessRefreshTokenGenerator.java similarity index 94% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/DefaultAccessRefreshTokenGenerator.java rename to security/src/main/java/io/micronaut/security/token/generator/DefaultAccessRefreshTokenGenerator.java index 04ac1bed00..7abd18884c 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/DefaultAccessRefreshTokenGenerator.java +++ b/security/src/main/java/io/micronaut/security/token/generator/DefaultAccessRefreshTokenGenerator.java @@ -1,5 +1,9 @@ /* +<<<<<<< HEAD:security/src/main/java/io/micronaut/security/token/generator/DefaultAccessRefreshTokenGenerator.java + * Copyright 2017-2021 original authors +======= * Copyright 2017-2023 original authors +>>>>>>> master:security-jwt/src/main/java/io/micronaut/security/token/jwt/generator/DefaultAccessRefreshTokenGenerator.java * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,28 +17,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.generator; +package io.micronaut.security.token.generator; import io.micronaut.context.BeanContext; import io.micronaut.context.event.ApplicationEventPublisher; import io.micronaut.core.annotation.NonNull; import io.micronaut.core.annotation.Nullable; import io.micronaut.security.authentication.Authentication; +import io.micronaut.security.token.claims.ClaimsGenerator; import io.micronaut.security.token.event.AccessTokenGeneratedEvent; import io.micronaut.security.token.event.RefreshTokenGeneratedEvent; -import io.micronaut.security.token.generator.RefreshTokenGenerator; -import io.micronaut.security.token.generator.TokenGenerator; -import io.micronaut.security.token.jwt.generator.claims.ClaimsGenerator; -import io.micronaut.security.token.jwt.render.AccessRefreshToken; -import io.micronaut.security.token.jwt.render.TokenRenderer; import io.micronaut.security.token.refresh.RefreshTokenPersistence; +import io.micronaut.security.token.render.AccessRefreshToken; +import io.micronaut.security.token.render.TokenRenderer; import io.micronaut.security.token.validator.RefreshTokenValidator; import jakarta.inject.Singleton; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import static io.micronaut.security.utils.LoggingUtils.debug; @@ -42,7 +45,7 @@ * Generates http responses with access and refresh token. * * @author Sergio del Amo - * @since 1.0 + * @since 3.2.0 */ @Singleton public class DefaultAccessRefreshTokenGenerator implements AccessRefreshTokenGenerator { diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/render/AccessRefreshToken.java b/security/src/main/java/io/micronaut/security/token/render/AccessRefreshToken.java similarity index 98% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/render/AccessRefreshToken.java rename to security/src/main/java/io/micronaut/security/token/render/AccessRefreshToken.java index 01ab70a739..24f73fd360 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/render/AccessRefreshToken.java +++ b/security/src/main/java/io/micronaut/security/token/render/AccessRefreshToken.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.render; +package io.micronaut.security.token.render; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; @@ -105,3 +105,4 @@ public Integer getExpiresIn() { return expiresIn; } } + diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/render/BearerAccessRefreshToken.java b/security/src/main/java/io/micronaut/security/token/render/BearerAccessRefreshToken.java similarity index 98% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/render/BearerAccessRefreshToken.java rename to security/src/main/java/io/micronaut/security/token/render/BearerAccessRefreshToken.java index 5139e4993f..666fb4a9b8 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/render/BearerAccessRefreshToken.java +++ b/security/src/main/java/io/micronaut/security/token/render/BearerAccessRefreshToken.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.render; +package io.micronaut.security.token.render; import io.micronaut.core.annotation.NonNull; import io.micronaut.core.annotation.Nullable; diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/render/BearerTokenRenderer.java b/security/src/main/java/io/micronaut/security/token/render/BearerTokenRenderer.java similarity index 96% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/render/BearerTokenRenderer.java rename to security/src/main/java/io/micronaut/security/token/render/BearerTokenRenderer.java index ba99a0e1b9..64b0f46f84 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/render/BearerTokenRenderer.java +++ b/security/src/main/java/io/micronaut/security/token/render/BearerTokenRenderer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.render; +package io.micronaut.security.token.render; import io.micronaut.core.annotation.Nullable; import io.micronaut.http.HttpHeaderValues; @@ -25,7 +25,6 @@ * @author Sergio del Amo * @since 1.0 */ - @Singleton public class BearerTokenRenderer implements TokenRenderer { diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/render/TokenRenderer.java b/security/src/main/java/io/micronaut/security/token/render/TokenRenderer.java similarity index 97% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/render/TokenRenderer.java rename to security/src/main/java/io/micronaut/security/token/render/TokenRenderer.java index 695e4867c3..63ac895799 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/render/TokenRenderer.java +++ b/security/src/main/java/io/micronaut/security/token/render/TokenRenderer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.security.token.jwt.render; +package io.micronaut.security.token.render; import io.micronaut.core.annotation.Nullable; import io.micronaut.security.authentication.Authentication; diff --git a/security-jwt/src/main/java/io/micronaut/security/token/jwt/render/package-info.java b/security/src/main/java/io/micronaut/security/token/render/package-info.java similarity index 93% rename from security-jwt/src/main/java/io/micronaut/security/token/jwt/render/package-info.java rename to security/src/main/java/io/micronaut/security/token/render/package-info.java index 8714cc14ce..94188b6c71 100644 --- a/security-jwt/src/main/java/io/micronaut/security/token/jwt/render/package-info.java +++ b/security/src/main/java/io/micronaut/security/token/render/package-info.java @@ -19,5 +19,5 @@ * @author Sergio del Amo * @since 1.0 */ -package io.micronaut.security.token.jwt.render; +package io.micronaut.security.token.render; diff --git a/security/src/test/groovy/io/micronaut/security/endpoints/LoginControllerPathConfigurableSpec.groovy b/security/src/test/groovy/io/micronaut/security/endpoints/LoginControllerPathConfigurableSpec.groovy index 3dec2edd26..89fe475a23 100644 --- a/security/src/test/groovy/io/micronaut/security/endpoints/LoginControllerPathConfigurableSpec.groovy +++ b/security/src/test/groovy/io/micronaut/security/endpoints/LoginControllerPathConfigurableSpec.groovy @@ -37,6 +37,11 @@ class LoginControllerPathConfigurableSpec extends EmbeddedServerSpecification { 'LoginControllerPathConfigurableSpec' } + @Override + Map getLoginModeCookie() { + [:] + } + @Override Map getConfiguration() { super.configuration + [ diff --git a/security/src/test/groovy/io/micronaut/security/endpoints/LoginControllerValidationSpec.groovy b/security/src/test/groovy/io/micronaut/security/endpoints/LoginControllerValidationSpec.groovy index 911530a61a..717dfd0dec 100644 --- a/security/src/test/groovy/io/micronaut/security/endpoints/LoginControllerValidationSpec.groovy +++ b/security/src/test/groovy/io/micronaut/security/endpoints/LoginControllerValidationSpec.groovy @@ -22,6 +22,11 @@ class LoginControllerValidationSpec extends EmbeddedServerSpecification { 'LoginControllerValidationSpec' } + @Override + Map getLoginModeCookie() { + [:] + } + @Unroll("{\"username\": \"#username\", \"password\": \"#password\"} is invalid payload") void "LoginController responds BAD_REQUEST if POJO sent to /login is invalid"(String username, String password) { given: diff --git a/security/src/test/groovy/io/micronaut/security/endpoints/LogoutControllerAllowedMethodsGetSpec.groovy b/security/src/test/groovy/io/micronaut/security/endpoints/LogoutControllerAllowedMethodsGetSpec.groovy index 8fcb602ed9..ad30d136d6 100644 --- a/security/src/test/groovy/io/micronaut/security/endpoints/LogoutControllerAllowedMethodsGetSpec.groovy +++ b/security/src/test/groovy/io/micronaut/security/endpoints/LogoutControllerAllowedMethodsGetSpec.groovy @@ -18,6 +18,11 @@ class LogoutControllerAllowedMethodsGetSpec extends EmbeddedServerSpecification 'LogoutControllerAllowedMethodsGetSpec' } + @Override + Map getLoginModeCookie() { + [:] + } + @Override Map getConfiguration() { super.configuration + [ diff --git a/security/src/test/groovy/io/micronaut/security/endpoints/LogoutControllerPathConfigurableSpec.groovy b/security/src/test/groovy/io/micronaut/security/endpoints/LogoutControllerPathConfigurableSpec.groovy index b972fb8e07..6dc3784494 100644 --- a/security/src/test/groovy/io/micronaut/security/endpoints/LogoutControllerPathConfigurableSpec.groovy +++ b/security/src/test/groovy/io/micronaut/security/endpoints/LogoutControllerPathConfigurableSpec.groovy @@ -19,6 +19,11 @@ class LogoutControllerPathConfigurableSpec extends EmbeddedServerSpecification { 'LogoutControllerPathConfigurableSpec' } + @Override + Map getLoginModeCookie() { + [:] + } + @Override Map getConfiguration() { super.configuration + [ diff --git a/security/src/test/groovy/io/micronaut/security/events/EventListenerSpec.groovy b/security/src/test/groovy/io/micronaut/security/events/EventListenerSpec.groovy index 0d92bd1ab7..617b37b029 100644 --- a/security/src/test/groovy/io/micronaut/security/events/EventListenerSpec.groovy +++ b/security/src/test/groovy/io/micronaut/security/events/EventListenerSpec.groovy @@ -37,6 +37,11 @@ class EventListenerSpec extends EmbeddedServerSpecification { ] } + @Override + Map getLoginModeCookie() { + [:] + } + def "failed login publishes LoginFailedEvent"() { when: "sending request to login with bogus/password" HttpRequest request = HttpRequest.POST("/login", new UsernamePasswordCredentials("bogus", "password")) diff --git a/settings.gradle b/settings.gradle index 329104ba89..bdcbdabeb8 100644 --- a/settings.gradle +++ b/settings.gradle @@ -30,6 +30,7 @@ include "test-suite-tomcat-groovy" include "test-suite-utils" include "test-suite-utils-security" include "test-suite-kotlin" +include 'security-paseto' include "test-suite-aot" include "test-suite-aot-authserver" include "test-suite-serde" @@ -48,3 +49,4 @@ micronautBuild { importMicronautCatalog("micronaut-validation") importMicronautCatalog("micronaut-logging") } + diff --git a/test-suite-geb/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieAuthenticationSpec.groovy b/test-suite-geb/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieAuthenticationSpec.groovy index 7d7b7c42bb..05be5cbba1 100644 --- a/test-suite-geb/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieAuthenticationSpec.groovy +++ b/test-suite-geb/src/test/groovy/io/micronaut/security/token/jwt/cookie/JwtCookieAuthenticationSpec.groovy @@ -29,6 +29,8 @@ import io.micronaut.security.testutils.ConfigurationFixture import io.micronaut.security.testutils.ConfigurationUtils import io.micronaut.security.testutils.authprovider.MockAuthenticationProvider import io.micronaut.security.testutils.authprovider.SuccessAuthenticationScenario +import io.micronaut.security.token.cookie.TokenCookieClearerLogoutHandler +import io.micronaut.security.token.cookie.TokenCookieLoginHandler import io.micronaut.security.token.jwt.encryption.EncryptionConfiguration import io.micronaut.security.token.jwt.signature.SignatureConfiguration import io.micronaut.security.utils.BaseUrlUtils @@ -79,8 +81,8 @@ class JwtCookieAuthenticationSpec extends GebSpec { applicationContext.getBean(AuthenticationProviderUserPassword.class) applicationContext.getBean(LoginController.class) applicationContext.getBean(LogoutController.class) - applicationContext.getBean(JwtCookieLoginHandler.class) - applicationContext.getBean(JwtCookieClearerLogoutHandler.class) + applicationContext.getBean(TokenCookieLoginHandler.class) + applicationContext.getBean(TokenCookieClearerLogoutHandler.class) applicationContext.getBean(SignatureConfiguration.class) applicationContext.getBean(SignatureConfiguration.class, Qualifiers.byName("generator")) diff --git a/test-suite-geb/src/test/groovy/io/micronaut/security/token/jwt/cookie/OauthControllerEnabledSpec.groovy b/test-suite-geb/src/test/groovy/io/micronaut/security/token/jwt/cookie/OauthControllerEnabledSpec.groovy index 3836e238ff..ef3e2e219c 100644 --- a/test-suite-geb/src/test/groovy/io/micronaut/security/token/jwt/cookie/OauthControllerEnabledSpec.groovy +++ b/test-suite-geb/src/test/groovy/io/micronaut/security/token/jwt/cookie/OauthControllerEnabledSpec.groovy @@ -7,13 +7,12 @@ import io.micronaut.context.annotation.Requires import io.micronaut.http.HttpMethod import io.micronaut.runtime.server.EmbeddedServer import io.micronaut.security.authentication.Authentication +import io.micronaut.security.endpoints.OauthController import io.micronaut.security.oauth2.keycloack.v16.Keycloak -import io.micronaut.security.testutils.ConfigurationFixture import io.micronaut.security.testutils.ConfigurationUtils import io.micronaut.security.testutils.authprovider.MockAuthenticationProvider import io.micronaut.security.testutils.authprovider.SuccessAuthenticationScenario import io.micronaut.security.token.event.RefreshTokenGeneratedEvent -import io.micronaut.security.token.jwt.endpoints.OauthController import io.micronaut.security.token.refresh.RefreshTokenPersistence import io.micronaut.security.utils.BaseUrlUtils import io.micronaut.web.router.RouteMatch diff --git a/test-suite-keycloak/src/main/java/io/micronaut/security/oauth2/keycloak/KeycloakProviderResolver.java b/test-suite-keycloak/src/main/java/io/micronaut/security/oauth2/keycloak/KeycloakProviderResolver.java index a58d10f0a7..41a600c76d 100644 --- a/test-suite-keycloak/src/main/java/io/micronaut/security/oauth2/keycloak/KeycloakProviderResolver.java +++ b/test-suite-keycloak/src/main/java/io/micronaut/security/oauth2/keycloak/KeycloakProviderResolver.java @@ -4,7 +4,7 @@ import io.micronaut.security.oauth2.DefaultProviderResolver; import io.micronaut.security.oauth2.configuration.OpenIdClientConfiguration; import io.micronaut.security.testutils.TestContainersUtils; -import io.micronaut.security.token.jwt.generator.claims.JwtClaims; +import io.micronaut.security.token.Claims; import java.util.List; import java.util.Optional; @@ -16,7 +16,7 @@ protected KeycloakProviderResolver(List openIdClientC @Override protected Optional openIdClientNameWhichMatchesIssClaim(Authentication authentication) { - Object issuer = authentication.getAttributes().get(JwtClaims.ISSUER); + Object issuer = authentication.getAttributes().get(Claims.ISSUER); if (issuer == null) { return Optional.empty(); } diff --git a/test-suite-serde/src/test/groovy/io/micronaut/security/token/jwt/render/SerdeSpec.groovy b/test-suite-serde/src/test/groovy/io/micronaut/security/token/jwt/render/SerdeSpec.groovy index 4b6824b427..32866558df 100644 --- a/test-suite-serde/src/test/groovy/io/micronaut/security/token/jwt/render/SerdeSpec.groovy +++ b/test-suite-serde/src/test/groovy/io/micronaut/security/token/jwt/render/SerdeSpec.groovy @@ -1,5 +1,7 @@ package io.micronaut.security.token.jwt.render +import io.micronaut.security.token.render.AccessRefreshToken +import io.micronaut.security.token.render.BearerAccessRefreshToken import io.micronaut.serde.ObjectMapper import io.micronaut.test.extensions.spock.annotation.MicronautTest import jakarta.inject.Inject diff --git a/test-suite-utils/src/main/groovy/io/micronaut/security/testutils/ApplicationContextSpecification.groovy b/test-suite-utils/src/main/groovy/io/micronaut/security/testutils/ApplicationContextSpecification.groovy index 779b2a67bc..67e73d8338 100644 --- a/test-suite-utils/src/main/groovy/io/micronaut/security/testutils/ApplicationContextSpecification.groovy +++ b/test-suite-utils/src/main/groovy/io/micronaut/security/testutils/ApplicationContextSpecification.groovy @@ -16,6 +16,7 @@ package io.micronaut.security.testutils import io.micronaut.context.ApplicationContext +import io.micronaut.core.annotation.NonNull import spock.lang.AutoCleanup import spock.lang.Shared import spock.lang.Specification @@ -25,4 +26,16 @@ abstract class ApplicationContextSpecification extends Specification implements @Shared @AutoCleanup ApplicationContext applicationContext = ApplicationContext.run(configuration) + + boolean containsBean(Class beanType) { + applicationContext.containsBean(beanType) + } + + public T getBean(@NonNull Class beanType) { + applicationContext.getBean(beanType) + } + + public Collection getBeansOfType(@NonNull Class beanType) { + applicationContext.getBeansOfType(beanType) + } } diff --git a/test-suite-utils/src/main/groovy/io/micronaut/security/testutils/EmbeddedServerSpecification.groovy b/test-suite-utils/src/main/groovy/io/micronaut/security/testutils/EmbeddedServerSpecification.groovy index 58cc2e99a5..55ca46d1b3 100644 --- a/test-suite-utils/src/main/groovy/io/micronaut/security/testutils/EmbeddedServerSpecification.groovy +++ b/test-suite-utils/src/main/groovy/io/micronaut/security/testutils/EmbeddedServerSpecification.groovy @@ -16,6 +16,7 @@ package io.micronaut.security.testutils import io.micronaut.context.ApplicationContext +import io.micronaut.core.annotation.NonNull import io.micronaut.http.client.BlockingHttpClient import io.micronaut.http.client.HttpClient import io.micronaut.runtime.server.EmbeddedServer @@ -37,4 +38,16 @@ abstract class EmbeddedServerSpecification extends Specification implements Conf @Shared BlockingHttpClient client = httpClient.toBlocking() + + boolean containsBean(Class beanType) { + applicationContext.containsBean(beanType) + } + + public T getBean(@NonNull Class beanType) { + applicationContext.getBean(beanType) + } + + public Collection getBeansOfType(@NonNull Class beanType) { + applicationContext.getBeansOfType(beanType) + } }