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.