Add the service-to-service module
This commit is contained in:
74
service-to-service/docs/05-the-gateway.md
Normal file
74
service-to-service/docs/05-the-gateway.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# 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) · Next: [6. mTLS](06-mtls.md)*
|
||||
Reference in New Issue
Block a user