Skip to content

OAuth2 - Support customizing OAuth2AuthenticationToken through a single AuthenticationProvider #10033

@Kehrlann

Description

@Kehrlann

Context

In my project, I am supporting multiple ways of logging in:

  • "internal" user store (in-memory or in-database),
  • OAuth2 login
  • SAML login

These three ways of authenticating yield different Authentication objects: UsernamePasswordAuthenticationToken, OAuth2[Login]AuthenticationToken and Saml2Authentication.

I have custom rules for extracting information from the authentications (e.g. scopes, SAML attributes, etc).

I use the authentication results through AbstractAuthenticationEvent (e.g. log some specific information when authentication fails), and through the Security context (e.g. to display values mapped from the authentication to the user).

To minimize code duplication and switch-case when using those authentications, I rely on a custom interface, e.g. CustomAuthentication.
I register custom AuthenticationProviders which extend from (or delegate to) spring-security basic authentication providers. Those providers obtain the base Authentication object (through calling super.authenticate(), for example), perform custom logic, and return a subclass of the base Authentication.

For example:

class CustomSamlAuthentication extends Saml2Authentication implements CustomAuthentication {
   [...]
}


public class CustomSamlAuthenticationProvider implements AuthenticationProvider {

	private final OpenSamlAuthenticationProvider delegate;

	public CustomSamlAuthenticationProvider() {
		this.delegate = new OpenSamlAuthenticationProvider();
	}

	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		Authentication authenticationResult = this.delegate.authenticate(authentication);
		return new CustomSamlAuthentication((Saml2AuthenticationToken) authentication, (Saml2Authentication) authenticationResult);
	}

	@Override
	public boolean supports(Class<?> authentication) {
		return delegate.supports(authentication);
	}
}

Alternatives would be to transform the authentication when we consume it, but that could be in multiple places. Also, some of the authentication information may be lost or more difficult to obtain by that time (e.g. which relying party registration was used to obtain this authentication?)

Expected Behavior

I want to get this result simply by providing two things:

  • A custom Authentication implementation
  • A custom AuthenticationProvider

This works very well with UserDetails and SAML, but it is not possible to accomplish this with OAuth2.

Current Behavior

To accomplish this with OAuth2, I need:

  • A custom OAuth2LoginAuthenticationToken + custom OAuth2LoginAuthenticationProvider and/or OidcAuthorizationCodeAuthenticationProvider
    • This is required for the authentication events to have the information I need
  • A custom OAuth2LoginToken
  • A custom OAuth2LoginAuthenticationFilter to override the behavior of the base class

See this sample project for a full implementation of the three use-cases.

Ideas

Two rough ideas:

  • Updating the OAuth2LoginAuthenticationFilter to support a custom converter, from OAuth2LoginAuthenticationToken to OAuth2AuthenticationToken
    • I could configure the login filter with "just" this converter. It is a bit involved but I don't have to track changes in the filter implementation over time
  • Update the OAuth2LoginAuthenticationProvider and OidcAuthorizationCodeAutheProvider to take a OAuth2LoginAuthenticationToken and return a straight OAuth2AuthenticationToken as the authentication result ... but this probably has very wide implications.

I have not looked into reactive support for this.

Metadata

Metadata

Assignees

Labels

in: oauth2An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose)type: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions