Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,17 @@
*/
package io.meeds.oauth2.server.configuration;

import java.time.Instant;
import java.util.UUID;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.OAuth2Token;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.token.JwtGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2AccessTokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;

import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;

import io.meeds.oauth2.server.service.OAuthJwkService;
import io.meeds.oauth2.server.service.OAuthJwtCustomizerService;
import io.meeds.oauth2.server.util.Utils;

@Configuration
public class OAuthJwtConfiguration {
Expand All @@ -68,35 +52,4 @@ JWKSource<SecurityContext> jwkSource(OAuthJwkService oAuthJwkService) {
return (jwkSelector, securityContext) -> jwkSelector.select(oAuthJwkService.getJwkSet());
}

@Bean
OAuth2TokenCustomizer<JwtEncodingContext> jwtAccessTokenCustomizer(OAuthJwtCustomizerService oAuthJwtCustomizerService) {
return oAuthJwtCustomizerService::customizeAccessTokenClaims;
}

@Bean
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator(JwtEncoder jwtEncoder) { // NOSONAR
JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder);
return new DelegatingOAuth2TokenGenerator(jwtGenerator,
new OAuth2AccessTokenGenerator(),
this::generateRefreshToken);
}

private OAuth2RefreshToken generateRefreshToken(OAuth2TokenContext context) {
if (OAuth2TokenType.REFRESH_TOKEN.equals(context.getTokenType())
&& context.getAuthorizedScopes().contains(Utils.OFFLINE_ACCESS_SCOPE)
&& context.getRegisteredClient()
.getAuthorizationGrantTypes()
.contains(AuthorizationGrantType.REFRESH_TOKEN)) {
Instant issuedAt = Instant.now();
Instant expiresAt = issuedAt.plus(context.getRegisteredClient()
.getTokenSettings()
.getRefreshTokenTimeToLive());

String value = UUID.randomUUID() + "-" + UUID.randomUUID();
return new OAuth2RefreshToken(value, issuedAt, expiresAt);
} else {
return null;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,19 @@
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.OAuth2Token;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadata;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationEndpointConfigurer;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OidcClientRegistrationEndpointConfigurer;
import org.springframework.security.oauth2.server.authorization.oidc.OidcProviderConfiguration;
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.JwtGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2AccessTokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.WebAttributes;
Expand All @@ -74,11 +80,13 @@
import io.meeds.oauth2.server.configuration.model.OAuthDefaultSettings;
import io.meeds.oauth2.server.plugin.OAuthAuthorizationRequestConverter;
import io.meeds.oauth2.server.plugin.OAuthDcrHttpAuthenticationConverter;
import io.meeds.oauth2.server.plugin.OAuthRefreshTokenGenerator;
import io.meeds.oauth2.server.plugin.OAuthRefreshTokenPublicAuthenticationProvider;
import io.meeds.oauth2.server.plugin.OAuthRefreshTokenPublicClientAuthenticationConverter;
import io.meeds.oauth2.server.security.OAuthCimdAuthenticationProvider;
import io.meeds.oauth2.server.security.OAuthDcrAuthenticationProvider;
import io.meeds.oauth2.server.security.OAuthPortalAuthenticationProvider;
import io.meeds.oauth2.server.service.OAuthAccessTokenCustomizerService;
import io.meeds.oauth2.server.service.OAuthClientService;
import io.meeds.oauth2.server.service.OAuthSettingService;
import io.meeds.oauth2.server.web.OAuthCorsConfigurationSource;
Expand Down Expand Up @@ -114,6 +122,8 @@ SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http,
OAuthRefreshTokenPublicAuthenticationProvider oAuthRefreshTokenPublicAuthenticationProvider,
OAuthRefreshTokenPublicClientAuthenticationConverter oAuthRefreshTokenPublicClientAuthenticationConverter,
SecurityContextRepository securityContextRepository,
@Qualifier("oauthTokenGenerator")
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator,
@Qualifier("oauthAuthenticationProvider")
OAuthDcrAuthenticationProvider oauthAuthenticationProvider,
@Qualifier("oauthAuthenticationEntryPoint")
Expand All @@ -128,7 +138,8 @@ SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http,
.addFilterBefore(portalPreAuthenticatedFilter, AbstractPreAuthenticatedProcessingFilter.class)
.authenticationProvider(portalAuthenticationProvider)
.with(authorizationServer,
a -> a.authorizationEndpoint(e -> customizeAuthorizationEndpoint(e,
a -> a.tokenGenerator(tokenGenerator)
.authorizationEndpoint(e -> customizeAuthorizationEndpoint(e,
cimdAuthenticationProvider,
oAuthAuthorizationRequestConverter))
.authorizationServerMetadataEndpoint(oauth -> oauth.authorizationServerMetadataCustomizer(c -> customizeMetadata(c,
Expand Down Expand Up @@ -247,6 +258,19 @@ OAuthDcrAuthenticationProvider oauthAuthenticationProvider(OAuthClientService oA
}
// @formatter:on

@Bean("oauthTokenGenerator")
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator(JwtEncoder jwtEncoder, // NOSONAR
OAuthAccessTokenCustomizerService oAuthAccessTokenCustomizerService,
OAuthRefreshTokenGenerator oAuthRefreshTokenGenerator) {
JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder);
jwtGenerator.setJwtCustomizer(oAuthAccessTokenCustomizerService::customize);
OAuth2AccessTokenGenerator oAuth2AccessTokenGenerator = new OAuth2AccessTokenGenerator();
oAuth2AccessTokenGenerator.setAccessTokenCustomizer(oAuthAccessTokenCustomizerService);
return new DelegatingOAuth2TokenGenerator(jwtGenerator,
oAuth2AccessTokenGenerator,
oAuthRefreshTokenGenerator);
}

@Bean
RestClient restClient() {
HttpClient httpClient = HttpClient.newBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;

@FunctionalInterface
public interface OAuthJwtAudienceProvider {
public interface OAuthAccessTokenAudienceProvider {

List<String> provideAudiences(OAuth2TokenContext context);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;

@FunctionalInterface
public interface OAuthJwtAuthorityProvider {
public interface OAuthAccessTokenAuthorityProvider {

Set<String> provideAuthorities(OAuth2TokenContext context);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
import org.springframework.stereotype.Service;

import io.meeds.oauth2.server.configuration.plugin.OAuthJwtAudienceProvider;
import io.meeds.oauth2.server.configuration.plugin.OAuthAccessTokenAudienceProvider;
import io.meeds.oauth2.server.service.OAuthSettingService;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class OAuthJwtAudienceTokenRequestProvider implements OAuthJwtAudienceProvider {
public class OAuthAccessTokenAudienceTokenRequestProvider implements OAuthAccessTokenAudienceProvider {

@Autowired
private OAuthSettingService oAuthSettingService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
import org.springframework.stereotype.Service;

import io.meeds.oauth2.server.configuration.plugin.OAuthJwtAuthorityProvider;
import io.meeds.oauth2.server.configuration.plugin.OAuthAccessTokenAuthorityProvider;

@Service
@Order(Ordered.LOWEST_PRECEDENCE)
public class OAuthJwtAuthorityPrincipalProvider implements OAuthJwtAuthorityProvider {
public class OAuthAccessTokenAuthorityPrincipalProvider implements OAuthAccessTokenAuthorityProvider {

@Override
public Set<String> provideAuthorities(OAuth2TokenContext context) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* This file is part of the Meeds project (https://meeds.io/).
*
* Copyright (C) 2020 - 2026 Meeds Association contact@meeds.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package io.meeds.oauth2.server.plugin;

import static io.meeds.oauth2.server.util.EntityMapper.CLIENT_IS_CIMD_SETTING;
import static io.meeds.oauth2.server.util.EntityMapper.CLIENT_IS_DCR_SETTING;

import java.time.Instant;
import java.util.Base64;
import java.util.Objects;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
import org.springframework.security.crypto.keygen.StringKeyGenerator;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.stereotype.Component;

import io.meeds.oauth2.server.util.Utils;

@Component
public final class OAuthRefreshTokenGenerator implements OAuth2TokenGenerator<OAuth2RefreshToken> {

private final StringKeyGenerator refreshTokenGenerator = new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(),
96);

@Value("${meeds.oauth.allow_public_refresh_tokens:true}")
private boolean allowPublicRefreshTokens;

@Override
public OAuth2RefreshToken generate(OAuth2TokenContext context) {
if (!OAuth2TokenType.REFRESH_TOKEN.equals(context.getTokenType())) {
return null;
}
RegisteredClient client = context.getRegisteredClient();
boolean isPublicClient = Objects.equals(client.getClientSettings().getSetting(CLIENT_IS_CIMD_SETTING), true)
|| Objects.equals(client.getClientSettings().getSetting(CLIENT_IS_DCR_SETTING), true)
|| client.getClientAuthenticationMethods().contains(ClientAuthenticationMethod.NONE);
if (isPublicClient && !allowPublicRefreshTokens) {
return null;
}

if (OAuth2TokenType.REFRESH_TOKEN.equals(context.getTokenType())
&& context.getAuthorizedScopes().contains(Utils.OFFLINE_ACCESS_SCOPE)
&& client.getScopes()
.contains(Utils.OFFLINE_ACCESS_SCOPE)
&& client.getAuthorizationGrantTypes()
.contains(AuthorizationGrantType.REFRESH_TOKEN)) {
Instant issuedAt = Instant.now();
Instant expiresAt = issuedAt.plus(client
.getTokenSettings()
.getRefreshTokenTimeToLive());

return new OAuth2RefreshToken(this.refreshTokenGenerator.generateKey(),
issuedAt,
expiresAt);
} else {
return null;
}
}

}
Loading
Loading