Skip to content

Introducing new TypedAuthentication<T> Interface - 7.0 Proposal #14352

Closed
@Crain-32

Description

@Crain-32

*Keeping Original Post for Context
Expected Behavior
The Authentication Interface is core to Spring Security, however there are some genuine pain points with it and the Developer Experience with Spring Security. This suggestion should be seen in line with the efforts of #13266 .
To help improve this I suggest adding a single Generic to Authentication,

public interface Authentication<T> extends Serializable {
    	Collection<? extends GrantedAuthority> getAuthorities();
    	
    	Object getCredentials();
    	
    	Object getDetails();
    	// Alternative would be keep Object getPrincipal() and include a new Function
    	T getPrincipal();
    	
    	boolean isAuthenticated();

        void setAuthenticated(boolean isAuthenticated);
}
// New Interface 
public interface UserAuthentication extends Authentication<UserDetails> {
     @Override
     default Collection<? extends GrantedAuthority> getAuthorities() {
         return this.getPrincipal().getAuthorities();
     }
}
// Example of a Future Interface
public interface JwtAuthentication extends Authentication<ImaginarySpringSecurityJwt> {}

This is similar to what was mentioned here, however taking a more step based approach, and focusing first on the main downstream usage.
To reduce adoption friction I'd also expect the following interface to be created,

public interface LegacyAuthentication extends Authentication<Object> {}

Current Behavior
Weak Typing with
Authentication.getPrinciple() -> Object

Context

The Authentication Interface is very old, and thus it falls into a hard spot of needing improvements, but being so core to the framework that adjusting it will have serious considerations for everyone involved.

Personally at work I've had to deal with the pain I assume many people have, which is Object getDetails() returns a UserDetails Object, but getPrincipal() doesn't, as getDetails sounds like it should return UserDetails. This means something like @AuthenticationPrincipal isn't useful, most of the Testing Support provided by Spring Security requires custom workarounds to use, etc. By putting a Generic around getPrincipal() and/or introducing a UserAuthentication, we're able to hint to Developers the approach Spring Security recommends, which I expect should improve the downstream experience of Spring Security.

As mentioned in the Issue Comment linked above, the ideal world would be Authentication<C, D, P>, but due to the core functionality of Authentication, we should take this safely, and I believe we can get most of the benefits for Developers by limiting the Generic to getPrincipal().

Alternatives
Wrap Authentication for typing, as an example

public TypedAuthentication<T> extends Authentication {
  @Override
  T getPrinciple();
}

This would provide a new Code Path for this "New" style to use, however would still leave the underlying smell. I believe we should only look into it if the potential User Disruption from the Generic Authentication is deemed to high to even consider this.

Downstream Issues
Although not on the Template, I figured if I'm going to suggest a breaking change, I should list the considerations I know about.

1, Contributor Conflicts, sweeping updates across all Modules as they move from Authentication to LegacyAuthentication, or Authentication<?>. This could be a source of merge conflicts, and without additional effort from maintainers down the line negate the Type Support this Issue is looking to provide.

2, Large Scale Documentation Updates, as mentioned Authentication is core to Spring Security, so this could entail reviewing the entire Spring Security Documentation for adjustments.

3, User Adoption Friction, given how we're swapping for a Generic our Users will have to deal with adjusting their implementations or variable references in line with the adjustment, providing LegacyAuthentication should hopefully improve that experience for them.

Post Attempt

The unfortunate truth is that Authentication is too ingrained in Spring to attempt to adjust it with Generics. I attempted it this afternoon to see the amount of effort it would take. Several hundred lines and files later I've conceded to the Java Compiler (And the pain of adding <?> to every reference)

I believe the alternate approach of introducing a new TypedAuthentication<T> extends Authentication is the best approach now, ideally with an upper bound like UserDetails to make the Compiler happy. The main issue I was experiencing was that because we had no upper bound, Java would complain about the capture groups, and because the original value was Object I couldn't introduce a new Upper Bound without creating breaking change that would invalidate the entire effort.

Metadata

Metadata

Assignees

Labels

in: coreAn issue in spring-security-corestatus: waiting-for-feedbackWe need additional information before we can continuetype: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions