# 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 tokenRelay(); public static HandlerFilterFunction 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) · Next: [6. mTLS](06-mtls.md)*