From 609e26b63346221c263d7f34843e23b90587040d Mon Sep 17 00:00:00 2001 From: damon Date: Sat, 23 May 2026 23:55:25 +0800 Subject: [PATCH 1/7] Update pom.xml to add Alibaba Nacos discovery dependency, adjust Spring dependencies, and bump neroyun.mediator version --- config/pom.xml | 4 ++++ framework/pom.xml | 29 +++++++++++++++++++++++++++++ identity/pom.xml | 6 +++--- message/pom.xml | 6 +++--- pom.xml | 4 ++-- 5 files changed, 41 insertions(+), 8 deletions(-) diff --git a/config/pom.xml b/config/pom.xml index 76ef508..285f24f 100644 --- a/config/pom.xml +++ b/config/pom.xml @@ -19,6 +19,10 @@ org.springframework.cloud spring-cloud-config-server + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + org.springframework.boot diff --git a/framework/pom.xml b/framework/pom.xml index 0c1cf48..cab14c2 100644 --- a/framework/pom.xml +++ b/framework/pom.xml @@ -12,7 +12,36 @@ framework + + 7.0.6 + + + + com.neroyun + mediator + ${neroyun.mediator.version} + + + org.springframework + spring-core + ${spring-framework.version} + + + org.springframework + spring-context + ${spring-framework.version} + + + org.springframework + spring-beans + ${spring-framework.version} + + + org.springframework + spring-web + ${spring-framework.version} + org.bouncycastle bcprov-jdk18on diff --git a/identity/pom.xml b/identity/pom.xml index c899e4a..7762455 100644 --- a/identity/pom.xml +++ b/identity/pom.xml @@ -47,11 +47,11 @@ org.springframework.boot - spring-boot-starter-webflux + spring-boot-starter-web org.springdoc - springdoc-openapi-starter-webflux-ui + springdoc-openapi-starter-webmvc-ui 3.0.3 @@ -91,7 +91,7 @@ org.springframework.boot - spring-boot-starter-webflux-test + spring-boot-starter-webmvc-test test diff --git a/message/pom.xml b/message/pom.xml index cc9fa41..fc8ce14 100644 --- a/message/pom.xml +++ b/message/pom.xml @@ -31,11 +31,11 @@ org.springframework.boot - spring-boot-starter-webflux + spring-boot-starter-web org.springdoc - springdoc-openapi-starter-webflux-ui + springdoc-openapi-starter-webmvc-ui 3.0.3 @@ -80,7 +80,7 @@ org.springframework.boot - spring-boot-starter-webflux-test + spring-boot-starter-webmvc-test test diff --git a/pom.xml b/pom.xml index fd5231f..711740f 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ 2025.1.0.0 4.0.6 true - 1.1.0 + 1.1.1 @@ -77,7 +77,7 @@ org.springdoc - springdoc-openapi-starter-webflux-ui + springdoc-openapi-starter-webmvc-ui 3.0.3 pom import From be2f7b326f703724433c353f2d9198e6b41909c9 Mon Sep 17 00:00:00 2001 From: damon Date: Sat, 23 May 2026 23:59:13 +0800 Subject: [PATCH 2/7] Add ApplicationService interface and BaseApplicationService implementation for orchestrating use cases --- .../application/ApplicationService.java | 7 +++++ .../application/BaseApplicationService.java | 31 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 framework/src/main/java/io/theurl/framework/application/ApplicationService.java create mode 100644 framework/src/main/java/io/theurl/framework/application/BaseApplicationService.java diff --git a/framework/src/main/java/io/theurl/framework/application/ApplicationService.java b/framework/src/main/java/io/theurl/framework/application/ApplicationService.java new file mode 100644 index 0000000..f95f4f3 --- /dev/null +++ b/framework/src/main/java/io/theurl/framework/application/ApplicationService.java @@ -0,0 +1,7 @@ +package io.theurl.framework.application; + +/** + * Marker interface for application services. Application services are responsible for orchestrating use cases and business logic. + */ +public interface ApplicationService { +} diff --git a/framework/src/main/java/io/theurl/framework/application/BaseApplicationService.java b/framework/src/main/java/io/theurl/framework/application/BaseApplicationService.java new file mode 100644 index 0000000..c1342ff --- /dev/null +++ b/framework/src/main/java/io/theurl/framework/application/BaseApplicationService.java @@ -0,0 +1,31 @@ +package io.theurl.framework.application; + +import com.neroyun.mediator.Mediator; +import org.springframework.context.ApplicationContext; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Service; +import org.springframework.web.context.annotation.RequestScope; + +import java.util.concurrent.CompletionException; +import java.util.function.Consumer; + +@Service +@RequestScope +public class BaseApplicationService implements ApplicationService { + protected Mediator mediator; + + protected ThreadPoolTaskExecutor mediatorTaskExecutor; + + protected BaseApplicationService(ApplicationContext applicationContext) { + mediator = applicationContext.getBean(Mediator.class); + mediatorTaskExecutor = applicationContext.getBean(ThreadPoolTaskExecutor.class); + } + + protected void handleException(Throwable throwable, Consumer consumer) { + if (throwable instanceof CompletionException exception) { + handleException(exception.getCause(), consumer); + } else { + consumer.accept(throwable); + } + } +} From e598db49f8c4bd243c18b75524d2f84b082b052e Mon Sep 17 00:00:00 2001 From: damon Date: Sun, 24 May 2026 00:01:44 +0800 Subject: [PATCH 3/7] Enhance application configuration with logging settings and JWT support --- identity/src/main/resources/application.yaml | 16 ++- .../src/main/resources/logback-spring.xml | 107 ++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 identity/src/main/resources/logback-spring.xml diff --git a/identity/src/main/resources/application.yaml b/identity/src/main/resources/application.yaml index d682a50..00d39a6 100644 --- a/identity/src/main/resources/application.yaml +++ b/identity/src/main/resources/application.yaml @@ -22,11 +22,19 @@ spring: ddl-auto: update dialect: ${DB_DIALECT:org.hibernate.dialect.PostgreSQLDialect} show-sql: true + properties: + hibernate: + format_sql: true data: redis: url: ${REDIS_URL:redis://localhost:6379} mongodb: uri: ${MONGO_URI:mongodb://localhost:27017/linkyou} + web: + error: + include-message: ALWAYS + include-exception: true + include-stacktrace: ALWAYS external-auth: redirect-uri: "https://theurl.io/auth/callback" @@ -43,10 +51,16 @@ external-auth: client-id: ${MICROSOFT_CLIENT_ID:your-microsoft-client-id} client-secret: ${MICROSOFT_CLIENT_SECRET:your-microsoft-client-secret} +jwt: + secret: ${JWT_SECRET:your-jwt-secret} + issuer: theurl.io + expiration: 3600 # in seconds + logging: file: + name: #{level}-{T(java.time.LocalDate).now()}.log path: logs level: io.theurl.identity: debug - org.springframework: info + org.springframework: debug root: info diff --git a/identity/src/main/resources/logback-spring.xml b/identity/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..2781bc8 --- /dev/null +++ b/identity/src/main/resources/logback-spring.xml @@ -0,0 +1,107 @@ + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + utf8 + + + + + ${LOG_PATH}/info/current.log + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + ${LOG_PATH}/info/%d{yyyy-MM-dd}.log + 100MB + 30 + + + + + ${LOG_PATH}/error/current.log + + ERROR + ACCEPT + DENY + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + ${LOG_PATH}/error/%d{yyyy-MM-dd}.log + 100MB + 30 + + + + + ${LOG_PATH}/warn/current.log + + WARN + ACCEPT + DENY + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + ${LOG_PATH}/warn/%d{yyyy-MM-dd}.log + 100MB + 30 + + + + + ${LOG_PATH}/debug/current.log + + DEBUG + ACCEPT + DENY + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + ${LOG_PATH}/debug/%d{yyyy-MM-dd}.log + 100MB + 30 + + + + + ${LOG_PATH}/sql/current.log + + DEBUG + ACCEPT + DENY + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + ${LOG_PATH}/sql/%d{yyyy-MM-dd}.log + 100MB + 30 + + + + + + + + + + + + + + + + + + + From c38d9659002bd9b150bf68f0a22b746ef075990f Mon Sep 17 00:00:00 2001 From: damon Date: Sun, 24 May 2026 00:20:18 +0800 Subject: [PATCH 4/7] Add global exception handler and mediator configuration for improved error handling --- framework/pom.xml | 9 ++ .../configure/GlobalExceptionHandler.java | 84 +++++++++++++++++++ .../configure/MediatorConfiguration.java | 50 +++++++++++ .../configure/RestTemplateConfig.java | 30 +++++++ .../framework/utility/GuidGenerator.java | 4 + pom.xml | 15 ++++ 6 files changed, 192 insertions(+) create mode 100644 framework/src/main/java/io/theurl/framework/configure/GlobalExceptionHandler.java create mode 100644 framework/src/main/java/io/theurl/framework/configure/MediatorConfiguration.java create mode 100644 framework/src/main/java/io/theurl/framework/configure/RestTemplateConfig.java create mode 100644 framework/src/main/java/io/theurl/framework/utility/GuidGenerator.java diff --git a/framework/pom.xml b/framework/pom.xml index cab14c2..0fb73e7 100644 --- a/framework/pom.xml +++ b/framework/pom.xml @@ -47,6 +47,15 @@ bcprov-jdk18on 1.84 + + jakarta.persistence + jakarta.persistence-api + ${jakarta-persistence.version} + + + org.slf4j + slf4j-api + org.junit.jupiter junit-jupiter diff --git a/framework/src/main/java/io/theurl/framework/configure/GlobalExceptionHandler.java b/framework/src/main/java/io/theurl/framework/configure/GlobalExceptionHandler.java new file mode 100644 index 0000000..52b835c --- /dev/null +++ b/framework/src/main/java/io/theurl/framework/configure/GlobalExceptionHandler.java @@ -0,0 +1,84 @@ +package io.theurl.framework.configure; + +import com.neroyun.mediator.validation.ValidationException; +import io.theurl.framework.security.AccountException; +import io.theurl.framework.security.CredentialException; +import io.theurl.framework.security.UnauthorizedAccessException; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.concurrent.CompletionException; + +@RestControllerAdvice +public class GlobalExceptionHandler { + private final String name = "message"; + private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + @ExceptionHandler(AccountException.class) + public ResponseEntity> handleAccountException(AccountException exception) { + LOGGER.error(exception.getMessage(), exception); + return ResponseEntity.status(HttpStatus.FORBIDDEN) + .body(Map.of(name, exception.getLocalizedMessage())); + } + + @ExceptionHandler(CredentialException.class) + public ResponseEntity> handleCredentialException(CredentialException exception) { + LOGGER.error(exception.getMessage(), exception); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED) + .body(Map.of(name, exception.getLocalizedMessage())); + } + + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity> handleIllegalArgumentException(IllegalArgumentException exception) { + LOGGER.error(exception.getMessage(), exception); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(Map.of(name, exception.getLocalizedMessage())); + } + + @ExceptionHandler(UnauthorizedAccessException.class) + public ResponseEntity> handleUnauthorizedAccessException(UnauthorizedAccessException exception) { + LOGGER.error(exception.getMessage(), exception); + return ResponseEntity.status(HttpStatus.FORBIDDEN) + .body(Map.of(name, exception.getLocalizedMessage())); + } + + @ExceptionHandler(ValidationException.class) + public ResponseEntity> handleValidationException(ValidationException exception) { + LOGGER.error(exception.getMessage(), exception); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(Map.of(name, exception.getLocalizedMessage())); + } + + @ExceptionHandler(EntityNotFoundException.class) + public ResponseEntity> handleEntityNotFoundException(EntityNotFoundException exception) { + LOGGER.error(exception.getMessage(), exception); + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .body(Map.of(name, exception.getLocalizedMessage())); + } + + @ExceptionHandler(CompletionException.class) + public ResponseEntity> handleCompletionException(CompletionException exception) { + LOGGER.error(exception.getMessage(), exception); + Throwable cause = exception.getCause(); + return switch (cause) { + case AccountException accountException -> handleAccountException(accountException); + case EntityNotFoundException entityNotFoundException -> + handleEntityNotFoundException(entityNotFoundException); + case CredentialException credentialException -> handleCredentialException(credentialException); + case IllegalArgumentException illegalArgumentException -> + handleIllegalArgumentException(illegalArgumentException); + case UnauthorizedAccessException unauthorizedAccessException -> + handleUnauthorizedAccessException(unauthorizedAccessException); + case ValidationException validationException -> handleValidationException(validationException); + case null, default -> ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(Map.of(name, exception.getLocalizedMessage())); + }; + } +} diff --git a/framework/src/main/java/io/theurl/framework/configure/MediatorConfiguration.java b/framework/src/main/java/io/theurl/framework/configure/MediatorConfiguration.java new file mode 100644 index 0000000..421a0d6 --- /dev/null +++ b/framework/src/main/java/io/theurl/framework/configure/MediatorConfiguration.java @@ -0,0 +1,50 @@ +package io.theurl.framework.configure; + +import com.neroyun.mediator.*; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.TaskDecorator; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; + +@SuppressWarnings("rawtypes") +@Configuration +public class MediatorConfiguration { + @Bean + public Mediator mediator(ObjectProvider handlers, ObjectProvider middlewares, ObjectProvider validators) { + return new PipelinedMediator() + .use(() -> handlers.stream()) + .use(() -> middlewares.stream()) + .use(() -> validators.stream()); + } + + @Bean(name = "taskExecutor") + public ThreadPoolTaskExecutor taskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setThreadNamePrefix("mediator-"); + executor.setCorePoolSize(8); + executor.setMaxPoolSize(32); + executor.setQueueCapacity(200); + executor.setTaskDecorator(copyRequestContextDecorator()); + executor.initialize(); + return executor; + } + + private TaskDecorator copyRequestContextDecorator() { + return runnable -> { + RequestAttributes context = RequestContextHolder.getRequestAttributes(); + return () -> { + try { + if (context != null) { + RequestContextHolder.setRequestAttributes(context); + } + runnable.run(); + } finally { + RequestContextHolder.resetRequestAttributes(); + } + }; + }; + } +} diff --git a/framework/src/main/java/io/theurl/framework/configure/RestTemplateConfig.java b/framework/src/main/java/io/theurl/framework/configure/RestTemplateConfig.java new file mode 100644 index 0000000..3204553 --- /dev/null +++ b/framework/src/main/java/io/theurl/framework/configure/RestTemplateConfig.java @@ -0,0 +1,30 @@ +package io.theurl.framework.configure; + +//import org.apache.hc.client5.http.config.RequestConfig; +//import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +//import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; +//import org.apache.hc.core5.util.Timeout; +//import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; + +//@Configuration +//public class RestTemplateConfig { +// @ConditionalOnMissingBean(RestTemplate.class) +// @Bean +// public RestTemplate restTemplate() { +// RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory()); +// return restTemplate; +// } +// +// private ClientHttpRequestFactory getClientHttpRequestFactory() { +// int timeout = 5000; +// RequestConfig config = RequestConfig.custom() +// .setConnectionRequestTimeout(Timeout.of(timeout, TimeUnit.MILLISECONDS)) +// //.setSocketTimeout(timeout) +// .build(); +// CloseableHttpClient client = HttpClientBuilder +// .create() +// .setDefaultRequestConfig(config) +// .build(); +// return new HttpComponentsClientHttpRequestFactory(client); +// } +//} diff --git a/framework/src/main/java/io/theurl/framework/utility/GuidGenerator.java b/framework/src/main/java/io/theurl/framework/utility/GuidGenerator.java new file mode 100644 index 0000000..4c1dd4c --- /dev/null +++ b/framework/src/main/java/io/theurl/framework/utility/GuidGenerator.java @@ -0,0 +1,4 @@ +package io.theurl.framework.utility; + +public class GuidGenerator { +} diff --git a/pom.xml b/pom.xml index 711740f..f5e24e6 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,7 @@ 4.0.6 true 1.1.1 + 3.2.0 @@ -96,6 +97,20 @@ pom import + + jakarta.persistence + jakarta.persistence-api + ${jakarta-persistence.version} + pom + import + + + org.slf4j + slf4j-api + 2.0.7 + pom + import + From 771dec7b25dd34fb721e679696e00dae48f328ab Mon Sep 17 00:00:00 2001 From: damon Date: Sun, 24 May 2026 00:50:15 +0800 Subject: [PATCH 5/7] Add handler for AggregateException in GlobalExceptionHandler to improve error reporting --- .../configure/GlobalExceptionHandler.java | 22 ++++++++++++++++--- .../src/main/resources/logback-spring.xml | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/framework/src/main/java/io/theurl/framework/configure/GlobalExceptionHandler.java b/framework/src/main/java/io/theurl/framework/configure/GlobalExceptionHandler.java index 52b835c..95a3525 100644 --- a/framework/src/main/java/io/theurl/framework/configure/GlobalExceptionHandler.java +++ b/framework/src/main/java/io/theurl/framework/configure/GlobalExceptionHandler.java @@ -1,5 +1,6 @@ package io.theurl.framework.configure; +import com.neroyun.mediator.internal.AggregateException; import com.neroyun.mediator.validation.ValidationException; import io.theurl.framework.security.AccountException; import io.theurl.framework.security.CredentialException; @@ -63,11 +64,26 @@ public ResponseEntity> handleEntityNotFoundException(EntityN .body(Map.of(name, exception.getLocalizedMessage())); } + @ExceptionHandler(AggregateException.class) + public ResponseEntity> handleAggregateException(AggregateException exception) { + LOGGER.error(exception.getMessage(), exception); + Throwable cause = exception.getExceptions().getFirst(); + LOGGER.debug("AggregateException contains {} exceptions, handling the first one: {}", exception.getExceptions().size(), cause.getMessage()); + return handleGeneralException(cause); + } + @ExceptionHandler(CompletionException.class) public ResponseEntity> handleCompletionException(CompletionException exception) { LOGGER.error(exception.getMessage(), exception); Throwable cause = exception.getCause(); - return switch (cause) { + return handleGeneralException(cause); + } + + private ResponseEntity> handleGeneralException(Throwable exception) { + if (exception == null) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + return switch (exception) { case AccountException accountException -> handleAccountException(accountException); case EntityNotFoundException entityNotFoundException -> handleEntityNotFoundException(entityNotFoundException); @@ -77,8 +93,8 @@ public ResponseEntity> handleCompletionException(CompletionE case UnauthorizedAccessException unauthorizedAccessException -> handleUnauthorizedAccessException(unauthorizedAccessException); case ValidationException validationException -> handleValidationException(validationException); - case null, default -> ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body(Map.of(name, exception.getLocalizedMessage())); + default -> ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(Map.of(name, exception.getLocalizedMessage())); }; } } diff --git a/identity/src/main/resources/logback-spring.xml b/identity/src/main/resources/logback-spring.xml index 2781bc8..4e7ae83 100644 --- a/identity/src/main/resources/logback-spring.xml +++ b/identity/src/main/resources/logback-spring.xml @@ -98,7 +98,7 @@ - + From 031e3e027ae213f9b04b478bc837226b75d3cad9 Mon Sep 17 00:00:00 2001 From: damon Date: Sun, 24 May 2026 00:50:43 +0800 Subject: [PATCH 6/7] Add component scans for framework and identity packages in IdentityApplication --- .../main/java/io/theurl/identity/IdentityApplication.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/identity/src/main/java/io/theurl/identity/IdentityApplication.java b/identity/src/main/java/io/theurl/identity/IdentityApplication.java index 6d50fa5..188d4b4 100644 --- a/identity/src/main/java/io/theurl/identity/IdentityApplication.java +++ b/identity/src/main/java/io/theurl/identity/IdentityApplication.java @@ -2,10 +2,16 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.ComponentScans; import org.springframework.scheduling.annotation.EnableAsync; @EnableAsync @SpringBootApplication +@ComponentScans({ + @ComponentScan("io.theurl.framework"), + @ComponentScan("io.theurl.identity") +}) public class IdentityApplication { public static void main(String[] args) { From 1b506cad5134d40cb60f207917f6c1da2cc65947 Mon Sep 17 00:00:00 2001 From: damon Date: Sun, 24 May 2026 00:51:12 +0800 Subject: [PATCH 7/7] Refactor external authentication providers to support asynchronous authentication --- .../external/ExternalAuthProvider.java | 4 +- .../external/FacebookAuthProvider.java | 55 +++++++------ .../identity/external/GithubAuthProvider.java | 69 ++++++++-------- .../identity/external/GoogleAuthProvider.java | 79 ++++++++++--------- .../external/MicrosoftAuthProvider.java | 79 ++++++++++--------- 5 files changed, 150 insertions(+), 136 deletions(-) diff --git a/identity/src/main/java/io/theurl/identity/external/ExternalAuthProvider.java b/identity/src/main/java/io/theurl/identity/external/ExternalAuthProvider.java index 742df87..9a0e329 100644 --- a/identity/src/main/java/io/theurl/identity/external/ExternalAuthProvider.java +++ b/identity/src/main/java/io/theurl/identity/external/ExternalAuthProvider.java @@ -1,5 +1,7 @@ package io.theurl.identity.external; +import java.util.concurrent.CompletableFuture; + /** * Defines the contract for external authentication providers. * Implementations of this interface should handle the authentication process with external services. @@ -10,5 +12,5 @@ public interface ExternalAuthProvider { * @param authCode The authentication code received from the external service. * @return The result of the authentication process encapsulated in an ExternalAuthResult object. */ - ExternalAuthResult authenticate(String authCode); + CompletableFuture authenticateAsync(String authCode); } diff --git a/identity/src/main/java/io/theurl/identity/external/FacebookAuthProvider.java b/identity/src/main/java/io/theurl/identity/external/FacebookAuthProvider.java index 1f79a91..874b6a7 100644 --- a/identity/src/main/java/io/theurl/identity/external/FacebookAuthProvider.java +++ b/identity/src/main/java/io/theurl/identity/external/FacebookAuthProvider.java @@ -4,6 +4,7 @@ import org.springframework.stereotype.Component; import java.util.Map; +import java.util.concurrent.CompletableFuture; @Component("external-auth-provider-facebook") public class FacebookAuthProvider extends BaseAuthProvider { @@ -16,31 +17,33 @@ public class FacebookAuthProvider extends BaseAuthProvider { private String redirectUri; @Override - public ExternalAuthResult authenticate(String authCode) { - var token = getToken("https://graph.facebook.com//v22.0/oauth/access_token", - Map.of( - "client_id", clientId, - "client_secret", clientSecret, - "code", authCode, - "redirect_uri", redirectUri - ), "query"); - - var userInfo = getUserInfo(token, "https://graph.facebook.com/v12.0/me?fields=id,name,email,picture"); - return new ExternalAuthResult() { - @Override - public String getId() { - return userInfo.get("id").asText(); - } - - @Override - public String getEmail() { - return userInfo.get("email").asText(); - } - - @Override - public String getNickname() { - return userInfo.get("name").asText(); - } - }; + public CompletableFuture authenticateAsync(String authCode) { + return CompletableFuture.supplyAsync(() -> { + var token = getToken("https://graph.facebook.com//v22.0/oauth/access_token", + Map.of( + "client_id", clientId, + "client_secret", clientSecret, + "code", authCode, + "redirect_uri", redirectUri + ), "query"); + + var userInfo = getUserInfo(token, "https://graph.facebook.com/v12.0/me?fields=id,name,email,picture"); + return new ExternalAuthResult() { + @Override + public String getId() { + return userInfo.get("id").asText(); + } + + @Override + public String getEmail() { + return userInfo.get("email").asText(); + } + + @Override + public String getNickname() { + return userInfo.get("name").asText(); + } + }; + }); } } diff --git a/identity/src/main/java/io/theurl/identity/external/GithubAuthProvider.java b/identity/src/main/java/io/theurl/identity/external/GithubAuthProvider.java index a8e62a9..2fca186 100644 --- a/identity/src/main/java/io/theurl/identity/external/GithubAuthProvider.java +++ b/identity/src/main/java/io/theurl/identity/external/GithubAuthProvider.java @@ -4,6 +4,7 @@ import org.springframework.stereotype.Component; import java.util.Map; +import java.util.concurrent.CompletableFuture; @Component("external-auth-provider-github") public class GithubAuthProvider extends BaseAuthProvider { @@ -14,38 +15,40 @@ public class GithubAuthProvider extends BaseAuthProvider { private String secret; @Override - public ExternalAuthResult authenticate(String authCode) { - var token = getToken("https://github.com/login/oauth/access_token", Map.of( - "client_id", clientId, - "client_secret", secret, - "code", authCode - ), "query"); - var user = getUserInfo(token, "https://api.github.com/user"); - return new ExternalAuthResult() { - @Override - public String getId() { - return user.findValue("id").asText(); - } - - @Override - public String getUsername() { - return user.findValue("login").asText(); - } - - @Override - public String getNickname() { - return user.findValue("name").asText(); - } - - @Override - public String getEmail() { - return user.findValue("email").asText(); - } - - @Override - public String getAvatarUrl() { - return user.findValue("avatar_url").asText(); - } - }; + public CompletableFuture authenticateAsync(String authCode) { + return CompletableFuture.supplyAsync(() -> { + var token = getToken("https://github.com/login/oauth/access_token", Map.of( + "client_id", clientId, + "client_secret", secret, + "code", authCode + ), "query"); + var user = getUserInfo(token, "https://api.github.com/user"); + return new ExternalAuthResult() { + @Override + public String getId() { + return user.findValue("id").asText(); + } + + @Override + public String getUsername() { + return user.findValue("login").asText(); + } + + @Override + public String getNickname() { + return user.findValue("name").asText(); + } + + @Override + public String getEmail() { + return user.findValue("email").asText(); + } + + @Override + public String getAvatarUrl() { + return user.findValue("avatar_url").asText(); + } + }; + }); } } diff --git a/identity/src/main/java/io/theurl/identity/external/GoogleAuthProvider.java b/identity/src/main/java/io/theurl/identity/external/GoogleAuthProvider.java index 917de7b..232dfec 100644 --- a/identity/src/main/java/io/theurl/identity/external/GoogleAuthProvider.java +++ b/identity/src/main/java/io/theurl/identity/external/GoogleAuthProvider.java @@ -4,6 +4,7 @@ import org.springframework.stereotype.Component; import java.util.Map; +import java.util.concurrent.CompletableFuture; @Component("external-auth-provider-google") public class GoogleAuthProvider extends BaseAuthProvider { @@ -16,43 +17,45 @@ public class GoogleAuthProvider extends BaseAuthProvider { private String redirectUri; @Override - public ExternalAuthResult authenticate(String authCode) { - var tokenParams = Map.of( - "client_id", clientId, - "client_secret", clientSecret, - "redirect_uri", redirectUri, - "code", authCode, - "grant_type", "authorization_code"); - - var token = getToken("https://oauth2.googleapis.com/token", tokenParams, "form"); - - var user = getUserInfo(token, "https://www.googleapis.com/oauth2/v3/userinfo"); - - return new ExternalAuthResult() { - @Override - public String getId() { - return user.findValue("sub").asText(); - } - - @Override - public String getUsername() { - return user.findValue("email").asText(); - } - - @Override - public String getNickname() { - return user.findValue("name").asText(); - } - - @Override - public String getEmail() { - return user.findValue("email").asText(); - } - - @Override - public String getAvatarUrl() { - return user.findValue("picture").asText(); - } - }; + public CompletableFuture authenticateAsync(String authCode) { + return CompletableFuture.supplyAsync(() -> { + var tokenParams = Map.of( + "client_id", clientId, + "client_secret", clientSecret, + "redirect_uri", redirectUri, + "code", authCode, + "grant_type", "authorization_code"); + + var token = getToken("https://oauth2.googleapis.com/token", tokenParams, "form"); + + var user = getUserInfo(token, "https://www.googleapis.com/oauth2/v3/userinfo"); + + return new ExternalAuthResult() { + @Override + public String getId() { + return user.findValue("sub").asText(); + } + + @Override + public String getUsername() { + return user.findValue("email").asText(); + } + + @Override + public String getNickname() { + return user.findValue("name").asText(); + } + + @Override + public String getEmail() { + return user.findValue("email").asText(); + } + + @Override + public String getAvatarUrl() { + return user.findValue("picture").asText(); + } + }; + }); } } diff --git a/identity/src/main/java/io/theurl/identity/external/MicrosoftAuthProvider.java b/identity/src/main/java/io/theurl/identity/external/MicrosoftAuthProvider.java index b5ce883..d83c55c 100644 --- a/identity/src/main/java/io/theurl/identity/external/MicrosoftAuthProvider.java +++ b/identity/src/main/java/io/theurl/identity/external/MicrosoftAuthProvider.java @@ -4,6 +4,7 @@ import org.springframework.stereotype.Component; import java.util.Map; +import java.util.concurrent.CompletableFuture; @Component("external-auth-provider-microsoft") public class MicrosoftAuthProvider extends BaseAuthProvider { @@ -15,43 +16,45 @@ public class MicrosoftAuthProvider extends BaseAuthProvider { private String redirectUri; @Override - public ExternalAuthResult authenticate(String authCode) { - Map getTokenParams = Map.of( - "client_id", clientId, - "client_secret", clientSecret, - "code", authCode, - "redirect_uri", redirectUri, - "grant_type", "authorization_code", - "scope", "User.Read Mail.Read" - ); - - var token = getToken("https://login.microsoftonline.com/consumers/oauth2/v2.0/token", getTokenParams, "form"); - var user = getUserInfo(token, "https://graph.microsoft.com/v1.0/me"); - return new ExternalAuthResult() { - @Override - public String getId() { - return user.findValue("id").asText(); - } - - @Override - public String getUsername() { - return user.findValue("userPrincipalName").asText(); - } - - @Override - public String getNickname() { - return user.findValue("displayName").asText(); - } - - @Override - public String getEmail() { - return user.findValue("email").asText(); - } - - @Override - public String getPhone() { - return user.findValue("mobilePhone").asText(); - } - }; + public CompletableFuture authenticateAsync(String authCode) { + return CompletableFuture.supplyAsync(() -> { + Map getTokenParams = Map.of( + "client_id", clientId, + "client_secret", clientSecret, + "code", authCode, + "redirect_uri", redirectUri, + "grant_type", "authorization_code", + "scope", "User.Read Mail.Read" + ); + + var token = getToken("https://login.microsoftonline.com/consumers/oauth2/v2.0/token", getTokenParams, "form"); + var user = getUserInfo(token, "https://graph.microsoft.com/v1.0/me"); + return new ExternalAuthResult() { + @Override + public String getId() { + return user.findValue("id").asText(); + } + + @Override + public String getUsername() { + return user.findValue("userPrincipalName").asText(); + } + + @Override + public String getNickname() { + return user.findValue("displayName").asText(); + } + + @Override + public String getEmail() { + return user.findValue("email").asText(); + } + + @Override + public String getPhone() { + return user.findValue("mobilePhone").asText(); + } + }; + }); } }