1
0
Files

75 lines
3.2 KiB
Markdown

# 5. The gateway, and what `TokenRelay` actually relays
*Prev: [4. What is not validated](04-what-is-not-validated.md) · Next: [6. mTLS](06-mtls.md)*
Spring Cloud Gateway Server MVC ships a filter that sounds like it solves the whole problem:
```yaml
filters:
- TokenRelay=
```
It is `TokenRelayFilterFunctions`, with two forms:
```java
public static HandlerFilterFunction<ServerResponse, ServerResponse> tokenRelay();
public static HandlerFilterFunction<ServerResponse, ServerResponse> tokenRelay(String clientRegistrationId);
```
The documentation is precise about what it does, and it is not what the name suggests to most
readers: it takes the access token **of the currently authenticated user** — the one obtained
by `oauth2Login()` — or of the named client registration, and puts it in the `Authorization`
header of the proxied request.
It does not mean "forward the incoming bearer token". There is nothing to forward from if the
gateway never performed a login.
## The A/B
`gateway.yml` defines two routes to the same destination, differing only in the filter.
[`docs/output/04-gateway-token-relay.txt`](output/04-gateway-token-relay.txt):
```
/edge/relay (TokenRelay=) → 200, sub: alice
/norelay/x (no TokenRelay) → 200, sub: alice
```
Identical. The token arrived because the gateway proxied the `Authorization` header the caller
sent, which it would have done anyway. In this configuration `TokenRelay` contributes nothing.
Where it earns its place is the other shape: a gateway that terminates a **browser session**
with `oauth2Login()`, keeps the tokens server-side, hands the browser nothing but a session
cookie, and attaches a token on the way through. That is the backend-for-frontend pattern, and
it is a genuinely good answer to "where do I keep the token in a SPA" — because the answer is
"not in the SPA".
## The 401 that has nothing to do with OAuth
Before `GatewaySecurityConfig` existed, every call through the gateway came back:
```
HTTP/1.1 401
WWW-Authenticate: Basic realm="Realm", charset="UTF-8"
```
A perfectly valid bearer token, rejected by a gateway that had never been told to expect one.
Spring Boot applies a default filter chain — HTTP Basic and form login over every path — to any
application on the classpath with Spring Security and no `SecurityFilterChain` bean. A gateway
is an application. `WWW-Authenticate: Basic` in front of a token-based estate always means this.
## Choosing where the gateway sits
| Shape | Gateway does | Downstream sees |
|---|---|---|
| Pass-through | Routes; validates nothing | The caller's token; each service validates it |
| Edge validation | Validates the token, strips it, adds its own identity | The gateway's identity |
| BFF | `oauth2Login()`, holds tokens, `TokenRelay` | The user's token, obtained by the gateway |
Pass-through is the default and is fine while every service validates properly — chapter 4 is
about how often that assumption is wrong. Edge validation is the one that tempts teams into
"the gateway checked it, so we can trust the header", which is chapter 6's failure mode wearing
a different hat.
---
*Prev: [4. What is not validated](04-what-is-not-validated.md) &middot; Next: [6. mTLS](06-mtls.md)*