OAuth2 — Letting a Trusted Server Vouch for Users
The library wants members to sign in with their school account instead of yet another password, and a partner reading app wants to show a member's loans without ever seeing that member's password. Both needs have the same answer: stop handling passwords in library-service and let a dedicated server vouch for users. That arrangement is OAuth2.
The rule
The authorization server issues tokens; everyone else just checks them.
OAuth2 names four roles:
| Role | In our story |
|---|---|
| Resource owner | Asha, the member who owns her loan data |
| Client | The reading app that wants access on her behalf |
| Authorization server | The school's login server (Keycloak, Okta, Google and so on) |
| Resource server | library-service, which holds the loans and checks tokens |
The most common flow for users is the authorization code flow: the client sends Asha's browser to the authorization server; Asha logs in there and agrees to share; the server sends a short-lived code back to the client; the client exchanges that code for an access token. Modern clients add PKCE, a one-time secret that stops a stolen code from being used. Machine-to-machine calls, with no user involved, use the client credentials flow instead.
A token carries scopes, the permissions granted, such as loans.read. The client never learns Asha's password, and she can revoke the app's access without changing it.
Spring Boot has a starter for each side:
spring-boot-starter-oauth2-client— your app logs users in through another server ("Sign in with...").spring-boot-starter-oauth2-resource-server— your API accepts tokens from callers.
library-service is a resource server. One property points it at the issuer:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://login.school.example/realms/libraryhttp.authorizeHttpRequests(auth -> auth
.requestMatchers("/loans/**").hasAuthority("SCOPE_loans.read")
.anyRequest().authenticated())
.oauth2ResourceServer(oauth -> oauth.jwt(Customizer.withDefaults()));Boot fetches the issuer's public keys and checks every incoming token's signature. Each scope becomes an authority with the SCOPE_ prefix.
The shortcut
In any OAuth2 diagram, first ask "Who issues, who holds, who checks?" The authorization server issues, the client holds, the resource server checks. Everything else is detail of how the token travels.
Client redirects
The reading app sends Asha's browser to the school login server, asking for scope
loans.read.
A frequent confusion: OAuth2 is about delegated access, not identity. Logging users in on top of OAuth2 uses OpenID Connect, which adds an ID token that describes who the user is.
Drills
Answer fast — trust the rule.
In OAuth2, which role holds the protected data and checks tokens?
Which starter lets library-service accept bearer tokens on its API?
A token has scope loans.read. Which authority does Spring Security create from it by default?
Why is the authorization code flow safer than giving the reading app Asha's password?
Remember
- The authorization server issues tokens; everyone else just checks them.
- Ask "who issues, who holds, who checks?" before reading any flow.
- Trap: mixing up the client starter (log users in) with the resource-server starter (accept tokens).
