Did you know ... Search Documentation:
http_openid.pl -- OpenID consumer and server library
PublicShow source

This library implements the OpenID protocol (http://openid.net/). OpenID is a protocol to share identities on the network. The protocol itself uses simple basic HTTP, adding reliability using digitally signed messages.

Steps, as seen from the consumer (or relying partner).

  1. Show login form, asking for openid_identifier
  2. Get HTML page from openid_identifier and lookup <link rel="openid.server" href="server">
  3. Associate to server
  4. Redirect browser (302) to server using mode checkid_setup, asking to validate the given OpenID.
  5. OpenID server redirects back, providing digitally signed conformation of the claimed identity.
  6. Validate signature and redirect to the target location.

A consumer (an application that allows OpenID login) typically uses this library through openid_user/3. In addition, it must implement the hook http_openid:openid_hook(trusted(OpenId, Server)) to define accepted OpenID servers. Typically, this hook is used to provide a white-list of acceptable servers. Note that accepting any OpenID server is possible, but anyone on the internet can setup a dummy OpenID server that simply grants and signs every request. Here is an example:

:- multifile http_openid:openid_hook/1.

http_openid:openid_hook(trusted(_, OpenIdServer)) :-
    (   trusted_server(OpenIdServer)
    ->  true
    ;   throw(http_reply(moved_temporary('/openid/trustedservers')))


By default, information who is logged on is maintained with the session using http_session_assert/1 with the term openid(Identity). The hooks login/logout/logged_in can be used to provide alternative administration of logged-in users (e.g., based on client-IP, using cookies, etc.).

To create a server, you must do four things: bind the handlers openid_server/2 and openid_grant/1 to HTTP locations, provide a user-page for registered users and define the grant(Request, Options) hook to verify your users. An example server is provided in in <plbase>/doc/packages/examples/demo_openid.pl

Source openid_hook(+Action)[multifile]
Call hook on the OpenID management library. Defined hooks are:
Consider OpenID logged in.
Logout OpenID
True if OpenID is logged in
grant(+Request, +Options)
Server: Reply positive on OpenID
trusted(+OpenID, +Server)
True if Server is a trusted OpenID server
Called if the server provided AX attributes
x_parameter(+Server, -Name, -Value)
Called to find additional HTTP parameters to send with the OpenID verify request.
Source openid_login(+OpenID) is det
Associate the current HTTP session with OpenID. If another OpenID is already associated, this association is first removed.
Source openid_logout(+OpenID) is det
Remove the association of the current session with any OpenID
Source openid_logged_in(-OpenID) is semidet
True if session is associated with OpenID.
Source openid_user(+Request:http_request, -OpenID:url, +Options) is det
True if OpenID is a validated OpenID associated with the current session. The scenario for which this predicate is designed is to allow an HTTP handler that requires a valid login to use the transparent code below.
handler(Request) :-
      openid_user(Request, OpenID, []),

If the user is not yet logged on a sequence of redirects will follow:

  1. Show a page for login (default: page /openid/login), predicate reply_openid_login/1)
  2. By default, the OpenID login page is a form that is submitted to the verify, which calls openid_verify/2.
  3. openid_verify/2 does the following:
    • Find the OpenID claimed identity and server
    • Associate to the OpenID server
    • redirects to the OpenID server for validation
  4. The OpenID server will redirect here with the authetication information. This is handled by openid_authenticate/4.


(Local) URL of page to enter OpenID information. Default is the handler for openid_login_page/1
See also
- openid_authenticate/4 produces errors if login is invalid or cancelled.
Source openid_xrds(Request)[private]
Reply to a request for "Discovering OpenID Relying Parties". This may happen as part of the provider verification procedure. The provider will do a Yadis discovery request on openid.return or openid.realm. This is picked up by openid_user/3, pointing the provider to openid(xrds). Now, we reply with the locations marked openid and the locations that have actually been doing OpenID validations.
Source openid_login_page(+Request) is det[private]
Present a login-form for OpenID. There are two ways to redefine this default login page. One is to provide the option login_url to openid_user/3 and the other is to define a new handler for /openid/login using http_handler/3.
Source openid_login_form(+ReturnTo, +Options)// is det
Create the OpenID form. This exported as a separate DCG, allowing applications to redefine /openid/login and reuse this part of the page. Options processed:
URL of action to call. Default is the handler calling openid_verify/1.
Buttons is a list of img structures where the href points to an OpenID 2.0 endpoint. These buttons are displayed below the OpenID URL field. Clicking the button sets the URL field and submits the form. Requires Javascript support.

If the href is relative, clicking it opens the given location after adding 'openid.return_to' and `stay'.

If true, show a checkbox that allows the user to stay logged on.
Source prelogin_button(+Image)// is det[private]
Handle OpenID 2.0 and other pre-login buttons. If the image has a href attribute that is absolute, it is taken as an OpenID 2.0 endpoint. Otherwise it is taken as a link on the current server. This allows us to present non-OpenId logons in the same screen. The dedicated handler is passed the HTTP parameters openid.return_to and stay.
Source openid_verify(+Options, +Request)
Handle the initial login form presented to the user by the relying party (consumer). This predicate discovers the OpenID server, associates itself with this server and redirects the user's browser to the OpenID server, providing the extra openid.X name-value pairs. Options is, against the conventions, placed in front of the Request to allow for smooth cooperation with http_dispatch.pl. Options processes:
Specifies where the OpenID provider should return to. Normally, that is the current location.
Specifies the openid.trust_root attribute. Defaults to the root of the current server (i.e., http://host[.port]/).
Specifies the openid.realm attribute. Default is the trust_root.
Request the exchange of additional attributes from the identity provider. See http_ax_attributes/2 for details.

The OpenId server will redirect to the openid.return_to URL.

- http_reply(moved_temporary(Redirect))
Source stay(+Response)[private]
Called if the user ask to stay signed in. This is called before control is handed to the OpenID server. It leaves the data openid_stay_signed_in(true) in the current session.
Source handle_stay_signed_in(+OpenID)[private]
Handle stay_signed_in option after the user has logged on
Source assert_openid(+OpenIDLogin, +OpenID, +Server, +Target) is det[private]
Associate the OpenID as typed by the user, the OpenID as validated by the Server with the current HTTP session.
OpenIDLogin- Canonized OpenID typed by user
OpenID- OpenID verified by Server.
Source openid_server(?OpenIDLogin, ?OpenID, ?Server) is nondet
True if OpenIDLogin is the typed id for OpenID verified by Server.
OpenIDLogin- ID as typed by user (canonized)
OpenID- ID as verified by server
Server- URL of the OpenID server
Source public_url(+Request, +Path, -URL) is det[private]
True when URL is a publically useable URL that leads to Path on the current server.
Source openid_current_url(+Request, -URL) is det
Find the public URL for Request that we can make available to our identity provider. This must be an absolute URL where we can be contacted. Before trying a configured version through http_public_url/2, we try to see wether the login message contains a referer parameter or wether the browser provided one.
Source openid_current_host(Request, Host, Port)
Find current location of the server.
- New code should use http_current_host/4 with the option global(true).
Source redirect_browser(+URL, +FormExtra)[private]
Generate a 302 temporary redirect to URL, adding the extra form information from FormExtra. The specs says we must retain the search specification already attached to the URL.
Source openid_resolve(+URL, -OpenIDOrig, -OpenID, -Server, -ServerOptions)[private]
True if OpenID is the claimed OpenID that belongs to URL and Server is the URL of the OpenID server that can be asked to verify this claim.
URL- The OpenID typed by the user
OpenIDOrig- Canonized OpenID typed by user
OpenID- Possibly delegated OpenID
Server- OpenID server that must validate OpenID
ServerOptions- provides additional XRDS information about the server. Currently supports xrds_types(Types).
To be done
- Implement complete URL canonization as defined by the OpenID 2.0 proposal.
Source ssl_verify(+SSL, +ProblemCert, +AllCerts, +FirstCert, +Error)
Accept all certificates. We do not care too much. Only the user cares s/he is not entering her credentials with a spoofed side. As we redirect, the browser will take care of this.
Source openid_authenticate(+Request)[private]
HTTP handler when redirected back from the OpenID provider.
Source openid_authenticate(+Request, -Server:url, -OpenID:url, -ReturnTo:url) is semidet
Succeeds if Request comes from the OpenID server and confirms that User is a verified OpenID user. ReturnTo provides the URL to return to.

After openid_verify/2 has redirected the browser to the OpenID server, and the OpenID server did its magic, it redirects the browser back to this address. The work is fairly trivial. If mode is cancel, the OpenId server denied. If id_res, the OpenId server replied positive, but we must verify what the server told us by checking the HMAC-SHA signature.

This call fails silently if their is no openid.mode field in the request.

- openid(cancel) if request was cancelled by the OpenId server
- openid(signature_mismatch) if the HMAC signature check failed
Source signed_pairs(+FieldNames, +Pairs:list(Field-Value), +Form, -SignedPairs) is det[private]
Extract the signed field in the order they appear in FieldNames.
Source check_obligatory_fields(+SignedFields:list) is det[private]
Verify fields from obligatory_field/1 are in the signed field list.
- existence_error(field, Field)
Source check_authentication(+Request, +Form) is semidet[private]
Implement the stateless verification method. This seems needed for stackexchange.com, which provides the res_id with a new association handle.
Source ax_options(+ServerOptions, +Options, +AXAttrs) is det[private]
True when AXAttrs is a list of additional attribute exchange options to add to the OpenID redirect request.
Source ax_store(+Form)[private]
Extract reported AX data and store this into the session. If there is a non-empty list of exchanged values, this calls

If this hook fails, Values are added to the session data using http_session_assert(ax(Values)).

Source openid_server(+Options, +Request)
Realise the OpenID server. The protocol demands a POST request here.
Source associate_server(+Request, +Form, +Options)[private]
Handle the association-request. If successful, create a clause for server_association/3 to record the current association.
Source checkid_setup_server(+Request, +Form, +Options)[private]
Validate an OpenID for a TrustRoot and redirect the browser back to the ReturnTo argument. There are many possible scenarios here:
  1. Check some cookie and if present, grant immediately
  2. Use a 401 challenge page
  3. Present a normal grant/password page
  4. As (3), but use HTTPS for the exchange
  5. etc.

First thing to check is the immediate acknowledgement.

Source openid_grant(+Request)
Handle the reply from checkid_setup_server/3. If the reply is yes, check the authority (typically the password) and if all looks good redirect the browser to ReturnTo, adding the OpenID properties needed by the Relying Party to verify the login.
Source grant_login(+Request, +Options) is det[private]
Validate login from Request (can be used to get cookies) and Options, which contains at least:
  • identity(Identity)
  • password(Password)
  • trustroot(TrustRoot)
Source trusted(+OpenID, +Server)[private]
True if we trust the given OpenID server. Must throw an exception, possibly redirecting to a page with trusted servers if the given server is not trusted.
Source signed_fields(+Pairs, -Signed) is det[private]
Create a comma-separated atom from the field-names without 'openid.' from Pairs.
Source signature(+Pairs, +Association, -Signature)[private]
Determine the signature for Pairs
Source openid_associate(?URL, ?Handle, ?Assoc) is det
Calls openid_associate/4 as
openid_associate(URL, Handle, Assoc, []).
Source openid_associate(+URL, -Handle, -Assoc, +Options) is det
openid_associate(?URL, +Handle, -Assoc, +Options) is semidet
Associate with an open-id server. We first check for a still valid old association. If there is none or it is expired, we esstablish one and remember it. Options:
One of http://specs.openid.net/auth/2.0 (default) or http://openid.net/signon/1.1.
To be done
- Should we store known associations permanently? Where?
Source shared_secret(+Pairs, +P, +X, -Secret:list(codes))[private]
Find the shared secret from the peer's reply and our data. First clause deals with the (deprecated) non-encoded version.
Source expires_at(+Pairs, -Time) is det[private]
Unify Time with a time-stamp stating when the association exires.
Source associate_data(-Data, -P, -G, -X, +Options) is det[private]
Generate the data to initiate an association using Diffie-Hellman shared secret key negotiation.
Source random_bytes(+N, -Bytes) is det[private]
Bytes is a list of N random bytes (integers 0..255).
Source key_values_data(+KeyValues:list(Key-Value), -Data:list(code)) is det[private]
key_values_data(-KeyValues:list(Key-Value), +Data:list(code)) is det[private]
Encoding and decoding of key-value pairs for OpenID POST messages according to Appendix C of the OpenID 1.1 specification.
Source utf8_string(-Codes)// is nondet[private]
Take a short UTF-8 code-list from input. Extend on backtracking.
Source base64_btwoc(+Int, -Base64:list(code)) is det[private]
base64_btwoc(-Int, +Base64:list(code)) is det[private]
base64_btwoc(-Int, +Base64:atom) is det[private]
Source btwoc(+Integer, -Bytes) is det[private]
btwoc(-Integer, +Bytes) is det[private]
Translate between a big integer and and its representation in bytes. The first bit is always 0, as Integer is nonneg.
Source xor_codes(+C1:list(int), +C2:list(int), -XOR:list(int)) is det[private]
Compute xor of two strings.
- length_mismatch(L1, L2) if the two lists do not have equal length.