How to verify opaque tokens with introspection

View Source

Problem

Your bearer tokens are opaque reference tokens, not self-contained JWTs, so you cannot verify them locally. You need to ask the authorization server whether a token is still valid (RFC 7662).

Solution

livery_auth_introspect POSTs the token to the introspection endpoint, authenticates this resource server with HTTP Basic, and trusts the active field of the JSON response. On success the response (with scope, sub, exp, ...) is stored under meta(user, _):

Stack = [
    {livery_auth_introspect, #{
        endpoint      => <<"https://issuer.example/oauth/introspect">>,
        client_id     => <<"my-api">>,
        client_secret => <<"s3cret">>
    }}
    %% ... handler
].

Read the claims in a handler with livery_ext:user/1:

fun(Req) ->
    #{<<"sub">> := Sub, <<"scope">> := Scope} = livery_ext:user(Req),
    livery_resp:text(200, [<<"hello ">>, Sub, <<" (">>, Scope, <<")">>])
end.

A missing token is rejected with 401 unless required => false. An inactive token (or any transport/decoding failure) is always rejected with 401 and a WWW-Authenticate: Bearer header.

JWT vs. introspection

Token kindUse
Self-contained JWTlivery_auth_bearer (local verify, no round trip)
Opaque / referencelivery_auth_introspect (round trip per request)

Introspection adds a network call per request. Cache results in your own layer if the round trip is too costly.

Custom HTTP client

The call is pluggable. Pass fetch => fun((Url, Headers, Body) -> {ok, Status, Body} | {error, _}) to use your own client or to test without a network:

#{endpoint => Endpoint,
  fetch => fun(_U, _H, _B) -> {ok, 200, <<"{\"active\":true}">>} end}

See also