The OAuth2 component that hosts protected resources and accepts access tokens to authorize API requests from clients.
OAuth2 Resource Server (RS) hosts the protected APIs and data. It never authenticates the user directly: it accepts an access token on each request, decides whether that token is valid and authorizes the specific operation, then serves or refuses the resource. Its security work is entirely about validating a token it did not issue.
How the RS validates depends on the token format the authorization server issues.
GET /api/user/profile
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
// JWT (self-contained): validate locally
// 1. signature against the AS public key (JWKS)
// 2. alg is an expected asymmetric algorithm (reject none/HS256 confusion)
// 3. exp not passed, nbf/iat sane
// 4. iss equals the trusted authorization server
// 5. aud contains THIS resource server's identifier
// 6. scope covers the requested operation
// Opaque (reference): ask the AS
POST /introspect
token=2YotnFZFEjr1zCsicMWpAA
// ->
{ "active": true, "scope": "read:profile", "aud": "api://profile",
"iss": "https://auth.example.com", "exp": 1770000000, "sub": "user456" }
A JWT is validated locally with no network call, which is fast and scales, but the RS cannot see a revocation that happened after the token was minted: the token stays valid until exp. Introspection asks the AS on every request, so revocation is immediate and the token stays opaque to clients, at the cost of a round trip and a hard dependency on the AS being reachable. Short JWT lifetimes narrow the revocation window; caching introspection results for a few seconds trades some freshness back for throughput.
A valid signature only proves the AS issued the token, not that it issued it for this RS. If an RS accepts any well-signed token from its issuer, a token minted for a different, lower-value API (or a token the user handed to an unrelated third-party service) can be replayed against it. The RS must check that aud names its own resource identifier and reject anything else, and confirm iss is an authorization server it actually trusts.
Scopes are not global. A token with read:profile must not reach a handler that writes, and a valid token for one user must not authorize acting on another user's records. The RS enforces the required scope at each endpoint and still applies object-level authorization (does sub own this record?): OAuth scopes grant a capability, they do not replace access-control checks on the data itself.