.. _library_http_oauth:

``http_oauth``
==============

This library implements OAuth protected-resource support for normalized
HTTP:

- RFC 6750 Bearer Authorization and challenge parsing/generation
- staged token authentication and scope authorization
- resource-aware verifier and pluggable scope-checker protocols
- RFC 9728 protected-resource metadata documents and public endpoint
  handling
- RFC 7662 token introspection with Basic, Bearer, or custom-header
  client authentication
- JWT access-token verification using the ``jwt`` library
- generic handler and ``http_router`` integration

API documentation
-----------------

Open the
`../../apis/library_index.html#http-oauth <../../apis/library_index.html#http-oauth>`__
link in a web browser.

Loading
-------

To load the library, load the ``loader.lgt`` file:

::

   | ?- logtalk_load(http_oauth(loader)).

Testing
-------

To test this library, load the ``tester.lgt`` file:

::

   | ?- logtalk_load(http_oauth(tester)).

Usage
-----

Protected-resource and verifier contract
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Every authentication call requires a canonical
``protected_resource(Identifier)`` option. Use the externally visible
HTTPS URL that identifies the protected resource and use the same atom
consistently in metadata, token audiences, and protection options.

A verifier is any object implementing ``http_oauth_verifier_protocol``.
Its ``verify/3`` predicate receives the raw token and expected
protected-resource identifier. It must fail for an invalid, inactive,
expired, or wrong-audience token and return normalized information on
success:

::

   :- object(my_token_verifier,
       implements(http_oauth_verifier_protocol)).

       verify(Token, ProtectedResource, oauth_token_info([
           source(my_authorization_server),
           scopes(Scopes),
           audience_validation(exact(ProtectedResource)),
           claims(Claims)
       ])) :-
           % Verify Token and bind Scopes and Claims here.
           ...

   :- end_object.

The library adds the resulting ``oauth_token_info/1`` and
``oauth_scopes/1`` properties to the normalized request. The raw Bearer
token is deliberately not retained. Handlers can query these properties
using ``token_info/2``, ``scopes/2``, or the ``http_core::property/2``
predicate.

Protecting a request with a static scope policy
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Use ``protect_request/4`` when the required scopes are known before
routing or dispatch. The default ``http_oauth_exact_scope_checker``
requires every listed scope and compares scope atoms exactly:

::

   http_oauth::protect_request(Request0, my_token_verifier, Action, [
       protected_resource('https://api.example.com/orders'),
       required_scopes([orders_read])
   ]),
   ( Action = continue(Request) ->
       orders_handler::handle(Request, Response)
   ; Action = respond(Response)
   ).

Missing credentials produce a 401 response, malformed authorization data
a 400 response, invalid tokens a 401 response, and insufficient scopes a
403 response. Optional ``realm/1``, ``resource_metadata/1``,
``headers/1``, ``body/1``, and ``properties/1`` options customize
generated error responses. Supply a ``scope_checker/1`` object
implementing ``http_oauth_scope_checker_protocol`` for policies other
than exact all-scopes matching.

Separating authentication from authorization
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Use the staged predicates when routing, operation selection, or
request-body inspection determines the required scopes. Authenticate
first, resolve the operation, and then authorize the annotated request:

::

   http_oauth::authenticate_request(Request0, my_token_verifier, Authentication, [
       protected_resource('https://api.example.com/orders')
   ]),
   ( Authentication = continue(AuthenticatedRequest) ->
       required_operation_scopes(AuthenticatedRequest, RequiredScopes),
       http_oauth::authorize_request(AuthenticatedRequest, Action, [
           required_scopes(RequiredScopes)
       ])
   ; Action = Authentication
   ).

This form is useful for streaming servers because scope authorization
can be completed before response headers are committed. Pass any
response-decoration options needed by each stage to that stage.

Wrapping an HTTP handler
~~~~~~~~~~~~~~~~~~~~~~~~

The parametric ``http_server_core_oauth_handler/3`` object applies a
static policy around any object implementing ``http_handler_protocol``:

::

   http_server_core_oauth_handler(
       my_token_verifier,
       orders_handler,
       [
           protected_resource('https://api.example.com/orders'),
           required_scopes([orders_read])
       ]
   )::handle(Request, Response).

The wrapped handler receives the annotated request only after
authentication and authorization succeed. Otherwise, the wrapper returns
the generated OAuth response without calling the handler.

Applying route-specific policies
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Objects importing ``http_router`` can also import the parametric
``http_router_oauth/2`` category. Base options define the protected
resource and shared policy; an ``oauth/1`` route metadata property
overrides options for an individual route:

::

   :- object(api,
       implements(http_handler_protocol),
       imports([
           http_router,
           http_router_oauth(my_token_verifier, [
               protected_resource('https://api.example.com')
           ])
       ])).

       route(list_orders, get, '/orders', list_orders).
       route_metadata(list_orders, [oauth([required_scopes([orders_read])])]).

       authorize_routed_request(Request, Action) :-
           ^^authorize_oauth_request(Request, Action).

Routes without ``oauth/1`` metadata remain public. Route options
override base options by option name and arity.

Publishing protected-resource metadata
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

``http_oauth_metadata::well_known_url/2`` derives the RFC 9728 endpoint
from a canonical HTTPS resource identifier. For example,
``https://api.example.com/orders`` maps to
``https://api.example.com/.well-known/oauth-protected-resource/orders``.

Use ``document/3-4`` to build a JSON term or ``response/3-4`` to build a
normalized HTTP response:

::

   http_oauth_metadata::response(
       'https://api.example.com/orders',
       [
           authorization_servers(['https://identity.example.com']),
           scopes_supported([orders_read, orders_write]),
           resource_name('Orders API'),
           resource_documentation('https://api.example.com/docs/orders')
       ],
       Response,
       [required_members([authorization_servers])]
   ).

Identifiers and URL-valued descriptors must use HTTPS. Use
``extension(Name, Value)`` for non-standard members; registered metadata
names cannot be supplied as extensions. The
``http_server_core_oauth_endpoint_handler/6`` wrapper combines public
metadata publication with a protected application handler. It serves
only ``GET`` at the exact derived well-known path, returns 405 for other
methods at that path, and protects every other request.

Verifying opaque tokens by introspection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Use the parametric ``http_oauth_introspection_verifier/2`` when access
tokens are validated by an RFC 7662 endpoint:

::

   Verifier = http_oauth_introspection_verifier(
       'https://identity.example.com/oauth/introspect',
       [authentication(basic(client_id, client_secret))]
   ),
   http_oauth::protect_request(Request, Verifier, Action, [
       protected_resource('https://api.example.com/orders'),
       required_scopes([orders_read])
   ]).

The introspection client also supports ``authentication(bearer(Token))``
and ``authentication(headers(Headers))``, plus ``token_type_hint/1``,
custom ``headers/1``, and underlying ``http_options/1``. Introspection
endpoints require HTTPS. The ``allow_insecure_localhost(true)`` option
exists only for local tests and accepts loopback HTTP endpoints.

By default, the verifier requires the introspection ``aud`` value to
contain the protected-resource identifier. Use
``audience_validation(trust_active)`` only when the authenticated
introspection service guarantees that ``active=true`` is already
specific to the requested resource. The inbound Bearer token must never
be reused as client authentication or forwarded to downstream APIs.

Verifying JWT access tokens locally
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Use ``http_oauth_jwt_verifier/2`` for signed JWT access tokens. Its
first parameter is any symmetric key, public JWK, or JWK Set accepted by
``jwt::verify/4``; its second parameter is the corresponding JWT
verification option list:

::

   Verifier = http_oauth_jwt_verifier(PublicJWKSet, [
       allow_algorithms(['RS256']),
       claim_policy([
           claim(iss, expected('https://identity.example.com'))
       ])
   ]),
   http_oauth::protect_request(Request, Verifier, Action, [
       protected_resource('https://api.example.com/orders')
   ]).

The verifier requires an ``aud`` claim matching the protected resource.
A space-delimited ``scope`` claim is normalized to a list of atoms; if
it is absent, the granted scope list is empty. Signature,
registered-claim, and time validation behavior is configured using the
``jwt`` library options.

Parsing headers and building OAuth responses
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Use ``authorization/2`` and ``challenge/2`` with normalized HTTP
messages, or the ``parse_authorization/2``,
``generate_authorization/2``, ``parse_challenge/2``, and
``generate_challenge/2`` predicates with individual header values.
Bearer terms use the forms ``bearer_authorization(Token)`` and
``bearer_challenge(Fields)``.

The ``unauthorized_response/3`` and ``forbidden_response/3`` predicates
construct new normalized responses. Their arity-four variants decorate
an existing response while preserving its HTTP version and any body,
headers, and properties not overridden by options. These helpers are
useful when an application needs to add OAuth challenges to its own
error representation.

MCP over HTTP
~~~~~~~~~~~~~

OAuth protection applies to MCP HTTP transports. It does not apply to
MCP stdio transports, and an MCP session identifier never substitutes
for per-request Bearer authentication. Every protected HTTP request must
therefore carry credentials accepted by the configured verifier.

For MCP protected-resource metadata, use
``required_members([authorization_servers])``. The
``http_server_core_oauth_endpoint_handler/6`` wrapper is the simplest
way to serve that metadata publicly at the exact RFC 9728 well-known
path while protecting the MCP handler at all other paths. Use staged
authentication and authorization when the MCP method determines the
required scopes, so an insufficient-scope response can be returned
before starting a streamed MCP response.

Limitations
-----------

- OAuth protection applies only to HTTP transports, not MCP stdio
  transports.
- Bearer tokens are accepted only in the HTTP ``Authorization`` header.
- The library does not implement authorization-server discovery, token
  acquisition, token refresh, or token revocation.
- Token introspection is synchronous and does not provide built-in
  caching.
- JWT verification is limited to the algorithms and capabilities
  supported by the ``jwt`` library and the selected backend.
- Protected-resource metadata and token introspection require HTTPS,
  except for the explicit introspection loopback override intended for
  local tests.
- The default scope policy requires exact matching of all required
  scopes; alternative policies require a custom scope-checker object.
