Search code examples
springspring-securityoauth-2.0openid-connect

Spring security configuration in `application.yml`, client or resource server?


I am getting confused by the spring security configuration. For example, I am developing a Spring-boot application. In project resources/application.yml, I can configure it as below to communicate with Keycloak(or any other OAuth2 server):

spring:
  security:
    oauth2:
      client:
        registration:
          spring-app:
            client-id: spring-app
            client-secret: VWsQaq02oUYaWNzb51dKzACrF5QLgTm8
            authorization-grant-type: authorization_code
            scope: openid, profile, roles
            redirect-uri: http://localhost:8080/login/oauth2/code/spring-app
        provider:
          spring-app:
            issuer-uri: http://localhost:8383/realms/foo

If I shrink above configuration to highlight the key things for my question later:

spring:
  security:
    oauth2:

      client:
        registration:
          spring-app:
            ...
            ...
        provider:
          spring-app:
            issuer-uri: http://localhost:8383/realms/foo

I am basically configured my project as an OAuth2 client, and I also tell who is the OAuth2 server provider (Keycloak in my case, but it can be any other OAuth2 server).

But If I change the configuration completely to the following one in the same spring-boot project:

spring:
  security:
    oauth2:
     
      resourceserver:
        jwt:
          jwk-set-uri: ${spring.security.oauth2.resourceserver.jwt.issuer-uri}/protocol/openid-connect/certs
          issuer-uri: http://localhost:8383/realms/foo

As you can see, above configuration is a resourceserver(Resource Server) configuration, which treats my spring-boot application as a resource server instead of client in the context of OAuth2. The endpoints exposed by my spring-boot controllers are also protected by keycloak with this configuration (which I've tested), as long as the issuer-uri is pointing to keycloak uri.

I get very confused on when should I do client configuration and when should I do resource server configuration for my spring-boot project when using Spring Security?

I see both make sense, because my spring application is indeed an OAuth2 client being registered in Keycloak, and also my spring application is indeed a resource server which has endpoints exposed that should be protected by OAuth2 flow(s).

Could someone please clarify this for me, when to use which? Thanks in advance!


Dependencies in my project to verify things:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web:3.2.3'
    implementation 'org.springframework.boot:spring-boot-starter-security:3.2.3'

    // oauth2 client
    implementation 'org.springframework.boot:spring-boot-starter-oauth2-client:3.2.3'

    // oauth2 resource server
    implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server:3.2.3'
    ...

Solution

  • An OAuth2 resource server processes requests from an OAuth2 client. So you use:

    • OAuth2 client dependencies and conf when you need to get tokens from the authorization server to authorize requests to a resource server
    • OAuth2 resource server dependencies and conf when you expose an API queried by OAuth2 client capable of authorizing requests with tokens

    Note that:

    • in clients using authorization_code flow (oauth2Login in Spring conf), the security is based on sessions, which requires protection against CSRF. In other words, when you send a request to a Spring app with oauth2Login, the request authorization is made with a session cookie, not a Bearer token. Also, it is generally expected that such an app responds with 302 redirect to login to unauthorized requests to prtocted resources.
    • in resource servers, sessions can (should?) be disabled along with CSRF protection because authorization is based on Bearer access tokens. It is generally expected that such an app responds with 401 unauthorized to unauthorized requests to prtocted resources.

    An application can be configured as both a client with oauth2Login and as an oauth2ResourceServer, but because of the very different security conf between the two, in such a case, I configure a separate Security(Web)Filterchain bean for each. For instance, my BFFs always are configured that way because it is a client by nature but also exposes REST endpoints like Actuator ones.

    When resource servers call each others using client-credentials, client dependencies and conf are required to fetch the new tokens used in this inter service calls. But no need for a separate security filter-chain.

    In the case where a service configured as a resource server calls another resource server on behalf of the resource owner who originated the request, it can forward the access token it received. In this case, the caller is a client for the other service, but doesn't need client deps and conf: the token is taken from the security context, not from the authorization server.

    If you like to, you might find some more OAuth2 background and configuration tutorials from this Github repo of mine