Table of Contents

Token introspection

An API that receives an access token asks the server whether it is active and what it says. The API authenticates as itself — the demo host's api-resource, with its API secret — and may read only tokens addressed to it: the same question from another API (reports-resource) is answered active: false, which is also the whole answer for a token that has expired, been revoked or never existed.

Follows: RFC 7662.

Recorded from the demo host (src/Fdyay.IdentityServer.Host) by the IntrospectionFlow test on every CI run. A value in braces, such as {access_token_1}, is one that differs on every run; the same placeholder is the same value wherever it appears. See how these pages are made.

sequenceDiagram
    participant Client
    participant AuthorizationServer as Authorization Server
    participant API
    participant ReportsAPI as Reports API
    Client->>AuthorizationServer: POST /connect/token
    AuthorizationServer-->>Client: 200 OK
    API->>AuthorizationServer: POST /connect/introspect
    AuthorizationServer-->>API: 200 OK
    ReportsAPI->>AuthorizationServer: POST /connect/introspect
    AuthorizationServer-->>ReportsAPI: 200 OK

1. The client gets a token for the API

The machine client, by client credentials, for api.

Client → Authorization Server

POST /connect/token HTTP/1.1
Authorization: Basic bWFjaGluZTpzZWNyZXQ=
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&scope=api

Authorization Server → Client

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Cache-Control: no-store, no-cache, max-age=0
Pragma: no-cache

{
  "access_token": "{access_token_1}",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "api"
}

{access_token_1} decoded — the header, then the payload; the signature is not shown:

{
  "alg": "RS256",
  "kid": "{key_id_1}",
  "typ": "at+jwt"
}
{
  "aud": [
    "api-resource"
  ],
  "iss": "http://localhost",
  "iat": {iat},
  "nbf": {iat},
  "exp": {iat + 3600},
  "jti": "{jti_1}",
  "client_id": "machine",
  "scope": "api"
}

2. The API introspects the token it was sent

API → Authorization Server

POST /connect/introspect HTTP/1.1
Authorization: Basic YXBpLXJlc291cmNlOmFwaS1zZWNyZXQ=
Content-Type: application/x-www-form-urlencoded

token={access_token_1}
&token_type_hint=access_token

Authorization Server → API

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Cache-Control: no-store, no-cache, max-age=0
Pragma: no-cache

{
  "active": true,
  "iss": "http://localhost",
  "iat": {iat},
  "nbf": {iat},
  "exp": {iat + 3600},
  "jti": "{jti_1}",
  "client_id": "machine",
  "scope": "api",
  "aud": "api-resource"
}

3. Another API asks about the same token

It is not addressed to reports-resource, so that API learns nothing.

Reports API → Authorization Server

POST /connect/introspect HTTP/1.1
Authorization: Basic cmVwb3J0cy1yZXNvdXJjZTpyZXBvcnRzLXNlY3JldA==
Content-Type: application/x-www-form-urlencoded

token={access_token_1}

Authorization Server → Reports API

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Cache-Control: no-store, no-cache, max-age=0
Pragma: no-cache

{
  "active": false
}