Skip to content

Commit

Permalink
feature : add conditional Introspector settings based on application.…
Browse files Browse the repository at this point in the history
…properties
  • Loading branch information
patternhelloworld committed Jan 4, 2025
1 parent 27d7cbb commit 6722a70
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 81 deletions.
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

> App-Token based OAuth2 POC built to grow with Spring Boot and ORM
- [NOTICE] Test codes will be temporarily non-functional due to the introduction of the Introspector, until the next version.

## Supporting Oauth2 Type
| ROPC | Authorization Code |
|------------------|-------------------------------------------------|
Expand All @@ -14,7 +12,7 @@
<dependency>
<groupId>io.github.patternknife.securityhelper.oauth2.api</groupId>
<artifactId>spring-security-oauth2-password-jpa-implementation</artifactId>
<version>3.3.0</version>
<version>3.4.0</version>
</dependency>
```
For v2, using the database tables from Spring Security 5 (only the database tables; follow the dependencies as above):
Expand Down
4 changes: 2 additions & 2 deletions client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.patternknife.securityhelper.oauth2.client</groupId>
<artifactId>spring-security-oauth2-password-jpa-implementation-client</artifactId>
<version>3.3.0</version>
<version>3.4.0</version>
<packaging>jar</packaging>

<properties>
Expand Down Expand Up @@ -48,7 +48,7 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd">
<dependency>
<groupId>io.github.patternknife.securityhelper.oauth2.api</groupId>
<artifactId>spring-security-oauth2-password-jpa-implementation</artifactId>
<version>3.3.0</version>
<version>3.4.0</version>
</dependency>

<!-- DB -->
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,21 @@ public class UserCustomerOnlyImpl {
public Object check(ProceedingJoinPoint joinPoint) throws Throwable {

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
AccessTokenUserInfo accessTokenUserInfo;
if (principal instanceof AccessTokenUserInfo) {
accessTokenUserInfo = ((AccessTokenUserInfo) principal);
}else {
String userName = ((OAuth2IntrospectionAuthenticatedPrincipal) principal).getUsername();
String clientId = ((OAuth2IntrospectionAuthenticatedPrincipal) principal).getClientId();
String appToken = ((OAuth2IntrospectionAuthenticatedPrincipal) principal).getAttribute("App-Token");

String userName =((OAuth2IntrospectionAuthenticatedPrincipal)principal).getUsername();
String clientId =((OAuth2IntrospectionAuthenticatedPrincipal)principal).getClientId();
String appToken = ((OAuth2IntrospectionAuthenticatedPrincipal)principal).getAttribute("App-Token");

OAuth2Authorization oAuth2Authorization = authorizationService.findByUserNameAndClientIdAndAppToken(userName, clientId, appToken);

OAuth2Authorization oAuth2Authorization = authorizationService.findByUserNameAndClientIdAndAppToken(userName, clientId, appToken);

UserDetails userDetails = conditionalDetailsService.loadUserByUsername(userName, clientId);

AccessTokenUserInfo accessTokenUserInfo = ((AccessTokenUserInfo)userDetails);
UserDetails userDetails = conditionalDetailsService.loadUserByUsername(userName, clientId);

accessTokenUserInfo = ((AccessTokenUserInfo) userDetails);
}

if(accessTokenUserInfo != null && (accessTokenUserInfo.getAdditionalAccessTokenUserInfo().getUserType() != AdditionalAccessTokenUserInfo.UserType.CUSTOMER)){
throw new CustomAuthGuardException("ID \"" + accessTokenUserInfo.getUsername() + "\" : Not in Customer Group");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,73 @@
package com.patternknife.securityhelper.oauth2.client.config.securityimpl.introspector;

import io.github.patternknife.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage;
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService;
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.exception.KnifeOauth2AuthenticationException;
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.persistence.authorization.OAuth2AuthorizationServiceImpl;
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.userdetail.ConditionalDetailsService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
import org.springframework.stereotype.Component;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;

/*
* Set this to your resource servers
* */
@Component
public class CustomDefaultResourceServerTokenIntrospector implements OpaqueTokenIntrospector {

private final OpaqueTokenIntrospector delegate;

/*
* api : resource servers call the authorization server
* database : the database is shared with the authorization server and resource servers
* */
@Value("${patternknife.securityhelper.oauth2.introspection.type}") String introspectionType;
@Value("${patternknife.securityhelper.oauth2.introspection.uri}") String introspectionUri;
@Value("${patternknife.securityhelper.oauth2.introspection.client-id}") String clientId;
@Value("${patternknife.securityhelper.oauth2.introspection.client-secret}") String clientSecret;


private final OAuth2AuthorizationServiceImpl authorizationService;
private final ConditionalDetailsService conditionalDetailsService;
private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService;


public CustomDefaultResourceServerTokenIntrospector(
@Value("${security.oauth2.introspection.uri}") String introspectionUri,
@Value("${security.oauth2.introspection.client-id}") String clientId,
@Value("${security.oauth2.introspection.client-secret}") String clientSecret) {
OAuth2AuthorizationServiceImpl authorizationService,
ConditionalDetailsService conditionalDetailsService,
ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService,
@Value("${patternknife.securityhelper.oauth2.introspection.type}") String introspectionType,
@Value("${patternknife.securityhelper.oauth2.introspection.uri}") String introspectionUri,
@Value("${patternknife.securityhelper.oauth2.introspection.client-id}") String clientId,
@Value("${patternknife.securityhelper.oauth2.introspection.client-secret}") String clientSecret) {
this.delegate = new SpringOpaqueTokenIntrospector(introspectionUri, clientId, clientSecret);
this.authorizationService = authorizationService;
this.conditionalDetailsService = conditionalDetailsService;
this.iSecurityUserExceptionMessageService = iSecurityUserExceptionMessageService;
}

@Override
public OAuth2AuthenticatedPrincipal introspect(String token) {
try {
return delegate.introspect(token);
} catch (Exception e) {
throw new KnifeOauth2AuthenticationException(e.getMessage());
if(introspectionType.equals("api")) {
try {
return delegate.introspect(token);
} catch (Exception e) {
throw new KnifeOauth2AuthenticationException(e.getMessage());
}
} else if (introspectionType.equals("database")) {
OAuth2Authorization oAuth2Authorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);

if(oAuth2Authorization == null || oAuth2Authorization.getAccessToken() == null || oAuth2Authorization.getAccessToken().isExpired()
|| oAuth2Authorization.getRefreshToken() == null || oAuth2Authorization.getRefreshToken().isExpired()){
throw new KnifeOauth2AuthenticationException(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_TOKEN_FAILURE));
}
return (OAuth2AuthenticatedPrincipal) conditionalDetailsService.loadUserByUsername(oAuth2Authorization.getPrincipalName(), (String) oAuth2Authorization.getAttributes().get("client_id"));
}else{
throw new KnifeOauth2AuthenticationException("Wrong introspection type : " + introspectionType);
}
}
}
Expand Down
11 changes: 7 additions & 4 deletions client/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ io.github.patternknife.securityhelper.oauth2.no-app-token-same-access-token=true
spring.mvc.view.prefix=/templates/
spring.mvc.view.suffix=.html


security.oauth2.introspection.uri=http://localhost:8370/oauth2/introspect
security.oauth2.introspection.client-id=client_customer
security.oauth2.introspection.client-secret=12345
# api vs database
# api : resource servers call the authorization server / database : the database is shared with resource servers
# [WARNING] api : some test codes currently NOT working due to the following uri calling.
patternknife.securityhelper.oauth2.introspection.type=database
patternknife.securityhelper.oauth2.introspection.uri=http://localhost:8370/oauth2/introspect
patternknife.securityhelper.oauth2.introspection.client-id=client_customer
patternknife.securityhelper.oauth2.introspection.client-secret=12345
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ public void setUp(RestDocumentationContextProvider restDocumentationContextProvi
.setControllerAdvice(new GlobalExceptionHandler(iSecurityUserExceptionMessageService))
.setCustomArgumentResolvers(putAuthenticationPrincipal)
.apply(documentationConfiguration(restDocumentationContextProvider).uris()
.withScheme("https")
.withHost("vholic.com")
.withPort(443))
.withScheme("http")
.withHost("localhost")
.withPort(8370))
.addFilters(new CharacterEncodingFilter("UTF-8", true))
.alwaysDo(document)
.build();
Expand Down
2 changes: 1 addition & 1 deletion lib/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd">

<groupId>io.github.patternknife.securityhelper.oauth2.api</groupId>
<artifactId>spring-security-oauth2-password-jpa-implementation</artifactId>
<version>3.3.0</version>
<version>3.4.0</version>
<name>spring-security-oauth2-password-jpa-implementation</name>
<description>The implementation of Spring Security 6 Spring Authorization Server for stateful OAuth2 Password Grant</description>
<packaging>jar</packaging>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,53 +1,51 @@
package io.github.patternknife.securityhelper.oauth2.api.config.security.provider.resource.introspector;
package io.github.patternknife.securityhelper.oauth2.api.config.security.introspector;


import io.github.patternknife.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage;
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService;
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.exception.KnifeOauth2AuthenticationException;
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.persistence.authorization.OAuth2AuthorizationServiceImpl;
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.userdetail.ConditionalDetailsService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
import org.springframework.stereotype.Component;


public class DefaultResourceServerTokenIntrospector implements OpaqueTokenIntrospector {

private OpaqueTokenIntrospector delegate =
new SpringOpaqueTokenIntrospector(
"http://localhost:8370/oauth2/introspect",
"barClient",
"barClientSecret"
);

public DefaultResourceServerTokenIntrospector() {

private final OAuth2AuthorizationServiceImpl authorizationService;
private final ConditionalDetailsService conditionalDetailsService;
private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService;

public DefaultResourceServerTokenIntrospector(
OAuth2AuthorizationServiceImpl authorizationService,
ConditionalDetailsService conditionalDetailsService,
ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService,
@Value("${patternknife.securityhelper.oauth2.introspection.type}") String introspectionType,
@Value("${patternknife.securityhelper.oauth2.introspection.uri}") String introspectionUri,
@Value("${patternknife.securityhelper.oauth2.introspection.client-id}") String clientId,
@Value("${patternknife.securityhelper.oauth2.introspection.client-secret}") String clientSecret) {

/*
* @Values will be ignored here. Check CustomResourceServerTokenIntrospector in the sample client folder.
* */
this.authorizationService = authorizationService;
this.conditionalDetailsService = conditionalDetailsService;
this.iSecurityUserExceptionMessageService = iSecurityUserExceptionMessageService;
}

@Override
public OAuth2AuthenticatedPrincipal introspect(String token) {

/* try {
OAuth2AuthenticatedPrincipal principal = delegate.introspect(token);
return principal;
}catch (Exception e){
//throw e;
throw new KnifeOauth2AuthenticationException(e.getMessage());
}*/
return null;

/*OAuth2Authorization oAuth2Authorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
OAuth2Authorization oAuth2Authorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);

if(oAuth2Authorization == null || oAuth2Authorization.getAccessToken() == null || oAuth2Authorization.getAccessToken().isExpired()
|| oAuth2Authorization.getRefreshToken() == null || oAuth2Authorization.getRefreshToken().isExpired()){
throw new KnifeOauth2AuthenticationException(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_TOKEN_FAILURE));
//return null;
}

return (OAuth2AuthenticatedPrincipal) conditionalDetailsService.loadUserByUsername(oAuth2Authorization.getPrincipalName(), (String) oAuth2Authorization.getAttributes().get("client_id"));*/
return (OAuth2AuthenticatedPrincipal) conditionalDetailsService.loadUserByUsername(oAuth2Authorization.getPrincipalName(), (String) oAuth2Authorization.getAttributes().get("client_id"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.userdetail.ConditionalDetailsService;
import io.github.patternknife.securityhelper.oauth2.api.config.security.token.generator.CustomDelegatingOAuth2TokenGenerator;

import io.github.patternknife.securityhelper.oauth2.api.config.security.provider.resource.introspector.DefaultResourceServerTokenIntrospector;
import io.github.patternknife.securityhelper.oauth2.api.config.security.introspector.DefaultResourceServerTokenIntrospector;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand Down Expand Up @@ -299,7 +300,12 @@ public AuthenticationEntryPoint iAuthenticationEntryPoint(@Qualifier("handlerExc

@Bean
@ConditionalOnMissingBean(OpaqueTokenIntrospector.class)
public OpaqueTokenIntrospector tokenIntrospector() {
return new DefaultResourceServerTokenIntrospector();
public OpaqueTokenIntrospector tokenIntrospector(OAuth2AuthorizationServiceImpl authorizationService,
ConditionalDetailsService conditionalDetailsService, ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService,
@Value("${patternknife.securityhelper.oauth2.introspection.type:database}") String introspectionType,
@Value("${patternknife.securityhelper.oauth2.introspection.uri}") String introspectionUri,
@Value("${patternknife.securityhelper.oauth2.introspection.client-id}") String clientId,
@Value("${patternknife.securityhelper.oauth2.introspection.client-secret}") String clientSecret) {
return new DefaultResourceServerTokenIntrospector(authorizationService, conditionalDetailsService, iSecurityUserExceptionMessageService, introspectionType, introspectionUri, clientId, clientSecret);
}
}

0 comments on commit 6722a70

Please sign in to comment.