--------------------------------------------------------------------------
 resource server profiles: stub,roles,audience,attyp
--------------------------------------------------------------------------
--------------------------------------------------------------------------
 1. A correct token
--------------------------------------------------------------------------
  header: {"kid": "stub-key-1", "typ": "JWT", "alg": "RS256"}
  iss                "http://localhost:9000"
  aud                "reports-api"
  scope              "profile:read reports:read"
  preferred_username "alice"
  realm_access       {"roles": ["USER"]}
  resource_access    {"reports-api": {"roles": ["reports-reader"]}}

$ GET /api/me
HTTP 200
{
    "name": "alice",
    "authorities": [
        "FACTOR_BEARER",
        "ROLE_USER",
        "ROLE_reports-reader",
        "SCOPE_profile:read",
        "SCOPE_reports:read"
    ],
    "iss": "http://localhost:9000",
    "aud": [
        "reports-api"
    ],
    "typ": "JWT",
    "kid": "stub-key-1",
    "exp": "2026-08-23T10:32:56Z"
}

--------------------------------------------------------------------------
 2. Signed by the right key, but iss says something else
--------------------------------------------------------------------------
The signature verifies. The key is the same key. Only the string differs.
  header: {"kid": "stub-key-1", "typ": "JWT", "alg": "RS256"}
  iss                "http://localhost:9000/other"
  aud                "reports-api"
  scope              "profile:read reports:read"
  preferred_username "alice"
  realm_access       {"roles": ["USER"]}
  resource_access    {"reports-api": {"roles": ["reports-reader"]}}

$ GET /api/me
HTTP 401
WWW-Authenticate: Bearer error="invalid_token", error_description="An error occurred while attempting to decode the Jwt: The iss claim is not valid", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1", resource_metadata="http://localhost:8081/.well-known/oauth-protected-resource"
--------------------------------------------------------------------------
 3. A token minted for a different service in the same realm
--------------------------------------------------------------------------
This is the one that silently works when nothing checks aud.
  header: {"kid": "stub-key-1", "typ": "JWT", "alg": "RS256"}
  iss                "http://localhost:9000"
  aud                "billing-api"
  scope              "profile:read reports:read"
  preferred_username "alice"
  realm_access       {"roles": ["USER"]}
  resource_access    {"reports-api": {"roles": ["reports-reader"]}}

$ GET /api/me
HTTP 401
WWW-Authenticate: Bearer error="invalid_token", error_description="An error occurred while attempting to decode the Jwt: The aud claim is not valid", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1", resource_metadata="http://localhost:8081/.well-known/oauth-protected-resource"
--------------------------------------------------------------------------
 4. Expired 90 seconds ago
--------------------------------------------------------------------------
The default clock skew is 60s, so a token has to be more than a minute stale
before JwtTimestampValidator refuses it.

$ GET /api/me
HTTP 401
WWW-Authenticate: Bearer error="invalid_token", error_description="An error occurred while attempting to decode the Jwt: Jwt expired at 2026-08-23T10:26:26Z", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1", resource_metadata="http://localhost:8081/.well-known/oauth-protected-resource"
--------------------------------------------------------------------------
 5. Expired 30 seconds ago - inside the default clock skew
--------------------------------------------------------------------------

$ GET /api/me
HTTP 200
{
    "name": "alice",
    "authorities": [
        "FACTOR_BEARER",
        "ROLE_USER",
        "ROLE_reports-reader",
        "SCOPE_profile:read",
        "SCOPE_reports:read"
    ],
    "iss": "http://localhost:9000",
    "aud": [
        "reports-api"
    ],
    "typ": "JWT",
    "kid": "stub-key-1",
    "exp": "2026-08-23T10:27:26Z"
}

--------------------------------------------------------------------------
 6. typ=at+jwt, which RFC 9068 says an access token SHOULD carry
--------------------------------------------------------------------------
The default validator stack contains JwtTypeValidator.jwt(), which accepts only an
absent typ or typ=JWT. Whether this passes depends on the attyp profile.
  header: {"kid": "stub-key-1", "typ": "at+jwt", "alg": "RS256"}
  iss                "http://localhost:9000"
  aud                "reports-api"
  scope              "profile:read reports:read"
  preferred_username "alice"
  realm_access       {"roles": ["USER"]}
  resource_access    {"reports-api": {"roles": ["reports-reader"]}}

$ GET /api/me
HTTP 200
{
    "name": "alice",
    "authorities": [
        "FACTOR_BEARER",
        "ROLE_USER",
        "ROLE_reports-reader",
        "SCOPE_profile:read",
        "SCOPE_reports:read"
    ],
    "iss": "http://localhost:9000",
    "aud": [
        "reports-api"
    ],
    "typ": "at+jwt",
    "kid": "stub-key-1",
    "exp": "2026-08-23T10:32:56Z"
}

--------------------------------------------------------------------------
 7. No token at all
--------------------------------------------------------------------------

$ GET /api/me
HTTP 401
WWW-Authenticate: Bearer resource_metadata="http://localhost:8081/.well-known/oauth-protected-resource"

$ GET /api/public/ping
HTTP 200
{
    "status": "up"
}

--------------------------------------------------------------------------
 8. The endpoint nobody configured
--------------------------------------------------------------------------
Spring Security 7 publishes RFC 9728 protected resource metadata and points the
WWW-Authenticate challenge at it. It answers without a token.

$ GET /.well-known/oauth-protected-resource
HTTP 200
{
    "resource": "http://localhost:8081",
    "bearer_methods_supported": [
        "header"
    ],
    "tls_client_certificate_bound_access_tokens": true
}

