Skip to content

Commit

Permalink
Support OIDC protocol (#71)
Browse files Browse the repository at this point in the history
This PR adds `OidcAuthorizationCodeReactiveAuthenticationManager` to authenticate request if authorization request contains the scope `openid`.

Below is an example of Logto integration:

- Oauth2ClientRegistration

```yaml
apiVersion: oauth.halo.run/v1alpha1
kind: Oauth2ClientRegistration
metadata:
  name: logto
spec:
  clientAuthenticationMethod: "client_secret_basic"
  authorizationGrantType: "authorization_code"
  redirectUri: "{baseUrl}/login/oauth2/code/logto"
  scopes:
    - "profile"
    - "openid"
  authorizationUri: "https://2ri34b.logto.app/oidc/auth"
  tokenUri: "https://2ri34b.logto.app/oidc/token"
  userInfoUri: "https://2ri34b.logto.app/oidc/me"
  issuerUri: "https://2ri34b.logto.app/oidc"
  jwkSetUri: "https://2ri34b.logto.app/oidc/jwks"
  userInfoAuthenticationMethod: "header"
  userNameAttributeName: "username"
  clientName: "Logto"
  configurationMetadata:
    id_token_signing_alg_values_supported:
      - ES384
```

> https://2ri34b.logto.app/oidc/.well-known/openid-configuration

Please note that the `spec.configurationMetadata.id_token_signing_alg_values_supported` is required because Logto only uses `ES384` algorithm to sign.

- AuthProvider

```yaml
apiVersion: auth.halo.run/v1alpha1
kind: AuthProvider
metadata:
  name: logto
  labels:
    auth.halo.run/auth-binding: "true"
spec:
  displayName: Logto
  description: Logto is an Auth0 alternative designed for modern apps and SaaS products. It offers a seamless developer experience and is well-suited for individuals and growing companies.
  logo: /plugins/plugin-oauth2/assets/static/logto.svg
  website: https://logto.io
  authenticationUrl: /oauth2/authorization/logto
  bindingUrl: /oauth2/authorization/logto
  unbindUrl: /apis/uc.api.auth.halo.run/v1alpha1/user-connections/logto/disconnect
  authType: oauth2
  settingRef:
    name: generic-oauth2-setting
    group: genericOauth
  configMapRef:
    name: oauth2-logto-config
```

/kind feature

Fixes #68 

```release-note
支持 OIDC 认证协议
```
  • Loading branch information
JohnNiang authored Oct 30, 2024
1 parent 8cc859d commit 79e06a1
Showing 1 changed file with 53 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
package run.halo.oauth;

import com.nimbusds.oauth2.sdk.ParseException;
import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
import lombok.extern.slf4j.Slf4j;
import net.minidev.json.JSONObject;
import org.springframework.security.authentication.DelegatingReactiveAuthenticationManager;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.client.authentication.OAuth2LoginReactiveAuthenticationManager;
import org.springframework.security.oauth2.client.endpoint.WebClientReactiveAuthorizationCodeTokenResponseClient;
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.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.jose.jws.SignatureAlgorithm;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationFailureHandler;
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
Expand All @@ -21,19 +32,49 @@
* @author johnniang
* @since 2.20.0
*/
@Slf4j
@Component
public class HaloOAuth2AuthenticationWebFilter implements AuthenticationSecurityWebFilter {

private final WebFilter delegate;

public HaloOAuth2AuthenticationWebFilter(Oauth2LoginConfiguration configuration,
ServerSecurityContextRepository securityContextRepository) {
var authManager = new OAuth2LoginReactiveAuthenticationManager(
new WebClientReactiveAuthorizationCodeTokenResponseClient(),
var accessTokenResponseClient = new WebClientReactiveAuthorizationCodeTokenResponseClient();
var oauth2AuthManager = new OAuth2LoginReactiveAuthenticationManager(
accessTokenResponseClient,
new DefaultReactiveOAuth2UserService()
);
var filter = new OAuth2LoginAuthenticationWebFilter(authManager,
configuration.getAuthorizedClientRepository());
var oidcAuthManager = new OidcAuthorizationCodeReactiveAuthenticationManager(
accessTokenResponseClient,
new OidcReactiveOAuth2UserService()
);
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;
});
oidcAuthManager.setJwtDecoderFactory(oidcIdTokenDecodeFactory);
var authManager =
new DelegatingReactiveAuthenticationManager(oauth2AuthManager, oidcAuthManager);
var filter = new OAuth2LoginAuthenticationWebFilter(
authManager, configuration.getAuthorizedClientRepository()
);
filter.setRequiresAuthenticationMatcher(configuration.getAuthenticationMatcher());
var converter = new ServerOAuth2AuthorizationCodeAuthenticationTokenConverter(
configuration.getClientRegistrationRepository()
Expand All @@ -42,7 +83,14 @@ public HaloOAuth2AuthenticationWebFilter(Oauth2LoginConfiguration configuration,
successHandler.setRequestCache(configuration.getRequestCache());
filter.setAuthenticationSuccessHandler(successHandler);
filter.setAuthenticationFailureHandler(
new RedirectServerAuthenticationFailureHandler("/login?oauth2_error")
new RedirectServerAuthenticationFailureHandler("/login?oauth2_error") {
@Override
public Mono<Void> onAuthenticationFailure(WebFilterExchange webFilterExchange,
AuthenticationException exception) {
log.error("Failed to authentication with OAuth2", exception);
return super.onAuthenticationFailure(webFilterExchange, exception);
}
}
);
filter.setServerAuthenticationConverter(converter);
filter.setSecurityContextRepository(securityContextRepository);
Expand Down

0 comments on commit 79e06a1

Please sign in to comment.