wileecoyote
wileecoyote

Reputation: 83

Spring Cloud Gateway Token Relay: who is the OAuth2 client?

I'm exploring the TokenRelay filter of Spring Cloud Gateway in order to mediate between a SAML2 Session and OAuth2 Tokens (More on this in my previous question here).

I've tried to figure this out myself but I can't seem to sort it out: using this filter, who is the OAuth2 client?

I was expecting this to be the gateway itself, hence a confidential one.. but in my initial setup I see calls for the authorization code in the browser and I don't understand if this is the expected behavior or not.

If it's indeed how it's supposed to work and the Browser carries out the token exchange, does this mean that the client cannot be confidential? Should I switch to PKCE?

I might sound a little confused (I really am!) but I can't seem to find much about the matter.

Thanks!

Upvotes: 0

Views: 1736

Answers (1)

ch4mp
ch4mp

Reputation: 12534

I'll make a distinction between:

  • the user-agent: a browser in your case, but it could be a mobile application, or whatever with an HTTP client
  • the OAuth2 client: the code that gets tokens from an authorization server and sends requests authorized with Bearer access tokens to resource servers (or forwards in the case of a gateway)

When configuring single-page and mobile applications as public OAuth2 clients, the OAuth2 client is running inside the user-agent, so it's difficult to distinguish. This also has security implications that aren't the subject here (the token endpoint cannot be protected with a secret, and the tokens are exposed to the Javascript code and, as such, to XSS attacks).

When the OAuth2 client with the authorization code and refresh token flows (oauth2Login in Spring Security's DSL) is configured on the backend, the token endpoint can be protected with a secret (and firewall rules if we get picky), and tokens are kept safe on the server. In this case the distinction between the user-agent and the OAuth2 client is clear.

Let's review how the authorization code flow works with this distinction in mind:

  1. the user-agent redirects to the the OAuth2 client authorization endpoint (to the OAuth2 client's /oauth2/authorization/{registration-id} by default with Spring Security)
  2. the OAuth2 client ensures that a session exists for the user-agent (sets a cookie if it's not the case yet), sets some security parameters, and redirects the user-agent to the authorization server authorization endpoint
  3. the authorization server runs whatever identification flow it is configured with (login forms which might include multi-factor stuff, remember me cookie, magic link, etc.). When the user is successfully authenticated, the authorization server redirects the user-agent to the OAuth2 client with an authorization code (to the OAuth2 client's /login/oauth2/code/{registration-id} by default)
  4. the OAuth2 client exchanges the code for tokens. This is direct communication between the OAuth2 client and the authorization server. So, when the client runs on the backend, the user-agent is not involved. After it got tokens, the OAuth2 client can change its state to "authorized" and redirect the user-agent to a "post-login" route.

In OAuth2 BFF pattern, the OAuth2 client is the BFF itself => the browser is the vessel to transmit the authorization-code from the authorization server to the gateway, but it never sees the OAuth2 tokens (only the gateway and downstream resource servers do).

Edit

I published on Baeldung a tutorial for configuring Spring Cloud Gateway as an OAuth2 BFF with oauth2Login and the TokenRelay filter. It contains sample implementations for Angular, React (Next.js, but without the NextAuth lib), and Vue.

Upvotes: 3

Related Questions