54 lines
2.8 KiB
Markdown
54 lines
2.8 KiB
Markdown
# 7. Choosing
|
|
|
|
*Prev: [6. mTLS](06-mtls.md)*
|
|
|
|
## Which propagation strategy
|
|
|
|
| Situation | Use |
|
|
|---|---|
|
|
| One hop, same team, same trust boundary, audit needs the user | Relay |
|
|
| A job with no user: scheduler, message listener, reconciliation | Client credentials |
|
|
| The next hop is another team's service, and audit needs the user | Token exchange |
|
|
| The next hop is outside your organisation | Client credentials, and a token minted for them |
|
|
|
|
The honest default is **relay inside a boundary, exchange across one**. Client credentials is
|
|
the right answer more often than it is used for work with no user attached, and the wrong answer
|
|
whenever somebody will later ask "who did this?".
|
|
|
|
## Do you need any of this?
|
|
|
|
A callout that belongs in every article on this subject and is usually missing:
|
|
|
|
> **If your services are a single deployment behind one ingress, all of this is cost with no
|
|
> benefit.** Two Spring Boot applications in one VPC, called by one another, with no
|
|
> multi-tenancy and no external partner, do not need an authorization server, a token exchange
|
|
> grant or a mesh. Network-level isolation plus a shared secret is a defensible design, and it
|
|
> is a design you can reason about at 3am. The machinery in this repository earns its keep when
|
|
> there are enough services, enough teams, or enough regulatory pressure that "who called this,
|
|
> on whose behalf, with what permission" has to be answerable from a log rather than from
|
|
> memory.
|
|
|
|
## A checklist that is short on purpose
|
|
|
|
1. Every resource server validates **audience**, not just signature and expiry (chapter 4).
|
|
2. No relay reads `SecurityContextHolder` from a thread the request did not create (chapter 2).
|
|
3. Every service has a `SecurityFilterChain` bean, so nothing falls back to Boot's Basic default
|
|
(chapter 5).
|
|
4. Client registrations name `token-uri` / `jwk-set-uri` rather than `issuer-uri`, unless you
|
|
want a startup-ordering dependency (chapter 1).
|
|
5. If identity arrives in a header, something upstream strips that header from every external
|
|
request, and you can name the component that does it (chapter 6).
|
|
6. Token lifetimes are short enough that the audience gap in item 1 is bounded even when
|
|
somebody forgets.
|
|
|
|
## Further reading
|
|
|
|
- [Spring Security Context Propagation](https://ankurm.com/spring-security-context-propagation-complete-guide/) — why the async relay returns 401
|
|
- [The Spring Security Filter Chain Explained](https://ankurm.com/spring-security-filter-chain-explained/) — where `BearerTokenAuthenticationFilter` sits
|
|
- [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) — token exchange
|
|
- [RFC 9068](https://datatracker.ietf.org/doc/html/rfc9068) — the `at+jwt` access token profile
|
|
- [RFC 8705](https://datatracker.ietf.org/doc/html/rfc8705) — mTLS client authentication and certificate-bound access tokens
|
|
|
|
---
|
|
*Prev: [6. mTLS](06-mtls.md)*
|