# `open_id`

The `open_id` library provides a portable OpenID Connect client for the Authorization Code + PKCE flow. It supports provider discovery, authorization URL construction, authorization response parsing and state validation, authorization-code and refresh-token exchanges, UserInfo requests, RP-initiated logout URL construction, JWKS retrieval and caching, and ID-token verification for `RS256` and `ES256`.

The library uses `http_client` with `transport(http_process_transport)` for HTTP requests. HTTPS requests are made using `http_process_transport` TLS connections, which require the `openssl` command to be available unless overridden with the `openssl_executable/1` option.

This library can be used with backend Prolog systems that support unbound integer arithmetic and the `sockets` library: ECLiPSe, SICStus Prolog, SWI-Prolog, Trealla Prolog, and XVM.

## Loading

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

    | ?- logtalk_load(open_id(loader)).

## Testing

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

    | ?- logtalk_load(open_id(tester)).

To run the optional live tests against Google’s public OpenID Connect discovery and JWKS endpoints, load the `tester_live.lgt` file:

    | ?- logtalk_load(open_id(tester_live)).

The Google discovery and JWKS live tests do not require credentials. The optional Google ID-token verification live test requires a fresh Google-issued ID token and its expected audience:

    $ export LOGTALK_OPEN_ID_GOOGLE_ID_TOKEN="..."
    $ export LOGTALK_OPEN_ID_GOOGLE_AUDIENCE="..."

If the ID token includes a nonce claim, also define the `LOGTALK_OPEN_ID_GOOGLE_NONCE` environment variable.

The live tests also include an end-to-end Authorization Code + PKCE flow against the community-run `https://oidctest.wsweet.org/` OpenID Connect test provider, using its documented test account `dwho`/`dwho` and pre-registered client `private`/`tardis`. This provider has no availability guarantees.

## Basic usage

    | ?- open_id::discover('https://issuer.example', Provider, []),
         open_id::authorization_url(
             Provider,
             authorization_request([
                 client_id('client-id'),
                 redirect_uri('https://client.example/callback'),
                 scope([openid, profile])
             ]),
             URL,
             Session,
             []
         ).

After redirecting the user to `URL` and receiving the callback URL:

    | ?- open_id::authorization_code(CallbackURL, Session, Code, []),
         open_id::exchange_code(
             Provider,
             Code,
             Session,
             Tokens,
             [client_authentication(client_secret_basic('client-secret'))]
         ),
         Tokens = tokens(Properties),
         member(id_token(IDToken), Properties),
         open_id::verify_id_token(IDToken, Provider, Claims,
             [expected_audience('client-id')]).

To call the UserInfo endpoint with the resulting access token:

    | ?- Tokens = tokens(Properties),
         member(access_token(AccessToken), Properties),
         open_id::userinfo(Provider, AccessToken, UserInfo, []).

To use a refresh token:

    | ?- open_id::refresh_token(
             Provider,
             RefreshToken,
             RefreshedTokens,
             [
                 client_id('client-id'),
                 client_authentication(client_secret_basic('client-secret'))
             ]
         ).

To build an RP-initiated logout URL:

    | ?- open_id::logout_url(
             Provider,
             logout_request([
                 id_token_hint(IDToken),
                 post_logout_redirect_uri('https://client.example/logout/callback'),
                 state('logout-state')
             ]),
             LogoutURL,
             []
         ).

## Current scope

- OpenID Provider discovery.

- Authorization Code + PKCE (`S256`) URL construction.

- Authorization response parsing and state validation.

- Authorization-code token exchange for public clients, `client_secret_post/1`, and `client_secret_basic/1`.

- Refresh-token exchange and UserInfo retrieval.

- RP-initiated logout URL construction.

- JWKS retrieval, caching, and refresh-on-unknown-`kid` key rotation.

- ID-token JWT verification for `RS256` and `ES256` using OpenSSL.

- Claim validation for issuer, subject, audience, authorized party, nonce, expiration, not-before, and issued-at tolerance.

## Current limitations

- Device authorization, dynamic client registration, browser launching, and callback server ownership are outside the current scope.

- ID-token verification requires the `openssl` command.