Description
Related to #5200 and #6352 (comment)
We should introduce OAuth2TokenAttributes
, a class that holds an OAuth 2.0 Token's attributes. To this point, each authentication token has exposed attributes simply as a Map
.
It works out best, though, when an new class's usage can be demonstrated, ensuring that the API is correct. So, to complete this ticket, a few things need to be done:
- Introduce
OAuth2TokenAttributes
This is a simple domain object that contains a Map
:
public class OAuth2TokenAttributes {
<T> T getAttribute(String name);
Map<String, Object> getAttributes();
}
It should probably go in oauth2-core
in org.springframework.security.oauth2.core
.
- Change
OAuth2IntrospectionAuthenticationToken
's constructor
It should take an OAuth2TokenAttributes
for its principal instead of a Map
. While this is a breaking change, it's allowable since that API has not GA'd yet
- Update the
oauth2resourceserver-opaque
sample
This sample should be adjusted to use the token's principal (OAuth2TokenAttributes
)
Extra Background
Spring Security offers different ways to access OAuth 2.0 token attributes, depending on how they were obtained.
For example, a Resource Server can authenticate via JWT tokens and obtain their contents in the following way:
@GetMapping("/protected")
public String method(@AuthenticationPrincipal Jwt jwt) {
String subject = jwt.getSubject();
// ...
}
// and
@GetMapping("/protected")
public String method(@AuthenticationPrincipal(expression="subject") String subject) {
// ...
}
When using introspection, though, the principal is simply a map, so the pattern is:
@GetMapping("/protected")
public String method(@AuthenticationPrincipal Map<String, Object> attributes) {
String subject = (String) attributes.get("sub");
// ...
}
// and
@GetMapping("/protected")
public String method(@AuthenticationPrincipal(expression="['sub']") String subject) {
// ...
}
It would be nice if application code didn't have to know the token verification strategy to extract the attributes from the principal.
Further, there is potentially some clean up here with clarifying the meaning around attributes and claims. Claims are unverified attributes. Or, in other words, a JWT has a claim set, but the ensuing Authentication
object has attributes since the JWT at that point is verified.
There is already one way to get attributes in an agnostic way:
@GetMapping("/protected")
public String method(AbstractOAuth2TokenAuthenticationToken token) {
String subject = (String) token.getTokenAttributes().get("sub");
// ...
}
But it would be nicer if this were a bit simpler and users could take advantage of more Spring Security features agnostically.
By thinking more about the principal and how to expose it, it may be possible to achieve something like:
@GetMapping("/protected")
public String method(@AuthenticationPrincipal OAuth2TokenAttributes attributes) {
String subject = attributes.getAttribute("sub");
// ...
}
// and
@GetMapping("/protected")
public String method(@AuthenticationPrincipal(expression="attribute['sub']") String subject) {
// ...
}
that works for any OAuth 2.0 Authentication
type.