6

When creating a resource server to protect my api endpoints in spring boot I am using spring-boot-starter-oauth2-resource-server and it does not try to pull back the claims from the userinfo endpoint on the authentication server. I am wondering if this is expected behavior and if so should I be using another library to setup spring security for my resource server? It appears debugging that this module pulls in the info from the well-known and should be able to easily know the userinfo endpoint.

This is the current dependencies that I am using maybe I am just missing some module that I am not aware of.

    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>openid-resource</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>openid-resource</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

2 Answers2

8

NatFar's answer is right on the money, but I thought I'd add some color that I couldn't fit into a comment.

Indeed, Resource Server is about authorization, but the API provides hooks for you to be able to customize this, calling a userinfo endpoint being among them.

As of Spring Security 5.1:

@Override
protected void configure(HttpSecurity http) {
    http
        .oauth2ResourceServer()
             .jwt()
                 .jwtAuthenticationConverter(new MyConverter());
}

private static class MyConverter
    implements Converter<Jwt, AbstractAuthenticationToken> {

    @Override
    public AbstractAuthenticationToken convert(Jwt jwt) {
        // invoke the userinfo endpoint
        // construct an Authentication statement from the response
    }

}

Spring Security 5.1 only supports JWT, however in Spring Security 5.2 (which GAs in a couple of weeks) it supports opaque tokens as well. It also generalizes the representation a bit:

@Override
protected void configure(HttpSecurity http) {
    http
        .oauth2ResourceServer()
             .opaqueToken()
                 .introspector(new MyIntrospector());
}

private static class MyIntrospector implements OpaqueTokenIntrospector {

    @Override
    public OAuth2AuthenticatedPrincipal introspect(String token) {
        // invoke the userinfo endpoint
        // construct an OAuth2AuthenticatedPrincipal from the response
    }
}

I've added a ticket to get documentation added around your usecase; however, the JWT-introspection example that's already there is fairly close.

jzheaux
  • 7,042
  • 3
  • 22
  • 36
  • Perfect. That really clears things up quite well. I am already having to create my own converter in order to use a custom JwtAuthenticationToken extension so that get name uses the username claim instead of the subject. – Jonathan Jensen Sep 12 '19 at 14:22
  • Here's some updated Spring Security documentation regarding this: https://docs.spring.io/spring-security/site/docs/current-SNAPSHOT/reference/htmlsingle/#oauth2resourceserver-opaque-userinfo – jzheaux Sep 12 '19 at 14:58
  • Thanks I will give it a look. – Jonathan Jensen Sep 12 '19 at 19:01
5

Check out what the Spring reference says about the resource server:

It’s atypical for a resource server to need to call a user info endpoint. This is because, fundamentally, a resource server is about authorizing a request, not authenticating it

Usually, it's the client application that queries the user info endpoint for more info about the user.

But the reference proceeds to show how to configure the resource server to call the user info endpoint if you're using the old Spring Security OAuth.

However, in Spring Security 5, it appears that you're only able to use the user info endpoint via .oauth2Client() or .oauth2Login().

The reference states that it's the client the makes a request for the user info.

NatFar
  • 2,090
  • 1
  • 12
  • 29
  • 2
    That documentation is for using spring-security-oauth2-autoconfigure I am using spring-boot-starter-oauth2-resource-server which uses spring-security-oauth2-resource-server. Why should I use spring-security-oauth2-autoconfigure instead of the one that I am using? When you create a resource server with Spring Boot Initializer it uses what I am using. – Jonathan Jensen Sep 12 '19 at 03:34
  • So that ends up pulling in Spring Security 5? – NatFar Sep 12 '19 at 03:40