From 0f7a4f392e6037c9190f83424fd3312afa1ba781 Mon Sep 17 00:00:00 2001 From: HowieHz Date: Sun, 8 Feb 2026 06:04:21 +0800 Subject: [PATCH 1/8] feat: Add LINUX DO provider and proxy improvements Co-Authored-By: HowieHz <94725606+HowieHz@users.noreply.github.com> --- README.md | 21 ++-- .../HaloOAuth2AuthenticationWebFilter.java | 104 ++++++++++++++---- .../resources/extensions/auth-provider.yaml | 22 ++++ .../extensions/client-registrations.yaml | 20 ++++ src/main/resources/static/linuxdo.svg | 1 + 5 files changed, 139 insertions(+), 29 deletions(-) create mode 100644 src/main/resources/static/linuxdo.svg diff --git a/README.md b/README.md index 716205f..6182e34 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # plugin-oauth2 -Halo 2.0 的 OAuth2 第三方登录插件。 +Halo 2.0 的 OAuth2/OIDC 第三方登录插件。 ## 使用方法 @@ -14,11 +14,12 @@ Halo 2.0 的 OAuth2 第三方登录插件。 目前支持的认证方式: -| 服务商 | 文档 | Halo 所需配置 | Scope | 回调地址 | -| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------- | ------------ | ------------------------------------- | -| GitHub | [https://docs.github.com](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) | `Client ID` `Client Secret` | 无需手动设置 | `/login/oauth2/code/github` | -| GitLab | [https://docs.gitlab.com](https://docs.gitlab.com/ee/integration/oauth_provider.html#configure-gitlab-as-an-oauth-20-authentication-identity-provider) | `Client ID` `Client Secret` | `read_user` | `/login/oauth2/code/gitlab` | -| Gitee | | `Client ID` `Client Secret` | `user_info` | `/login/oauth2/code/gitee` | +| 服务商 | 文档 | Halo 所需配置 | Scope | 回调地址 | +| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------- | ---------------- | -------------------------------------- | +| GitHub | [https://docs.github.com](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) | `Client ID` `Client Secret` | 无需手动设置 | `/login/oauth2/code/github` | +| GitLab | [https://docs.gitlab.com](https://docs.gitlab.com/ee/integration/oauth_provider.html#configure-gitlab-as-an-oauth-20-authentication-identity-provider) | `Client ID` `Client Secret` | `read_user` | `/login/oauth2/code/gitlab` | +| Gitee | [https://gitee.com](https://gitee.com/api/v5/oauth_doc#/) | `Client ID` `Client Secret` | `user_info` | `/login/oauth2/code/gitee` | +| LINUX DO | [https://wiki.linux.do](https://wiki.linux.do/Community/LinuxDoConnect) | `Client ID` `Client Secret` | 无需手动设置 | `/login/oauth2/code/linuxdo` | 注意事项: @@ -28,7 +29,13 @@ Halo 2.0 的 OAuth2 第三方登录插件。 ## 代理配置(可选) -如果你部署的 Halo 服务器无法直接访问 GitHub、GitLab 或 Gitee 的 API,你可以配置代理。 +如果你部署的 Halo 服务器无法直接访问 GitHub、GitLab、Gitee 或 LINUX DO 的 API,你可以配置代理。 + +代理配置将应用于所有 OAuth2/OIDC 请求,包括: +- Token 交换请求 +- 用户信息请求 +- OIDC JWKS (JSON Web Key Set) 获取 +- OIDC Issuer 发现 配置路径示例:`${Halo 工作目录}/plugins/configs/plugin-oauth2.yaml`。配置示例如下所示: diff --git a/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java b/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java index 2e37506..2906f61 100644 --- a/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java +++ b/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java @@ -13,10 +13,17 @@ import org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeReactiveAuthenticationManager; import org.springframework.security.oauth2.client.oidc.authentication.ReactiveOidcIdTokenDecoderFactory; import org.springframework.security.oauth2.client.oidc.userinfo.OidcReactiveOAuth2UserService; +import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.userinfo.DefaultReactiveOAuth2UserService; import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationCodeAuthenticationTokenConverter; import org.springframework.security.oauth2.client.web.server.authentication.OAuth2LoginAuthenticationWebFilter; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; +import org.springframework.security.oauth2.jwt.MappedJwtClaimSetConverter; +import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder; +import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder; +import org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory; import org.springframework.security.web.server.WebFilterExchange; import org.springframework.security.web.server.authentication.RedirectServerAuthenticationFailureHandler; import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler; @@ -74,34 +81,23 @@ public HaloOAuth2AuthenticationWebFilter(Oauth2LoginConfiguration configuration, var accessTokenResponseClient = new WebClientReactiveAuthorizationCodeTokenResponseClient(); accessTokenResponseClient.setWebClient(webClient); + var oauth2UserService = new DefaultReactiveOAuth2UserService(); + oauth2UserService.setWebClient(webClient); + var oauth2AuthManager = new OAuth2LoginReactiveAuthenticationManager( accessTokenResponseClient, - new DefaultReactiveOAuth2UserService() + oauth2UserService ); + + var oidcUserService = new OidcReactiveOAuth2UserService(); + oidcUserService.setOauth2UserService(oauth2UserService); + var oidcAuthManager = new OidcAuthorizationCodeReactiveAuthenticationManager( accessTokenResponseClient, - new OidcReactiveOAuth2UserService() + oidcUserService ); - var oidcIdTokenDecodeFactory = new ReactiveOidcIdTokenDecoderFactory(); - oidcIdTokenDecodeFactory.setJwsAlgorithmResolver(clientRegistration -> { - var configurationMetadata = clientRegistration.getProviderDetails() - .getConfigurationMetadata(); - try { - var supportedJwsAlgorithms = JSONObjectUtils.getStringList( - new JSONObject(configurationMetadata), - "id_token_signing_alg_values_supported" - ); - // we choose the first one as JWS algorithm - if (!supportedJwsAlgorithms.isEmpty()) { - var jwsAlgorithm = supportedJwsAlgorithms.get(0); - return SignatureAlgorithm.from(jwsAlgorithm); - } - } catch (ParseException e) { - // ignore the error. - } - // default algorithm - return SignatureAlgorithm.RS256; - }); + // Create custom OIDC ID token decoder factory with proxy-enabled WebClient + var oidcIdTokenDecodeFactory = createOidcIdTokenDecoderFactory(webClient); oidcAuthManager.setJwtDecoderFactory(oidcIdTokenDecodeFactory); var authManager = new DelegatingReactiveAuthenticationManager(oauth2AuthManager, oidcAuthManager); @@ -136,4 +132,68 @@ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { return delegate.filter(exchange, chain); } + /** + * Creates a custom OIDC ID token decoder factory that uses the provided WebClient + * for JWKS/issuer discovery, ensuring proxy configuration is applied. + */ + private ReactiveJwtDecoderFactory createOidcIdTokenDecoderFactory( + WebClient webClient) { + + return new ReactiveJwtDecoderFactory() { + @Override + public ReactiveJwtDecoder createDecoder(ClientRegistration clientRegistration) { + // Determine the JWS algorithm from provider metadata + SignatureAlgorithm jwsAlgorithm = resolveJwsAlgorithm(clientRegistration); + + String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri(); + if (jwkSetUri == null) { + OAuth2Error oauth2Error = new OAuth2Error( + "missing_signature_verifier", + "Failed to find a Signature Verifier for Client Registration: '" + + clientRegistration.getRegistrationId() + + "'. Check to ensure you have configured the JWK Set URI.", + null + ); + throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); + } + + // Build decoder with custom WebClient for JWKS retrieval + NimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder + .withJwkSetUri(jwkSetUri) + .jwsAlgorithm(jwsAlgorithm) + .webClient(webClient) + .build(); + + // Apply default OIDC claim type converters + decoder.setClaimSetConverter( + MappedJwtClaimSetConverter.withDefaults( + ReactiveOidcIdTokenDecoderFactory.createDefaultClaimTypeConverters() + ) + ); + + return decoder; + } + + private SignatureAlgorithm resolveJwsAlgorithm(ClientRegistration clientRegistration) { + var configurationMetadata = clientRegistration.getProviderDetails() + .getConfigurationMetadata(); + try { + var supportedJwsAlgorithms = JSONObjectUtils.getStringList( + new JSONObject(configurationMetadata), + "id_token_signing_alg_values_supported" + ); + // we choose the first one as JWS algorithm + if (!supportedJwsAlgorithms.isEmpty()) { + var jwsAlgorithm = supportedJwsAlgorithms.get(0); + return SignatureAlgorithm.from(jwsAlgorithm); + } + } catch (ParseException e) { + // Ignore the error if metadata is missing or malformed and fall back to default RS256 algorithm + } + // default algorithm + return SignatureAlgorithm.RS256; + } + }; + } + } diff --git a/src/main/resources/extensions/auth-provider.yaml b/src/main/resources/extensions/auth-provider.yaml index 17403b0..fc0ed1d 100644 --- a/src/main/resources/extensions/auth-provider.yaml +++ b/src/main/resources/extensions/auth-provider.yaml @@ -64,6 +64,28 @@ spec: --- apiVersion: auth.halo.run/v1alpha1 kind: AuthProvider +metadata: + name: linuxdo + labels: + auth.halo.run/auth-binding: "true" +spec: + displayName: LINUX DO + description: LINUX DO Connect OAuth2 provider for sharing forum user information. + logo: /plugins/plugin-oauth2/assets/static/linuxdo.svg + website: https://linux.do + helpPage: https://wiki.linux.do/Community/LinuxDoConnect + authenticationUrl: /oauth2/authorization/linuxdo + bindingUrl: /oauth2/authorization/linuxdo + unbindUrl: /apis/uc.api.auth.halo.run/v1alpha1/user-connections/linuxdo/disconnect + authType: oauth2 + settingRef: + name: generic-oauth2-setting + group: genericOauth + configMapRef: + name: oauth2-linuxdo-config +--- +apiVersion: auth.halo.run/v1alpha1 +kind: AuthProvider metadata: name: sso labels: diff --git a/src/main/resources/extensions/client-registrations.yaml b/src/main/resources/extensions/client-registrations.yaml index 12b8bf5..2c24013 100644 --- a/src/main/resources/extensions/client-registrations.yaml +++ b/src/main/resources/extensions/client-registrations.yaml @@ -51,6 +51,26 @@ spec: --- apiVersion: oauth.halo.run/v1alpha1 kind: Oauth2ClientRegistration +metadata: + name: linuxdo +spec: + clientAuthenticationMethod: "client_secret_basic" + authorizationGrantType: "authorization_code" + redirectUri: "{baseUrl}/login/oauth2/code/linuxdo" + scopes: + - "openid" + - "profile" + authorizationUri: "https://connect.linux.do/oauth2/authorize" + tokenUri: "https://connect.linux.do/oauth2/token" + userInfoUri: "https://connect.linux.do/api/user" + userInfoAuthenticationMethod: "header" + userNameAttributeName: "sub" + issuerUri: "https://connect.linux.do/" + jwkSetUri: "https://connect.linux.do/.well-known/jwks.json" + clientName: "LINUX DO" +--- +apiVersion: oauth.halo.run/v1alpha1 +kind: Oauth2ClientRegistration metadata: name: sso spec: diff --git a/src/main/resources/static/linuxdo.svg b/src/main/resources/static/linuxdo.svg new file mode 100644 index 0000000..de551ee --- /dev/null +++ b/src/main/resources/static/linuxdo.svg @@ -0,0 +1 @@ + \ No newline at end of file From a2b06fbd50d60947ee97a6136687afe6b9fc7a55 Mon Sep 17 00:00:00 2001 From: HowieHz Date: Sun, 8 Feb 2026 22:01:34 +0800 Subject: [PATCH 2/8] feat: Add Google OAuth2 provider and client registration --- README.md | 1 + .../resources/extensions/auth-provider.yaml | 26 +++++++++++++++++-- .../extensions/client-registrations.yaml | 20 ++++++++++++++ src/main/resources/static/google.svg | 1 + 4 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 src/main/resources/static/google.svg diff --git a/README.md b/README.md index 6182e34..61d5164 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Halo 2.0 的 OAuth2/OIDC 第三方登录插件。 | 服务商 | 文档 | Halo 所需配置 | Scope | 回调地址 | | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------- | ---------------- | -------------------------------------- | | GitHub | [https://docs.github.com](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) | `Client ID` `Client Secret` | 无需手动设置 | `/login/oauth2/code/github` | +| Google | [https://developers.google.com](https://developers.google.com/identity/openid-connect/openid-connect) | `Client ID` `Client Secret` | 无需手动设置 | `/login/oauth2/code/google` | | GitLab | [https://docs.gitlab.com](https://docs.gitlab.com/ee/integration/oauth_provider.html#configure-gitlab-as-an-oauth-20-authentication-identity-provider) | `Client ID` `Client Secret` | `read_user` | `/login/oauth2/code/gitlab` | | Gitee | [https://gitee.com](https://gitee.com/api/v5/oauth_doc#/) | `Client ID` `Client Secret` | `user_info` | `/login/oauth2/code/gitee` | | LINUX DO | [https://wiki.linux.do](https://wiki.linux.do/Community/LinuxDoConnect) | `Client ID` `Client Secret` | 无需手动设置 | `/login/oauth2/code/linuxdo` | diff --git a/src/main/resources/extensions/auth-provider.yaml b/src/main/resources/extensions/auth-provider.yaml index fc0ed1d..8edbf1d 100644 --- a/src/main/resources/extensions/auth-provider.yaml +++ b/src/main/resources/extensions/auth-provider.yaml @@ -22,6 +22,28 @@ spec: --- apiVersion: auth.halo.run/v1alpha1 kind: AuthProvider +metadata: + name: google + labels: + auth.halo.run/auth-binding: "true" +spec: + displayName: Google + description: Google LLC is an American multinational technology company that provides Google Sign-In via OAuth 2.0 / OpenID Connect. + logo: /plugins/plugin-oauth2/assets/static/google.svg + website: https://accounts.google.com + helpPage: https://developers.google.com/identity/protocols/oauth2/openid-connect + authenticationUrl: /oauth2/authorization/google + bindingUrl: /oauth2/authorization/google + unbindUrl: /apis/uc.api.auth.halo.run/v1alpha1/user-connections/google/disconnect + authType: oauth2 + settingRef: + name: generic-oauth2-setting + group: genericOauth + configMapRef: + name: oauth2-google-config +--- +apiVersion: auth.halo.run/v1alpha1 +kind: AuthProvider metadata: name: gitee labels: @@ -70,7 +92,7 @@ metadata: auth.halo.run/auth-binding: "true" spec: displayName: LINUX DO - description: LINUX DO Connect OAuth2 provider for sharing forum user information. + description: LINUX DO is a community forum that provides LINUX DO Connect for sharing forum user information via OAuth 2.0 / OpenID Connect. logo: /plugins/plugin-oauth2/assets/static/linuxdo.svg website: https://linux.do helpPage: https://wiki.linux.do/Community/LinuxDoConnect @@ -102,4 +124,4 @@ spec: name: sso-oauth2-setting group: ssoOauth configMapRef: - name: oauth2-sso-config \ No newline at end of file + name: oauth2-sso-config diff --git a/src/main/resources/extensions/client-registrations.yaml b/src/main/resources/extensions/client-registrations.yaml index 2c24013..3949982 100644 --- a/src/main/resources/extensions/client-registrations.yaml +++ b/src/main/resources/extensions/client-registrations.yaml @@ -17,6 +17,26 @@ spec: --- apiVersion: oauth.halo.run/v1alpha1 kind: Oauth2ClientRegistration +metadata: + name: google +spec: + clientAuthenticationMethod: "client_secret_basic" + authorizationGrantType: "authorization_code" + redirectUri: "{baseUrl}/login/oauth2/code/google" + scopes: + - "openid" + - "profile" + authorizationUri: "https://accounts.google.com/o/oauth2/v2/auth" + tokenUri: "https://oauth2.googleapis.com/token" + userInfoUri: "https://openidconnect.googleapis.com/v1/userinfo" + userInfoAuthenticationMethod: "header" + userNameAttributeName: "sub" + issuerUri: "https://accounts.google.com" + jwkSetUri: "https://www.googleapis.com/oauth2/v3/certs" + clientName: "Google" +--- +apiVersion: oauth.halo.run/v1alpha1 +kind: Oauth2ClientRegistration metadata: name: gitee spec: diff --git a/src/main/resources/static/google.svg b/src/main/resources/static/google.svg new file mode 100644 index 0000000..58280b2 --- /dev/null +++ b/src/main/resources/static/google.svg @@ -0,0 +1 @@ + \ No newline at end of file From 8cf8addc70d52c1ea50b595a703b4751cac263f2 Mon Sep 17 00:00:00 2001 From: Howie Xie <94725606+HowieHz@users.noreply.github.com> Date: Wed, 11 Feb 2026 16:10:02 +0800 Subject: [PATCH 3/8] docs: add SSO provider and reformat auth table (#3) --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 716205f..2d562ea 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,12 @@ Halo 2.0 的 OAuth2 第三方登录插件。 目前支持的认证方式: -| 服务商 | 文档 | Halo 所需配置 | Scope | 回调地址 | -| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------- | ------------ | ------------------------------------- | -| GitHub | [https://docs.github.com](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) | `Client ID` `Client Secret` | 无需手动设置 | `/login/oauth2/code/github` | -| GitLab | [https://docs.gitlab.com](https://docs.gitlab.com/ee/integration/oauth_provider.html#configure-gitlab-as-an-oauth-20-authentication-identity-provider) | `Client ID` `Client Secret` | `read_user` | `/login/oauth2/code/gitlab` | -| Gitee | | `Client ID` `Client Secret` | `user_info` | `/login/oauth2/code/gitee` | +| 服务商 | 文档 | Halo 所需配置 | Scope | 回调地址 | +| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- | ---------------- | ------------------------------------- | +| GitHub | [https://docs.github.com](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) | `Client ID` `Client Secret` | 无需手动设置 | `/login/oauth2/code/github` | +| GitLab | [https://docs.gitlab.com](https://docs.gitlab.com/ee/integration/oauth_provider.html#configure-gitlab-as-an-oauth-20-authentication-identity-provider) | `Client ID` `Client Secret` | `read_user` | `/login/oauth2/code/gitlab` | +| Gitee | | `Client ID` `Client Secret` | `user_info` | `/login/oauth2/code/gitee` | +| SSO(任意 OAuth2 / OpenID Connect 提供商) | 通用 OAuth2 / OpenID Connect(参见 [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749) / OIDC 文档) | `Client ID` `Client Secret` `Authorization URI` `Token URI` `UserInfo Url` `Scopes` `UserName Attribute` `` | 按提供商要求设置 | `/login/oauth2/code/sso` | 注意事项: From e37c7e534a0f06deef3ac7c0d8517bf984863f1f Mon Sep 17 00:00:00 2001 From: HowieHz Date: Wed, 11 Feb 2026 16:14:16 +0800 Subject: [PATCH 4/8] style: Reformat OAuth providers table in README --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index bb065e5..40ba205 100644 --- a/README.md +++ b/README.md @@ -14,14 +14,14 @@ Halo 2.0 的 OAuth2/OIDC 第三方登录插件。 目前支持的认证方式: -| 服务商 | 文档 | Halo 所需配置 | Scope | 回调地址 | -| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------- | ---------------- | -------------------------------------- | -| GitHub | [https://docs.github.com](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) | `Client ID` `Client Secret` | 无需手动设置 | `/login/oauth2/code/github` | -| Google | [https://developers.google.com](https://developers.google.com/identity/openid-connect/openid-connect) | `Client ID` `Client Secret` | 无需手动设置 | `/login/oauth2/code/google` | -| GitLab | [https://docs.gitlab.com](https://docs.gitlab.com/ee/integration/oauth_provider.html#configure-gitlab-as-an-oauth-20-authentication-identity-provider) | `Client ID` `Client Secret` | `read_user` | `/login/oauth2/code/gitlab` | -| Gitee | [https://gitee.com](https://gitee.com/api/v5/oauth_doc#/) | `Client ID` `Client Secret` | `user_info` | `/login/oauth2/code/gitee` | -| LINUX DO | [https://wiki.linux.do](https://wiki.linux.do/Community/LinuxDoConnect) | `Client ID` `Client Secret` | 无需手动设置 | `/login/oauth2/code/linuxdo` | -| SSO(任意 OAuth2 / OpenID Connect 提供商) | 通用 OAuth2 / OpenID Connect(参见 [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749) / OIDC 文档) | `Client ID` `Client Secret` `Authorization URI` `Token URI` `UserInfo Url` `Scopes` `UserName Attribute` | 按提供商要求设置 | `/login/oauth2/code/sso` | +| 服务商 | 文档 | Halo 所需配置 | Scope | 回调地址 | +| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- | ---------------- | -------------------------------------- | +| GitHub | [https://docs.github.com](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) | `Client ID` `Client Secret` | 无需手动设置 | `/login/oauth2/code/github` | +| Google | [https://developers.google.com](https://developers.google.com/identity/openid-connect/openid-connect) | `Client ID` `Client Secret` | 无需手动设置 | `/login/oauth2/code/google` | +| GitLab | [https://docs.gitlab.com](https://docs.gitlab.com/ee/integration/oauth_provider.html#configure-gitlab-as-an-oauth-20-authentication-identity-provider) | `Client ID` `Client Secret` | `read_user` | `/login/oauth2/code/gitlab` | +| Gitee | [https://gitee.com](https://gitee.com/api/v5/oauth_doc#/) | `Client ID` `Client Secret` | `user_info` | `/login/oauth2/code/gitee` | +| LINUX DO | [https://wiki.linux.do](https://wiki.linux.do/Community/LinuxDoConnect) | `Client ID` `Client Secret` | 无需手动设置 | `/login/oauth2/code/linuxdo` | +| SSO(任意 OAuth2 / OpenID Connect 提供商) | 通用 OAuth2 / OpenID Connect(参见 [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749) / OIDC 文档) | `Client ID` `Client Secret` `Authorization URI` `Token URI` `UserInfo Url` `Scopes` `UserName Attribute` | 按提供商要求设置 | `/login/oauth2/code/sso` | 注意事项: From 81d6380590fca0d451fce3aaf4644d4baf2686bc Mon Sep 17 00:00:00 2001 From: Howie Xie <94725606+HowieHz@users.noreply.github.com> Date: Wed, 11 Feb 2026 16:23:22 +0800 Subject: [PATCH 5/8] Update src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java b/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java index 2906f61..6333eda 100644 --- a/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java +++ b/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java @@ -138,7 +138,6 @@ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { */ private ReactiveJwtDecoderFactory createOidcIdTokenDecoderFactory( WebClient webClient) { - return new ReactiveJwtDecoderFactory() { @Override public ReactiveJwtDecoder createDecoder(ClientRegistration clientRegistration) { From dacf9c262cca9a2d2d234a6e0766ae9cb8bac2b3 Mon Sep 17 00:00:00 2001 From: Howie Xie <94725606+HowieHz@users.noreply.github.com> Date: Wed, 11 Feb 2026 16:34:37 +0800 Subject: [PATCH 6/8] Update src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../HaloOAuth2AuthenticationWebFilter.java | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java b/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java index 6333eda..aac0e92 100644 --- a/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java +++ b/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java @@ -143,33 +143,43 @@ private ReactiveJwtDecoderFactory createOidcIdTokenDecoderFa public ReactiveJwtDecoder createDecoder(ClientRegistration clientRegistration) { // Determine the JWS algorithm from provider metadata SignatureAlgorithm jwsAlgorithm = resolveJwsAlgorithm(clientRegistration); - + String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri(); - if (jwkSetUri == null) { - OAuth2Error oauth2Error = new OAuth2Error( - "missing_signature_verifier", - "Failed to find a Signature Verifier for Client Registration: '" - + clientRegistration.getRegistrationId() - + "'. Check to ensure you have configured the JWK Set URI.", - null - ); - throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); + NimbusReactiveJwtDecoder decoder; + if (StringUtils.hasText(jwkSetUri)) { + // Build decoder with custom WebClient for JWKS retrieval using explicit JWK Set URI + decoder = NimbusReactiveJwtDecoder + .withJwkSetUri(jwkSetUri) + .jwsAlgorithm(jwsAlgorithm) + .webClient(webClient) + .build(); } - - // Build decoder with custom WebClient for JWKS retrieval - NimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder - .withJwkSetUri(jwkSetUri) - .jwsAlgorithm(jwsAlgorithm) - .webClient(webClient) - .build(); - + else { + // Fall back to issuer-based discovery when JWK Set URI is not configured + String issuerUri = clientRegistration.getProviderDetails().getIssuerUri(); + if (!StringUtils.hasText(issuerUri)) { + OAuth2Error oauth2Error = new OAuth2Error( + "missing_signature_verifier", + "Failed to find a Signature Verifier for Client Registration: '" + + clientRegistration.getRegistrationId() + + "'. Configure either the JWK Set URI or the Issuer URI.", + null + ); + throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); + } + decoder = NimbusReactiveJwtDecoder + .withIssuerLocation(issuerUri) + .jwsAlgorithm(jwsAlgorithm) + .webClient(webClient) + .build(); + } + // Apply default OIDC claim type converters decoder.setClaimSetConverter( MappedJwtClaimSetConverter.withDefaults( ReactiveOidcIdTokenDecoderFactory.createDefaultClaimTypeConverters() ) ); - return decoder; } From eb7b139720d998e57ecb3139023c145f9ad61c04 Mon Sep 17 00:00:00 2001 From: HowieHz Date: Wed, 11 Feb 2026 16:36:55 +0800 Subject: [PATCH 7/8] docs: Clarify OIDC ID token decoder javadoc --- .../run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java b/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java index aac0e92..4a7c61e 100644 --- a/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java +++ b/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java @@ -134,7 +134,11 @@ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { /** * Creates a custom OIDC ID token decoder factory that uses the provided WebClient - * for JWKS/issuer discovery, ensuring proxy configuration is applied. + * for JWKS retrieval and issuer discovery, ensuring proxy configuration is applied. + * + * When jwkSetUri is provided, it directly retrieves the JWKS from that URI. + * When only issuerUri is provided, it performs issuer-based discovery by fetching + * the OpenID Connect configuration from the issuer's .well-known endpoint. */ private ReactiveJwtDecoderFactory createOidcIdTokenDecoderFactory( WebClient webClient) { From 5b26ed2b6854b4f8e88f8cf16f5390ef24b95d0a Mon Sep 17 00:00:00 2001 From: HowieHz Date: Wed, 11 Feb 2026 16:54:13 +0800 Subject: [PATCH 8/8] Prioritize OIDC auth manager in delegating auth --- .../java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java b/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java index 4a7c61e..16c7951 100644 --- a/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java +++ b/src/main/java/run/halo/oauth/HaloOAuth2AuthenticationWebFilter.java @@ -100,7 +100,7 @@ public HaloOAuth2AuthenticationWebFilter(Oauth2LoginConfiguration configuration, var oidcIdTokenDecodeFactory = createOidcIdTokenDecoderFactory(webClient); oidcAuthManager.setJwtDecoderFactory(oidcIdTokenDecodeFactory); var authManager = - new DelegatingReactiveAuthenticationManager(oauth2AuthManager, oidcAuthManager); + new DelegatingReactiveAuthenticationManager(oidcAuthManager, oauth2AuthManager); var filter = new OAuth2LoginAuthenticationWebFilter( authManager, configuration.getAuthorizedClientRepository() );