     [file version.texi not included]

This is the manual of disfluid (version VERSION, UPDATED), an implementation of the Solid authentication protocol for guile, client and server.

Copyright © 2020, 2021 Vivien Kraus

     Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''

Disfluid
********

Disfluid is an independent implementation of a web stack focusing on interoperability. In this implementation, the users control what programs run in their computers. They also choose who to trust for online data storage and processing, without needing any permission, or can self-host their data.

The software is available at https://labo.planete-kraus.eu/webid-oidc.git. The latest commit is tracked in the Guix channel https://labo.planete-kraus.eu/webid-oidc-channel.git.

A PDF version of this manual is available at https://disfluid.planete-kraus.eu/disfluid.pdf.

Decentralized Authentication on the Web
***************************************

Authentication on the web is currently handled in the following way: anyone can install a server that will authenticate users on the web. The problem is interoperability. If a client (an application) wants to authenticate a user, it has to be approved by the authentication server. In other words, if USEFUL-PROGRAM wants to authenticate MEGACORP users, then USEFUL-PROGRAM has to register to MEGACORP first, and get approved. This goes against the principle of permission-less innovation, which is at the heart of the web.

In the decentralized authentication web, the best attempt so far is that of ActivityPub. All servers are interoperable with respect to authentication: if user A emits an activity, it is forwarded by A's server to its recipients, and A's server is responsible for A's identity.

The problem with that approach is that the data is tied to the application. It is not possible to use another application to process the data differently, or to use multiple data sources, in an interoperable way (without the ActivityPub server knowing). This means that on Activitypub, microblogging applications will not present different activities correctly. This also means that it is difficult to write a free replacement to a non-free application program, because it would need to manage the data.

In the Solid ecosystem, there is a clear distinction between servers and applications. An application is free to read data from all places at the same time, using a permission-less authentication system. Since the applications do not need to store data, the cost of having users is neglectible, so users do not need prior approval before using them (making captchas and the like a thing of the past). Servers do not have a say in which applications the user uses.

The authentication used is a slight modification of the well-established OpenID Connect. It is intended to work in a web browser, but this package demonstrates that it also works without a web browser.

Invoking disfluid
*****************

The disfluid program provides different modes of operations:

reverse-proxy
     Run an authenticating reverse proxy. With this command, you specify a backend server. When an authenticated user makes a request, you receive an additional header containing the user’s identity.

identity-provider
     Run the identity provider only.

client-service
     The client applications must serve some resources: namely, the client manifest and the redirect URI.

server
     Run both an identity provider and a resource server.

The server is configured with command-line arguments, and environment variables.

General options
===============

The server will respond to -h and -v commands, to get the help output and the version information.

The server output (command-line, logs) are localized for the system administrator. You can control it with the LANG environment variable. So if your locale is not English, you can have the same commands as in this manual by running with LANG=C.

The programs respect the XDG_DATA_HOME and XDG_CACHE_HOME to store persistent data and disposable data. The cache directory can be deleted at any time. If one of these variables is not set, its value is computed from the HOME environment variable.

General server configuration
============================

All servers are published under the Affero GPL, which means that the service provider needs to publish all changes made to the program to users over the network. The disfluid command provides a --complete-corresponding-source option so that the system administrator can specify a means to download the source.

The servers will add a Source: header in each response, containing the value of this configuration option.

The servers can be configured to redirect output and errors to a log file and an error file, with the --log-file and --error-file options.

The server will listen to port 8080 by default, but this may be configured with --port. Since the servers do not support TLS, and they only support HTTP/1.1, they are intended to run behind a reverse proxy (even for the authenticating reverse proxy).

Finally, the servers are required to know their public name. This is configured with the --server-name option.

The server will make requests on the world-wide web, for instance to download client manifests. The requests can be redirected with XML Catalog, by setting the XML_CATALOG_FILES to a space-separated list of URIs (can be file: URIs). The requests cannot be directed to the file system.

Configuration for the resource server
=====================================

The reverse proxy sets an identity header to authenticated requests. By default, it is XXX-Agent, but it can be configured with --header.

The reverse proxy is configured to contact a backend URI with --backend-uri. This backend URI should not be directly exposed, because a malicious user could set the identity header.

Configuration for the identity provider
=======================================

The identity provider can only handle one user. If you want to handle multiple users, it is highly advised to use a different host name for each user, in case the server is accessed from a web browser. You can set the identity of the user with --subject, and write the user’s password in a file. Pass the file name with --encrypted-password-file. You can pass the encrypted password directly with --encrypted-password, but the encrypted password will be public.

The encrypted password format is defined by the crypt function in the C library. For glibc, it looks like this: $N$SALT$HASH, where N is the algorithm identifier, SALT is the password salt annd HASH is its hash.

The server uses a key, which is not the same thing as the TLS certificate of the server (remember, the servers don’t support TLS). It is in the JWK format. You set its file name with --key-file. If the key file does not exist, it will be generated.

Finally, the public openid configuration requires you to set the JWKS URI (--jwks-uri), authorization endpoint URI (--authorization-endpoint-uri) and token endpoint URI (--token-endpoint-uri). The identity provider will publish the full URIs, but will respond to their path, regardless of the host.

Configuration for the client service
====================================

The client will serve a stupid page for the redirect URI that will only display the authorization code. The redirect URI is set with --redirect-uri.

The client ID is set with --client-id. This is the URI under which the client registrationn is served.

Finally, you can set some cosmetic options, but since it can confuse the user, they are hidden by default by the identity provider.

--client-name
     set the name of the application.

--client-uri
     set an URI where to find more information about the client.

Running disfluid with GNU Guix
******************************

The channel at https://labo.planete-kraus.eu/webid-oidc-channel.git can be used with guix. It defines the package at the latest commit, and a service definition in (vkraus services disfluid).

 -- service type: disfluid-service-type
     This service runs a bunch of disfluid servers with the disfluid system user, each with a unique name. The value it takes is an alist of service configurations: the keys are unique names (to differenciate the generated shepherd services), and the values are configuration records for an issuer, reverse proxy, server, or client service.

 -- configuration record: <disfluid-issuer-configuration> [DISFLUID] COMPLETE-CORRESPONDING-SOURCE ISSUER KEY-FILE SUBJECT ENCRYPTED-PASSWORD-FILE JWKS-URI AUTHORIZATION-ENDPOINT-URI TOKEN-ENDPOINT-URI PORT [EXTRA-OPTIONS]
     The configuration for the identity provider. The optional DISFLUID argument is the package containing the binary to run, if you want to apply some patches, and EXTRA-OPTIONS is an empty list by default.

 -- configuration record: <disfluid-reverse-proxy-configuration> [DISFLUID] COMPLETE-CORRESPONDING-SOURCE PORT INBOUND-URI OUTBOUND-URI HEADER [EXTRA-OPTIONS]
     This record configures an authenticating reverse proxy.

 -- configuration record: <disfluid-client-service-configuration> [DISFLUID] COMPLETE-CORRESPONDING-SOURCE CLIENT-ID REDIRECT-URI [CLIENT-NAME] [CLIENT-URI] PORT [EXTRA-OPTIONS]
     This record configures a server to serve public application pages.

 -- configuration record: <disfluid-server-configuration> [DISFLUID] COMPLETE-CORRESPONDING-SOURCE SERVER-NAME KEY-FILE SUBJECT ENCRYPTED-PASSWORD-FILE JWKS-URI AUTHORIZATION-ENDPOINT-URI TOKEN-ENDPOINT-URI PORT [EXTRA-OPTIONS]
     The configuration for the full server.

Common parameters
*****************

The (webid-oidc parameters) module provides a set of Guile parameter to control the program behavior.

 -- parameter: data-home
     This parameter controls the location where the program stores persistent data. By default, it is located in XDG_DATA_HOME.

 -- parameter: cache-home
     This parameter controls the location where the program stores data that might get deleted at any time. By default, it uses XDG_CACHE_HOME.

 -- parameter: current-date
     This parameter is a thunk similar to SRFI-19 current-date, except it can be set with a thunk returning a date, time or number of seconds, or a date, time or number of seconds.

 -- parameter: anonymous-http-request
     This parameter is a function similar to the http-request function in (web client).

 -- parameter: authorization-code-default-validity
     This parameter controls the number of seconds for which an authorization code is valid at creation time.

 -- parameter: oidc-token-default-validity
     This parameter controls the number of seconds for which an ID token or access token is valid at creation time.

 -- parameter: dpop-proof-validity
     This parameter controls the number of seconds for which a DPoP proof is valid after it has been issued.

Managing keys
*************

Some functions require a key, or a key pair, to operate. The (webid-oidc jwk) module provides you with everything required to manage keys.

 -- Class: <private-key> () ALG
     This is the base class for a private key. You need it to issue signatures. Signatures issued with this key will use ALG for the signature algorithm, but the public key associated with this private key will verify signatures in any compatible algorithm, not just ALG.

     ALG is a symbol, for instance 'RS256.

 -- Class: <public-key> ()
     This is the base class for a public key. You need it to check signatures.

 -- Class: <key-pair> () PUBLIC-KEY PRIVATE-KEY
     A key pair contains a PUBLIC-KEY and a matching PRIVATE-KEY. You use this form for keys you own.

 -- Class: <rsa-key-pair> () (<key-pair>)
     This key pair contains matching RSA keys.

 -- Class: <ec-key-pair> () (<key-pair>) CRV
     This key pair contains matching elliptic curve keys. CRV is a symbol identifiying the curve.

 -- Class: <rsa-private-key> (<private-key>) D P Q DP DQ QI
 -- Class: <rsa-public-key> (<public-key>) N E
 -- Class: <ec-scalar> (<private-key>) CRV Z
 -- Class: <ec-point> (<public-key>) CRV X Y
     All fields are strings, base64 encoding the parameters, except CRV, which is a symbol.

 -- Class: <jwks> () KEYS
     An identity provider may use different keys that are in validity to sign different access tokens. The JWKS encapsulates many public KEYS.

 -- Generic method: <public-key> public-key (KEY <key-pair>)
 -- Generic method: <public-key> public-key (KEY <public-key>)
     Return the public part of KEY, which may either be a key pair or a public key.

 -- Generic method: <private-key> private-key (KEY <key-pair>)
 -- Generic method: <private-key> private-key (KEY <private-key>)
     Return the private part of KEY.

 -- Generic method: <string> rsa-d (KEY <rsa-key-pair>)
 -- Generic method: <string> rsa-d (KEY <rsa-private-key>)
 -- Generic method: <string> rsa-p (KEY <rsa-key-pair>)
 -- Generic method: <string> rsa-p (KEY <rsa-private-key>)
 -- Generic method: <string> rsa-q (KEY <rsa-key-pair>)
 -- Generic method: <string> rsa-q (KEY <rsa-private-key>)
 -- Generic method: <string> rsa-dp (KEY <rsa-key-pair>)
 -- Generic method: <string> rsa-dp (KEY <rsa-private-key>)
 -- Generic method: <string> rsa-dq (KEY <rsa-key-pair>)
 -- Generic method: <string> rsa-dq (KEY <rsa-private-key>)
 -- Generic method: <string> rsa-qi (KEY <rsa-key-pair>)
 -- Generic method: <string> rsa-qi (KEY <rsa-private-key>)
 -- Generic method: <string> rsa-n (KEY <rsa-key-pair>)
 -- Generic method: <string> rsa-n (KEY <rsa-public-key>)
 -- Generic method: <string> rsa-e (KEY <rsa-key-pair>)
 -- Generic method: <string> rsa-e (KEY <rsa-public-key>)
 -- Generic method: <symbol> ec-crv (KEY <ec-key-pair>)
 -- Generic method: <symbol> ec-crv (KEY <ec-point>)
 -- Generic method: <symbol> ec-crv (KEY <ec-scalar>)
 -- Generic method: <string> ec-x (KEY <ec-key-pair>)
 -- Generic method: <string> ec-x (KEY <ec-point>)
 -- Generic method: <string> ec-y (KEY <ec-key-pair>)
 -- Generic method: <string> ec-y (KEY <ec-point>)
 -- Generic method: <string> ec-z (KEY <ec-key-pair>)
 -- Generic method: <string> ec-z (KEY <ec-scalar>)
 -- Generic method: <symbol> alg (KEY <key-pair>)
 -- Generic method: <symbol> alg (KEY <private-key>)
     Key parameter getters.

 -- Generic method: <list> keys (JWKS <jwks>)
     Return all the public keys used by JWKS.

 -- Generic method: <undefined> check-key (KEY <public-key>)
 -- Generic method: <undefined> check-key (KEY <private-key>)
 -- Generic method: <undefined> check-key (KEY <key-pair>)
     Check that the KEY parameters are consistent.

When exchanging keys, maybe you will have them in the form of a JWK: an alist from symbols to strings, as a representation for a JSON object.

 -- Generic method: <list> key->jwk (KEY <public-key>)
 -- Generic method: <list> key->jwk (KEY <private-key>)
 -- Generic method: <list> key->jwk (KEY <key-pair>)
     Return an alist with known parameter names for JSON.

 -- function: jwk->key JWK
     Parse JWK as a key or a key pair.

 -- Generic method: <symbol> kty (KEY <rsa-key-pair>)
 -- Generic method: <symbol> kty (KEY <rsa-public-key>)
 -- Generic method: <symbol> kty (KEY <rsa-private-key>)
 -- Generic method: <symbol> kty (KEY <ec-key-pair>)
 -- Generic method: <symbol> kty (KEY <ec-point>)
 -- Generic method: <symbol> kty (KEY <ec-scalar>)
     Return 'RSA for RSA keys, or 'EC for elliptic curve keys.

 -- Generic method: <string> jkt (KEY <key-pair>)
 -- Generic method: <string> jkt (KEY <public-key>)
     Hash the KEY parameters in a reproducible order to get the hash of a key.

 -- function: generate-key [#:N-SIZE] [#:E-SIZE] [#:E=\"AQAB\"] [#:CRV]
     Generate a new key pair.

 -- Generic method: <values> serve (JWKS <jwks>) EXPIRATION-DATE
     Return a response and response body for serving JWKS. Client-side caching is very much necessary for a JWKS, so pass EXPIRATION-DATE as a SRFI-19 date to define a maximum date for caching. It should be in the future, for instance in 1 hour.

 -- function: get-jwks URI [#:HTTP-REQUEST]
     Download a JWKS on the web at URI. Use HTTP-REQUEST, with the same interface as that of (web client), to actually get the JWKS.

 -- Exception type: &not-a-jwk
     If the key parameters are incorrect, this exception is raised.

 -- Exception type: &not-a-jwks
     If the JWKS cannot be downloaded, or is incorrect, this exception is raised.

OIDC discovery
**************

An identity provider is known by its server name. The different endpoints can be discovered from there.

 -- Class: <oidc-configuration> () JWKS-URI AUTHORIZATION-ENDPOINT TOKEN-ENDPOINT
     The OIDC configuration for an identity provider. JWKS-URI, AUTHORIZATION-ENDPOINT adn TOKEN-ENDPOINT are all URIs.

     You can construct an OIDC configuration two different ways:

       * by passing #:JWKS-URI, #:AUTHORIZATION-ENDPOINT and #:TOKEN-ENDPOINT to the constructor;

       * by passing #:SERVER, and optionally #:HTTP-REQUEST to the constructor, to query the SERVER for its configuration.

 -- Exception type: &invalid-oidc-configuration
     This exception is raised when the configuration is unusable or incomplete.

 -- function: make-invalid-oidc-configuration
     Constructor for the &invalid-oidc-configuration exception type.

 -- function: invalid-oidc-configuration? EXCEPTION
     Check whether EXCEPTION was raised because of an invalid OIDC configuration.

 -- Generic: jwks-uri OIDC-CONFIGURATION
     Return the JWKS uri of OIDC-CONFIGURATION.

 -- Generic: jwks OIDC-CONFIGURATION
     Query the JWKS uri of OIDC-CONFIGURATION.

 -- Generic: authorization-endpoint OIDC-CONFIGURATION
     Return the authorization endpoint of OIDC-CONFIGURATION.

 -- Generic: token-endpoint OIDC-CONFIGURATION
     Return the token endpoint of OIDC-CONFIGURATION.

 -- Generic: serve CONFIGURATION EXPIRATION-DATE
     Return 2 values: the response, and response body, needed to serve CONFIGURATION. It is very much recommended to let clients cache this value. They will not revalidate it until after EXPIRATION-DATE, a SRFI-19 date.

Client manifest
***************

To make sure that a client application is legitimate, it is mandated that it serves a public document under its ID URI, and that document should confirm the URI and the redirection URI, where the client application gets the authorization code.

 -- Class: <client-manifest> () CLIENT-ID REDIRECT-URIS
     This is the class encapsulating a very basic client manifest. CLIENT-ID is an URI, and REDIRECT-URIS is a list of URIs.

     You can construct one by providing both #:CLIENT-ID and #:REDIRECT-URIS, or by providing only #:CLIENT-ID, in which case it will be downloaded from the web.

Clients that cannot serve pages should use the anonymous client ID, that accepts all redirect URIs.

 -- Generic: client-id MANIFEST
     Return the client ID of MANIFEST.

 -- Generic: redirect-uris MANIFEST
     Return the list of accepted redirection URIs for MANIFEST.

 -- Generic: ->json-data MANIFEST
     Convert MANIFEST to JSON data (alists for objects, vectors for arrays). You should override this method if you design an extended client manifest class.

 -- Generic: check-redirect-uri MANIFEST URI
     Check that MANIFEST controls URI, where to send the authorization code. Raises an exception if that’s not the case.

 -- Exception type: &invalid-client-manifest
     This exception is raised when the client manifest is invalid.

 -- function: make-invalid-client-manifest
     Constructor for the &invalid-client-manifest exception type.

 -- function: invalid-client-manifest? EXCEPTION
     Check whether EXCEPTION was raised because of an invalid client manifest.

 -- Exception type: &unauthorized-redirect-uri
     This exception is raised when the requested authorization URI is unauthorized.

 -- function: make-unauthorized-redirect-uri
     Constructor for the &unauthorized-redirect-uri exception type.

 -- function: unauthorized-redirect-uri? EXCEPTION
     Check whether EXCEPTION was raised because of an unauthorized redirection URI.

 -- Exception type: &inconsistent-client-manifest
     This exception is raised when the client ID does not match what the client manifest says.

 -- function: make-inconsistent-client-manifest
     Constructor for the &inconsistent-client-manifest exception type.

 -- function: inconsistent-client-manifest? EXCEPTION
     Check whether EXCEPTION was raised because of an inconsistent client manifest.

 -- Exception type: &cannot-serve-public-manifest
     This exception is raised when the manifest to serve has the public client URI as ID.

 -- function: make-cannot-serve-public-manifest
     Constructor for the &cannot-serve-public-manifest exception type.

 -- function: cannot-serve-public-manifest? EXCEPTION
     Check whether EXCEPTION was raised because the server wants to serve a public manifest.

 -- Exception type: &cannot-fetch-client-manifest
     This exception is raised when the server does not behave correctly when fetching the manifest.

 -- function: make-cannot-fetch-client-manifest
     Constructor for the &cannot-fetch-client-manifest exception type.

 -- function: cannot-fetch-client-manifest? EXCEPTION
     Check whether EXCEPTION was raised because we could not fetch a client manifest.

The Json Web Token
******************

The Json Web Token, or JWT, is a terse representation of a pair of JSON objects: the header, and the payload. The JWT can be encoded as a Json Web Signature (JWS), in which case the header is encoded to base64 with the URL alphabet, and without padding characters, the payload is also encoded to base64, and the concatenation of the encoding of the header, a dot, and the encoding of the payload is signed with some cryptography algorithm. In the following, we will only be interested by public-key cryptography. The concatenation of header, dot, payload, dot and signature in base64 is the encoding of the JWT.

Tokens
======

The (webid-oidc jws) implements some functionality for tokens.

 -- Class: <token> () ALG
     The base class for all tokens. It only knows the signature ALGorithm. You can construct one in different ways:

       * the #:ALG construct keyword supports a string or a keyword as a value, containing a valid JWA identifier, such as RS256;

       * the #:SIGNING-KEY keyword defines the key that will serve to sign the token. The signature algorithm is set to the default of SIGNING-KEY;

       * the #:JWT-HEADER and #:JWT-PAYLOAD keywords let you pass two alists, following the JSON representation from srfi-180: objects are alists of symbols to values, arrays are vectors.

 -- Exception type: &invalid-jws
     This exception is raised when a JWT cannot be parsed or constructed as a JWS.

 -- function: make-invalid-jws
     Construct an exception of type &invalid-jws.

 -- function: invalid-jws? EXCEPTION
     Check whether EXCEPTION was raised because of an invalid JWS.

There are multiple things you can do with a token.

 -- Generic: alg TOKEN
     Return the signature algorithm used for TOKEN, as a symbol.

 -- Generic: token->jwt TOKEN
     Return two alists, following the JSON representation from srfi-180: one for the header, and then one for the payload.

 -- Generic: lookup-keys TOKEN ARGS
     Return the set of keys that could be used to sign TOKEN, as a public key, a list of keys, or a JWKS. ARGS is a list of keyword arguments for specific implementations.

 -- Generic: verify TOKEN ARGS
     Suppose that the TOKEN signature has been checked, perform some additional verifications. This function should raise exceptions to signal an invalid token.

 -- function: decode EXPECTED-TOKEN-CLASS ENCODED . ARGS
     Parse ENCODED as a token from the EXPECTED-TOKEN-CLASS, check its signature against the key obtained by (lookup-keys TOKEN ARGS) where TOKEN is the parsed token, and perform additional verifications with (verify TOKEN ARGS).

 -- function: encode TOKEN KEY
     Encode and sign TOKEN with KEY, returning a string.

 -- function: issue TOKEN-CLASS ISSUER-KEY . ARGS
     Construct a token of TOKEN-CLASS and ARGS and sign it with ISSUER-KEY. Since we know the key to sign it, it is not necessary to pass either #:signing-key nor #:alg to the constructor.

Tokens issued by an OIDC provider
=================================

OIDC tokens are those signed by an OIDC identity provider. This kind of token knows its issuer, and getting the keys to check the token signature is done by OIDC discovery.

 -- Class: <oidc-token> (<token>) ISS
     The base class for tokens which are issued by an identity provider. It knows the issuer (ISS, an uri from (web uri)), and can query it to check the token signature.

     Similarly to the base token type, you can construct one by specifying its arguments, or create one from a pair of alists.

       * #:ALG or #:SIGNING-KEY is required to construct the base token;

       * #:ISS specifies the issuer.

     The main point of this class is to provide a method for the lookup-keys generic. This method accepts one keyword argument, #:HTTP-REQUEST, a function that behaves like the web client in (web client). You can set this value as a keyword argument in the decode function.

 -- Generic: iss TOKEN
     Return the issuer of TOKEN, as an URI.

 -- Exception type: &cannot-query-identity-provider IDENTITY-PROVIDER
     This exception is raised when the OIDC discovery fails. IDENTITY-PROVIDER is an URI.

 -- function: make-cannot-query-identity-provider IDENTITY-PROVIDER
     Construct an exception of type &cannot-query-identity-provider.

 -- function: cannot-query-identity-provider? EXCEPTION
     Check whether EXCEPTION was raised because an identity provider could not be queried.

 -- function: cannot-query-identity-provider-value EXCEPTION
     Return the faulty identity provider for EXCEPTION.

Date verification for tokens
============================

Different kinds of tokens have a requirement for a limited time window for which the signature should be valid.

 -- Class: <time-bound-token> (<token>) IAT EXP
     The base class for tokens which are issued for a limited time window. It knows the issuance date (IAT, a date from (srfi srfi-19)), and the expiration date (IAT, a date from (srfi srfi-19)).

     Similarly to the base token type, you can construct one by specifying its arguments, or create one from a pair of alists.

       * #:ALG or #:SIGNING-KEY is required to construct the base token;

       * #:IAT specifies the issuance date. It defaults to the current date;

       * #:EXP specifies the expiration date. If it is not set, the value will be computed from IAT and VALIDITY;

       * #:VALIDITY is used when the expiration date is not known in advance. It is a number of seconds. For a DPoP proof, the value should be around 30 seconds. For an access token, a good value is in the ballpark of 3600 seconds (an hour). Defaults to 3600 seconds, but be aware that for single-use tokens, this value will be ignored and replaced with a much shorter time.

     The main point of this class is to provide a stricter token validation function. You can customize the current date by passing #:CURRENT-DATE ... as keyword arguments to decode. ... would be replaced with a time or date.

 -- Generic: default-validity TOKEN
     Return the default validity as a number of seconds to construct TOKEN, or #f if an explicit #:validity is required.

 -- Generic: has-explicit-exp? TOKEN
     Check whether we should trust the JWT exp field when constructing TOKEN. DPoP proofs should not be able to fill our cache with infinitely-valid proofs, so it is disabled for DPoP proofs.

 -- Generic: iat TOKEN
     Return the signature date of TOKEN, as a srfi-19 date.

 -- Generic: exp TOKEN
     Return the expiration date of TOKEN, as a srfi-19 date.

 -- Exception type: &signed-in-future SIGNATURE-DATE CURRENT-DATE
 -- Exception type: &expired EXPIRATION-DATE CURRENT-DATE
     An exception of type &signed-in-future is raised when the current date is before the alleged signature date. Since the signing entity and the verifier entity may not be on the same system, the clocks may be slightly out of synchronization, so a margin of 5 seconds is usually accepted.

     An exception of type &expired indicates that the signature is no longer valid.

 -- function: make-signed-in-future SIGNATURE-DATE CURRENT-DATE
 -- function: make-expired EXPIRATION-DATE CURRENT-DATE
     Constructors for the &signed-in-future and &expired exception types.

 -- function: signed-in-future? EXCEPTION
 -- function: expired? EXCEPTION
     Check whether EXCEPTION was raised because of a date mismatch.

 -- function: error-signature-date EXCEPTION
 -- function: error-expiration-date EXCEPTION
 -- function: error-current-date EXCEPTION
     If EXCEPTION was raised because of a date mismatch, return the signature, expiration or current date.

Single-use tokens
=================

To prevent replay attacks, you might want to assign an unique identifier to each token of some kind. If you have an expiration date, you could remember that this identifier has been seen, and forget about it as soon as the token expires. For this to work, you would need an expiration date for your single-use token: this is why we only support it for time-bound tokens, and the validity is reduced down to 2 minutes.

 -- Class: <single-use-token> (<time-bound-token>) NONCE
     The base class for tokens which are intended to be decoded only once. The unique identifier string NONCE will be remembered as long as the program is running and the token is not expired.

     Similarly to the base token type, you can construct one by specifying its arguments, or create one from a pair of alists.

       * #:ALG or #:SIGNING-KEY is required to construct the base token;

       * #:IAT and #:EXP or #:VALIDITY is required to construct the time-bound token;

       * #:NONCE specifies the unique identifier. It defaults to a random string of base64 data encoding 96 bits of entropy.

     The main point of this class is to provide an even stricter token validation function, that can only be run once for a given token (with reasonable limits: if the program is killed, it won’t remember the tokens from before). You can customize the current date by passing #:CURRENT-DATE ... as keyword arguments to decode, just as you do for regular time-bound tokens. ... would be replaced with a time or date.

 -- Generic: nonce-field-name TOKEN
     When constructing TOKEN from an existing JWT, this method gives the field name in the JWT payload that represents the nonce. DPoP proofs use 'jti, so they override this value.

 -- Generic: nonce TOKEN
     Return the unique identifier of TOKEN, as a string.

 -- Exception type: &nonce-found NONCE
     If a token with the same nonce has already been decoded during its life time, this exception is raised with the duplicated NONCE.

 -- function: make-nonce-found NONCE
     Construct an exception of type &nonce-found.

 -- function: nonce-found? EXCEPTION
     Check whether EXCEPTION was raised because a single-use token was already parsed.

 -- function: nonce-found-nonce EXCEPTION
     Return the faulty nonce in EXCEPTION.

ID tokens
=========

The (webid-oidc oidc-id-token) module contains a definition for the OIDC ID token.

 -- Class: <id-token> (<single-use-token> <oidc-token>) WEBID SUB AUD
     The ID token is issued by an identity provider, and is intended to be used by the client only. It gives information about the user identified by a WEBID, an URI from (web uri), and the client ID, AUD, an URI too. Since the client should not communicate this token, it is reasonable to think that the client will deccode the token as soon as it gets it, and then forget the now useless signature. This is why this token is considered single-use. The SUB field should store a username as a string, but if it is missing, the webid (as a string) will be used.

     To construct an ID token, you would either need #:JWT-HEADER and #:JWT-PAYLOAD, as for any token, or a combination of parameters:

       * #:ALG or #:SIGNING-KEY, to initialize a JWT;

       * #:IAT and #:EXP or #:VALIDITY, because it is issued for a limited time window (around an hour);

       * #:NONCE to define its identifier (defaults to a random one);

       * #:ISS, the issuer URI, because it is an OIDC token;

       * #:WEBID, an URI identifying the user;

       * #:SUB, a string that defaults to the webid;

       * #:AUD, an URI identifying the application.

 -- Generic: webid TOKEN
     Return the user identifier in TOKEN, as an URI.

 -- Generic: sub TOKEN
     Return the username in TOKEN, as a string.

 -- Generic: aud TOKEN
     Return the client identifier in TOKEN, as an URI.

 -- Exception type: &invalid-id-token
     This exception is raised when the ID token is invalid.

 -- function: make-invalid-id-token
     Construct an exception of type &invalid-id-token.

 -- function: invalid-id-token? EXCEPTION
     Check whether EXCEPTION was raised because of an invalid ID token.

Access tokens
=============

The (webid-oidc access-token) module contains a definition for the OIDC access token.

 -- Class: <access-token> (<time-bound-token> <oidc-token>) WEBID AUD CLIENT-ID CNF/JKT
     The access token is issued by an identity provider for a client, and is intended to be used by the resource servers. It indicates that the agent possessing a key hashed to CNF/JKT (a string) is identified by CLIENT-ID (an URI) and is authorized to act on behalf of the user identified by WEBID (an URI). For compatibility, AUD should be set to the literal string "solid". The agent demonstrates that it owns this key by issuing a DPoP proof alongside the access token.

     To construct an access token, you would either need #:JWT-HEADER and #:JWT-PAYLOAD, as for any token, or a combination of parameters:

       * #:ALG or #:SIGNING-KEY, to initialize a JWT;

       * #:IAT and #:EXP or #:VALIDITY, because it is issued for a limited time window (around an hour);

       * #:ISS, the issuer URI, because it is an OIDC token;

       * #:WEBID, an URI identifying the user;

       * #:CLIENT-ID, an URI identifying the client;

       * #:CNF/JKT, the hash of a public key whose private key is owned by the client, or #:CLIENT-KEY, the client key itself;

       * #:AUD, literally "solid", optional, defaults to the correct value.

     Since the same access token is presented on each request, it is not single-use.

 -- Generic: webid TOKEN
     Return the user identifier in TOKEN, as an URI.

 -- Generic: client-id TOKEN
     Return the client identifier in TOKEN, as an URI.

 -- Generic: cnf/jkt TOKEN
     Return the hash of the client key, as a string.

 -- Generic: aud TOKEN
     Return "solid".

 -- Exception type: &invalid-access-token
     This exception is raised when the access token is invalid.

 -- function: make-invalid-access-token
     Construct an exception of type &invalid-access-token.

 -- function: invalid-access-token? EXCEPTION
     Check whether EXCEPTION was raised because of an invalid access token.

DPoP proofs
===========

The (webid-oidc dpop-proof) module contains a definition for the DPoP proof token.

 -- Class: <dpop-proof> (<single-use-token>) TYP JWK HTM HTU ATH
     The DPoP proof is a token that is issued by the client, and presented to the resource server along with an access token. It is only valid for one request, and for one use. So, it should have a very short validity frame, for instance 30 seconds, and should only be valid for a specific request method HTM and a specific request URI HTU, down to the path, but ignoring the query and fragment.

     The DPoP proof is the proof of possession of JWK, a public key. It should always have a TYP field set to "dpop+jwt".

     To construct a DPoP proof, you would either need #:JWT-HEADER and #:JWT-PAYLOAD, as for any token, or a combination of parameters:

       * #:ALG or #:SIGNING-KEY, to initialize a JWT;

       * #:IAT and #:EXP or #:VALIDITY, because it is issued for a limited time window (around 30 seconds);

       * #:NONCE, because it is single-use;

       * #:JWK, the public key whose possession we demonstrate by signing the proof;

       * #:HTM, the HTTP method used (as a symbol);

       * #:HTU, the HTTP URI used (as an URI);

       * #:ATH, the hash of the access token that goes with this proof, or #:ACCESS-TOKEN, the encoded access token itself, if the proof goes with an access token. Otherwise, pass #f. Defaults to #f;

       * #:TYP, literally "dpop+jwt", optional, defaults to the correct value.

     This token class makes a stricter verification function. It requires you to set as a keyword argument in decode the following parameters:

     #:ACCESS-TOKEN
          set the access token that should go with the proof, defaults to #f (no access token);

     #:METHOD
          set the method used for the proof;

     #:URI
          set the URI used for the proof;

     #:CNF/CHECK
          set the expected hash of the key used by the DPoP proof, or a function taking a public key hash. If this is a function, it should raise an exception if the hash is invalid, because its return value is ignored.

 -- Generic: jwk PROOF
     Return the public key whose possession PROOF demonstrates.

 -- Generic: htm PROOF
     Return the HTTP method in PROOF, as a symbol.

 -- Generic: htu PROOF
     Return the HTTP URI in PROOF, as an URI.

 -- Generic: ath PROOF
     Return the hash of the access token that should go with PROOF, or #f if PROOF is not used with an access token.

 -- Generic: typ PROOF
     Return "dpop+jwt".

 -- Exception type: &invalid-dpop-proof
     This exception is raised when the DPoP proof is invalid.

 -- function: make-invalid-dpop-proof
     Construct an exception of type &invalid-dpop-proof.

 -- function: invalid-dpop-proof? EXCEPTION
     Check whether EXCEPTION was raised because of an invalid DPoP proof.

 -- Exception type: &dpop-method-mismatch ADVERTISED ACTUAL
     This exception is raised when the ADVERTISED method is not what is ACTUALly used in the request (both symbols).

 -- function: make-dpop-method-mismatch ADVERTISED ACTUAL
     Construct an exception of type &dpop-method-mismatch.

 -- function: dpop-method-mismatch? EXCEPTION
     Check whether EXCEPTION was raised because of a difference between the advertised and actual HTTP methods used.

 -- function: dpop-method-mismatch-advertised EXCEPTION
     In case of a DPoP method mismatch causing EXCEPTION, return the method used in the proof signature.

 -- function: dpop-method-mismatch-actual EXCEPTION
     In case of a DPoP method mismatch causing EXCEPTION, return the method that the server received.

 -- Exception type: &dpop-uri-mismatch ADVERTISED ACTUAL
     This exception is raised when the ADVERTISED URI is not what is ACTUALly used in the request (both URIs).

 -- function: make-dpop-uri-mismatch ADVERTISED ACTUAL
     Construct an exception of type &dpop-uri-mismatch.

 -- function: dpop-uri-mismatch? EXCEPTION
     Check whether EXCEPTION was raised because of a difference between the advertised and actual HTTP URIs used.

 -- function: dpop-uri-mismatch-advertised EXCEPTION
     In case of a DPoP URI mismatch causing EXCEPTION, return the URI used in the proof signature.

 -- function: dpop-uri-mismatch-actual EXCEPTION
     In case of a DPoP URI mismatch causing EXCEPTION, return the URI that the server received.

 -- Exception type: &dpop-invalid-ath HASH ACCESS-TOKEN
     This exception is raised when the DPoP proof is intended for use along with an access token identified by HASH, but is actually used along with ACCESS-TOKEN.

 -- function: make-dpop-invalid-ath HASH ACCESS-TOKEN
     Construct an exception of type &dpop-invalid-ath.

 -- function: dpop-invalid-ath? EXCEPTION
     Check whether EXCEPTION was raised because the DPoP proof was not used with the correct access token.

 -- function: dpop-invalid-ath-hash EXCEPTION
     In case of a DPoP presented with the wrong access token, causing EXCEPTION, return the hash of the intended access token.

 -- function: dpop-invalid-ath-access-token EXCEPTION
     In case of a DPoP presented with the wrong access token, causing EXCEPTION, return the actual access token.

 -- Exception type: &dpop-unconfirmed-key
     This exception is raised when the DPoP proof does not demonstrate the possession of the correct key.

 -- function: make-dpop-unconfirmed-key
     Construct an exception of type &dpop-unconfirmed-key.

 -- function: dpop-unconfirmed-key? EXCEPTION
     Check whether EXCEPTION was raised because the DPoP proof demonstrated the possession of an incorrect key.

Authorization codes
===================

(webid-oidc authorization-code) defines an authorization code type.

 -- Class: <authorization-code> (<single-use-token>) WEBID CLIENT-ID
     While it is not necessary that an authorization code is a JWT, it is easier to implement that way. It is an authorization for CLIENT-ID, an URI identifying a client, to access the data of the user identified by WEBID, an URI too. It should only be valid for a limited amount of time, and used once only.

     The DPoP proof is a token that is issued by the client, and presented to the resource server along with an access token. It is only valid for one request, and for one use. So, it should have a very short validity frame, for instance 30 seconds, and should only be valid for a specific request method HTM and a specific request URI HTU, down to the path, but ignoring the query and fragment.

     The DPoP proof is the proof of possession of JWK, a public key. It should always have a TYP field set to "dpop+jwt".

     To construct an authorization code, you would either need #:JWT-HEADER and #:JWT-PAYLOAD, as for any token, or a combination of parameters:

       * #:ALG or #:SIGNING-KEY, to initialize a JWT;

       * #:IAT and #:EXP or #:VALIDITY, because it is issued for a limited time window (around 30 seconds);

       * #:NONCE, because it is single-use;

       * #:WEBID, the user identifier;

       * #:CLIENT-ID, the client identifier.

     The authorization code is signed and verified by the same entity. So, the key lookup function is tuned to always return the issuer key. You need to set it as the #:ISSUER-KEY keyword argument of the decode function.

 -- Generic: webid TOKEN
     Return the user identifier in TOKEN, as an URI.

 -- Generic: client-id TOKEN
     Return the client identifier in TOKEN, as an URI.

 -- Exception type: &invalid-authorization-code
     This exception is raised when the authorization ccode is invalid.

 -- function: make-invalid-authorization-code
     Construct an exception of type &invalid-authorization-code.

 -- function: invalid-authorization-code? EXCEPTION
     Check whether EXCEPTION was raised because of an invalid authorization code.

Caching on server side
**********************

Both the identity provider and the resource server need to cache things. The identity provider will cache application webids, and the resource server will cache the identity provider keys, for instance.

The solution is to use a file-system cache. Every response (except those that have a cache-control policy of no-store) are stored to a sub-directory of XDG_CACHE_HOME. Each store has a 5% chance of triggering a cleanup of the cache. When a cleanup occurs, each cached response has a 5% chance of being dropped, including responses that are indicated as valid. This way, a malicious cache response that has a maliciously long validity will not stay too long in the cache. A log line will indicate which items are dropped.

The (webid-oidc cache) module exports two functions to deal with the cache.

 -- function: clean-cache [#PERCENTS]
     Drop PERCENTS% of the cache right now.

 -- function: use-cache F
     Call F with no arguments, with the default HTTP request method set to a function that tries to use the cache first.o

     The cache will be read and written in the web-cache subdirectory of the cache home. To check the time window validity, the CURRENT-DATE parameter is used.

     The back-end function, HTTP-GET, defaults to that of (web client).

 -- parameter: cache-home
     This parameters sets the cache directory. By default, it is XDG_CACHE_HOME.

The HTTP Link header
********************

The HTTP Link header lets you attach metadata about a resource, directly in the HTTP protocol. It is used to link resources to their auxiliary resources, for instance.

The following API is defined in (webid-oidc http-link):

 -- Class: <link> TARGET-IRI RELATION-TYPE TARGET-ATTRIBUTES
     The link refers to the TARGET-IRI that is being linked to the requested resource, with a given RELATION-TYPE (a string), and optional additional TARGET-ATTRIBUTES.

     When constructing a <link>, you should use the #:TARGET-IRI, #:RELATION-TYPE and #:TARGET-ATTRIBUTES keyword arguments (#:TARGET-ATTRIBUTES defaults to the empty list) to initialize the link. For convenience, the #:ANCHOR, #:HREFLANG, #:MEDIA, #:TITLE, #:TITLE* and #:TYPE keyword arguments can be passed to add well-known target attributes.

 -- Class: <target-attribute> KEY VALUE
     If you wish to add new extension target attributes, you can create an ad-hoc target attribute with KEY and VALUE (initialized as #:KEY and #:VALUE constructor keyword arguments).

 -- Generic: target-iri LINK
 -- Generic: relation-type LINK
 -- Generic: target-attributes LINK
     Getters for the <link> class.

 -- Generic: key TARGET-ATTRIBUTE
 -- Generic: value TARGET-ATTRIBUTE
     Getters for the <target-attribute> class.

 -- Generic: target-attribute LINK KEY
     Return the value of the first target attributet with KEY.

 -- Generic: anchor LINK
 -- Generic: hreflang LINK
 -- Generic: media LINK
 -- Generic: title LINK
 -- Generic: title* LINK
 -- Generic: type LINK
     Convenience attribute lookup functions. anchor returns an URI referencce, the others return a string.

 -- function: declare-link-header!
     Declare functions to parse, validate and print HTTP Link headers with the Guile web request / response API.

 -- function: request-links REQUEST
 -- function: response-links RESPONSE
     Return the list of links in REQUEST or RESPONSE.

Content negociation
*******************

There are a number of different available syntaxes for RDF, some being simple and human readable like turtle, and others more adapted to the JavaScript ecosystem like json-ld. To help clients both from and outside of the JS ecosystem, the server needs to perform content negociation, i.e. convert from one content-type to another.

 -- function from (webid-oidc serve): convert CLIENT-ACCEPTS SERVER-NAME PATH CONTENT-TYPE CONTENT
     Convert the resource representation under PATH on SERVER-NAME, which has a given CONTENT-TYPE and CONTENT, to a content-type that the CLIENT ACCEPTS.

     Return 2 values:

      1. the accepted content-type;

      2. the content in the given content-type.

     Currently, the only conversions are from and to Turtle and N-Quads.

Server endpoints
****************

The disfluid server consists of a set of endpoints that handle requests. The (webid-oidc server endpoint) module defines the base building blocks.

 -- Class: <endpoint> () HOST PATH
     All endpoints define a HOST for which they are relevant, and an absolute PATH. If a request comes with a matching host and a matching path prefix, then it will be handled by this endpoint.

     If HOST is #f, then this endpoint will be used for all hosts.

     You can construct an endpoint with the #:HOST and #:PATH keyword arguments: the former is a string (defaults to #f), and the latter is a string starting with "/" (defaults to "/").

 -- Generic: handle ENDPOINT REQUEST REQUEST-BODY
     Handle REQUEST with ENDPOINT.

     REQUEST is the request, in the form of an object from (web request). REQUEST-BODY is #f, a string or a bytevector.

     Return 3 values: a response, a response body (a string, bytevector, input port, or #f if no body is expected), and some response meta-data.

 -- Generic: host ENDPOINT
     Return the host name ENDPOINT is configured to respond to.

 -- Generic: path ENDPOINT
     Return the path prefix ENDPOINT is configured to respond to.

 -- Generic: relevant? ENDPOINT REQUEST
     Check if ENDPOINT is configured to respond to REQUEST.

The handler may throw exceptions to signal errors. Exception messages will be printed to the log file, and user messages will be passed to the user.

Error signalling
================

The (webid-oidc server endpoint) module defines exception types that can be emitted to abort the computation in a handler. If an exception of a different kind is raised, this will lead to a 500 Internal Server Error response.

 -- Exception type: &web-exception CODE REASON-PHRASE
     The request failed, with CODE and REASON-PHRASE.

 -- function: make-web-exception CODE REASON-PHRASE
     Create an exception with CODE and REASON-PHRASE.

 -- function: web-exception? EXN
     Check if EXN was thrown because the request failed.

 -- function: web-exception-code EXN
 -- function: web-exception-reason-phrase EXN
     Return the code and reason-phrase for when EXN was thrown, if it was thrown because of a failing request.

 -- Exception type: &caused-by-user WEBID
     If a web exception is raised, maybe it is caused by some user identified by WEBID (an URI, or #f.

 -- function: make-caused-by-user WEBID
     Constructor for &caused-by-user.

 -- function: caused-by-user? EXN
     Check if EXN was caused by the user.

 -- function: caused-by-user-webid EXN
     Return the webid of the user that caused EXN.

 -- Exception type: &user-message SXML
     An exception containing a message that is safe to show to the user, as an SXML fragment of XHTML. Typically, this would be a <p/>, or a <div/>.

     You can set a user-message multiple times. The occurences will be concatenated in the response, in the order they appear in the composite exception.

 -- function: make-user-message SXML
     Create a new user message containing the SXML fragment.

 -- function: user-message? EXN
     Check if there is at least one user message in EXN.

 -- function: user-message-sxml EXN
     Return all user messages in EXN, as a <div/> SXML fragment.

Router endpoint
===============

The first non-trivial handler is for the router endpoint, defined in (webid-oidc server endpoint).

 -- Class: <router> (<endpoint>) ROUTED
     The router has a list of endpoints, and chooses which one will handle an incoming request based on the request fields. The ROUTED endpoints is a list of endpoints. You can set it at construction time with #:ROUTED.

     The router will check if the ROUTED endpoints are relevant, in turn, or return a 404 Not Found response if no endpoint is relevant.

 -- Generic: routed ROUTER
     Return the list of endpoints for ROUTER.

Running an Identity Provider
****************************

This project is packaged with a barebones identity provider. It has an authorization endpoint and a token endpoint (and it serves its public keys), but it is only intended for one specific person.

You can start it by invoking the webid-oidc program with the issuer command, with the following options:

-h, or --help
     prints a summary of options and exit.

-v, or --version
     prints the version of the program and exits.

-n URI, or --server-name=URI
     sets the global server name of the identity provider. It should have an empty path.

-k FILE.JWK, or --key-file=FILE.JWK
     sets the file name where to read or generate a key for the identity provider. This file should be JSON, containing the representation of a JWK key pair.

-s WEBID, or --subject=WEBID
     sets the webid of the only user of the identity provider. This is an URI, pointing to a RDF node corresponding to the user’s profile.

-w PASSWORD, or --password=PASSWORD
     sets the password that the user must enter to authorize an application.

-j URI, or --jwks-uri=URI
     tells the server that requests to URI should be responded with the public key used to sign the tokens.

-a URI, or --authorization-endpoint-uri=URI
     tells the server that requests to URI should be treated as authorization requests.

-t URI, or --token-endpoint-uri=URI
     tells the server that requests to URI should be treated as token negociation requests.

-p PORT, or --port=PORT
     change the port number used by the server. By default, it is set to 8080.

-l FILE.LOG, or --log-file=FILE.LOG
     let the server dump all its output to FILE.LOG. Since I don’t know how to deal with syslog, this is the only way to keep logs with a shepherd service.

-e FILE.ERR, or --error-file=FILE.ERR
     let the server dump all its errors to FILE.ERR.

The program is sensitive to the environment variables. The most important one is LANG, which influences how the program is internationalized to the server administrator (the pages served to the user use the user agent’s locale). This changes the long form of the options, and the language in the log files.

The XDG_DATA_HOME should point to some place where the program will store refresh tokens, under the webid-oidc directory. For a system service, you might want to define that environment to /var/lib, for instance.

The XDG_CACHE_HOME should point to a directory where to store the seed of the random number generator (under a webid-oidc directory, again). Changing the seed only happens when a program starts to require the random number generator. You can safely delete this directory, but you need to restart the program to actually change the seed.

Running a Resource Server
*************************

A Solid server is the server that manages your data. It needs to check that the proofs of possession are correct, and the possessed key is signed by the identity provider.

The authenticator
=================

In (webid-oidc resource-server), the following function gives a simple API for a web server:

 -- function: make-authenticator JTI-LIST [#SERVER-URI] [#CURRENT-TIME] [#HTTP-GET]
     Create an authenticator, i.e. a function that takes a request and request body and returns the webid of the authenticated user, or #f if it is not authenticated.

     To prevent replay attacks, each request is signed by the client with a different unique padding value. If such a value has already been seen, then the request must fail.

     The authenticator expects the client to demonstrate the possession of a key that the identity provider knows. So the client creates a DPoP proof, targetted to a specific URI. In order to check that the URI is correct, the authenticator needs the public URI of the service.

     The JTIs are checked within a small time frame. By default, the system time will be used. Otherwise, you can customize the current-time optional keyword argument, to pass a thunk returning a time from (srfi srfi-19).

     You may want to customize the HTTP-GET optional keyword argument to pass a function to replace http-get from (http client). This function takes an URI and optional #:headers arguments, makes the request, and return two values: the response, and the response body.

     This function, in (webid-oidc resource-server), returns a web request handler, taking the request and request body, and returning the subject of the access token. If an error happens, it is thrown; the function always returns a valid URI.

The full server
===============

 -- function from (webid-oidc resource-server): make-server [#:SERVER-URI] [#:OWNER] [#:AUTHENTICATOR] [#:CURRENT-TIME] [#:HTTP-GET]
     Return a server handler, a function taking 2 values, a request and a request body, and returning 2 values, the response and response body.

     The optional [#:AUTHENTICATOR] argument defaults to the webid-oidc authenticator, [#:CURRENT-TIME] defaults to a thunk returning the system time and [#:HTTP-GET] to the web client from (web client).

Resources stored on the server
==============================

To store and serve resources, the server has two distinct mechanisms. A content is a read-only possible value for a resource, indexed by etags, and a path is a mutable value that indicates the etag of the resource, and of the auxiliary resources (description and ACL). With this separation, it is possible to atomically delete a resource and all associated auxiliary resources, by unlinking the corresponding path. It is also possible to mutate separately the ACL and the resource itself without writing a copy for both.

The content API is contained in the (webid-oidc server resource content) module.

 -- Class: <content> () ETAG CONTENT-TYPE CONTAINED STATIC-CONTENT
     This class encapsulate a static resource content linked to a particular ETAG.

     The CONTENT-TYPE is a symbol, and STATIC-CONTENT is a bytevector, although a string will be encoded to UTF-8 at construction time. CONTAINED is either #f, if the resource is not a container, or a list of resource paths (each one is a string) identifying contained resources.

     You can construct a content in two ways.

     If you pass #:ETAG, it will be loaded from the file system under the ETAG index, or if #:CACHE is passed or the current-content-cache is set to CACHE, it will try to load from CACHE first. If you define a cache, the result will also be added to CACHE.

     If you pass #:CONTENT-TYPE, #:CONTAINED and #:STATIC-CONTENT, but not #:etag, it will be created and saved to disk, and optionally added to the #:CACHE or the current content cache.

 -- Class: <content-cache> () CACHE
     Since the contents are read-only, it is possible to cache the values in memory to avoid reading the same file more than once. This is how the session works.

     CACHE is a hash table for string etag values to cached content values. It is initialized as an empty hash table.

 -- Generic: etag CONTENT
     Return the ETag of CONTENT, as a string.

 -- Generic: content-type CONTENT
     Return the Content-Type of CONTENT, as a symbol.

 -- Generic: contained CONTENT
     Return the contained paths of CONTENT, as a list of strings, or #f if it is not a container.

 -- Generic: static-content CONTENT
     Return the static content of CONTENT, as a bytevector.

 -- Generic: delete-content CONTENT
 -- Generic: delete-content ETAG
     Remove [CONTENT’s] ETAG from the file system. If it is cached in SESSION, also remove the cached value. Otherwise, other sessions can still access it.

 -- parameter: current-content-cache
     A guile parameter indicating a cache where loaded contents should be added and preferably fetched. By default, no caching is performed. You need to set this parameter to benefit from it.

The path API is defined in (webid-oidc server resource path).

 -- function: read-path PATH
     Read the resource at PATH, and return 2 values:

      1. the content of the main resource;

      2. an alist where keys are auxiliary resource type URIs (the type is from (web uri)), and the values are contents of the corresponding resource.

     If the resource is not found, raise an exception with type &path-not-found, and maybe &uri-slash-semantics-error if a resource with a different ending-in-slash exists.

     If the current-content-cache parameter is set to a cache, it will be used to load the content and auxiliary contents.

     This function is safe to call when the path is being modified, either by another thread, process or else, as the returned values will always be consistent. However, once the function returns, an updating process may have deleted the returned content. If this is the case, then you must call this function again to read the updated path.

 -- function: update-path PATH F [#:CREATE-INTERMEDIATE-CONTAINERS?=#f]
     Read PATH, call F with two values: the main content and the auxiliary contents (as returned by READ-PATH), and update the path accordingly. If PATH does not exist, then the first argument is #f and the second one is the empty list.

     If F returns #f, then the resource is deleted.

     If F returns two values: a content as the first and an alist of auxiliary types (as URIs) to auxiliary contents as the second, then the resource is updated.

     This function uses the current-content-cache parameter to load contents. If a resource is created or deleted, the parent’s containment triples will be modified, so they will also be loaded in the cache.

     Some operations should create the intermediate containers for a given path, this is the case for the PUT HTTP verb. For POST, the parent should exist. The #:CREATE-INTERMEDIATE-CONTAINERS? switch lets you change the behavior. In any case, it is an error to delete a non-empty container.

     The update is atomic, meaning that at any point in time the file is fully written out. Concurrent access to the same resource is performed by locking the lock file named X/.lock, where X is the first character of the base64-url sha-256 hash of the path. The lock file is not meant to be removed when the resource is unlocked. It should be locked with flock instead. Like other forms of lock-based synchronization, this function is not composable. This means that you cannot call this function within F, otherwise a deadlock may ensue.

     If the resource is created or deleted, then the parent resource is updated as well. To avoid deadlocks with other processes, please follow the following rules: lock the path, then lock the parent path, then update the parent, then unlock the parent, and finally unlock the child path.

The Web Access Control specification defines an RDF vocabulary to check whether a given user is allowed to perform some operations. The (webid-oidc server resource wac) helps you do that.

 -- function: wac-get-modes SERVER-NAME PATH USER [#:HTTP-GET]
     Return the list of modes that are allowed for USER accessing PATH. The SERVER-NAME URI is required to find the relevant triples in the ACL. If USER is unauthenticated, pass #f.

     Please note that in any case, the data owner should have all rights whatsoever, bypassing WAC. Otherwise, it is possible to steal control away from the data owner.

 -- function: check-acl-can-read SERVER-NAME PATH OWNER USER [#:HTTP-GET]
 -- function: check-acl-can-write SERVER-NAME PATH OWNER USER [#:HTTP-GET]
 -- function: check-acl-can-append SERVER-NAME PATH OWNER USER [#:HTTP-GET]
 -- function: check-acl-can-control SERVER-NAME PATH OWNER USER [#:HTTP-GET]
     Assert that the resource at PATH on SERVER-NAME is owned by OWNER, and check that USER has the proper authorization. Otherwise, raise an exception of type &forbidden.

Running a client
****************

The job of the client is to use accounts to fetch private resources on the web. The (webid-oidc client) defines the <client> class.

 -- Class: <client> CLIENT-ID KEY-PAIR REDIRECT-URI
     In OIDC, a client is an application that does not hold the resources. It may in fact be a network server available on the web, or a program that you run on your machine. Being a network server or not is irrelevant.

     The <client> class is designed with immutability in mind. You can create a client with the make generic method, using these keywords to initialize values:

     #:client-id
          to set the public client identifier (this endpoint should be available on the world-wide web), as a string representing an URI or an URI from (web uri);

     #:key-pair
          to use a specific key pair. If not set, a new key pair will be generated;

     #:redirect-uri
          to set the redirect URI that the application controls. It may just be a page showing the authorization code, with instructions on how to paste this code into the application. It should match one of the authorized redirect URIs in the client identifier endpoint.

          If you want to set a state parameter for the redirection, you can do it by setting the guile parameter authorization-state.

 -- Generic method: uri client-id (CLIENT <client>)
 -- Generic method: key pair key-pair (CLIENT <client>)
 -- Generic method: uri redirect-uri (CLIENT <client>)
     Slot accessors for CLIENT.

 -- Parameter: client
     Define this parameter to set the client to use to access private data.

To access private data, you must identify yourself. The (webid-oidc client accounts) module lets you define accounts.

 -- Class: <account> SUBJECT ISSUER ID-TOKEN ACCESS-TOKEN REFRESH-TOKEN KEY-PAIR
     Encapsulate an account. SUBJECT is your webid, while ISSUER is a host name. ID-TOKEN is the decoded OIDC ID token, i.e. a pair of (header . payload), because we don’t need to show it to any other party, so its authenticity needs not be demonstrated. However, ACCESS-TOKEN is an encoded access token (into a string), because we don’t need to worry about its internals on client side.

     There are different ways to initialize an account. First, you can save all parameters to some form of storage, and restore it by using the associated keyword arguments at construction time:

     #:subject

     #:issuer

     #:id-token

     #:access-token

     #:refresh-token

     #:key-pair

     If you want to make a new account, you would ask the user for an identity provider, and pass it with #:issuer as the only initialized value. The constructor will log you in, using the authorization-process and anonymous-http-request function parameters.

     If you want to refresh an access token, you would also set #:refresh-token.

     In any case, when you don’t specify a value, it’s as if you passed #f.

 -- Parameter: authorization-process
     This function is called when an explicit user authorization is required, for instance because there is no refresh token and the access token expired. The function takes an URI as argument, with an additional #:reason keyword argument containing the reason for the authorization as a string. In this function, you should present the reason to the user and ask the user to browse this URI so that your application gets the authorization code.

 -- Parameter: anonymous-http-request
     This function is used as a back-end for private resource access, and to query the server configuration. It defaults to http-request from (web client).

 -- Generic method: uri subject (ACCOUNT <account>)
 -- Generic method: <account> set-subject (ACCOUNT <account>) (URI string or URI)
 -- Generic method: uri issuer (ACCOUNT <account>)
 -- Generic method: <account> set-issuer (ACCOUNT <account>) (URI string or URI)
 -- Generic method: optional decoded ID token id-token (ACCOUNT <account>)
 -- Generic method: <account> set-id-token (ACCOUNT <account>) (ID-TOKEN optional ID token)
 -- Generic method: optional encoded access token access-token (ACCOUNT <account>)
 -- Generic method: <account> set-access-token (ACCOUNT <account>) (ACCESS-TOKEN optional access token)
 -- Generic method: optional <string> refresh-token (ACCOUNT <account>)
 -- Generic method: <account> set-refresh-token (ACCOUNT <account>) (REFRESH-TOKEN optional <string>)
 -- Generic method: key pair key-pair (ACCOUNT <account>)
 -- Generic method: <account> set-key-pair (ACCOUNT <account>) (KEY-PAIR optional key pair)
     Slot accessors and functional setters for ACCOUNT.

If you intend to run a public network server as a client application, you may have multiple different users, but you should not let any user use any account. If this is the case, you can either store the accounts on the user agent storage (for instance, as a cookie), or store all of them on the server. If you choose to store the accounts on the user agent, at least use a new key pair for each of them. If you want to store the user database on the server side, be aware that no entity other than yourself will check that your user abides by any term of service, so it is possible that a single user makes a lot of accounts to annoy you and fill your hard drive with key pairs. If your application does not let random people to use it, you might want to use protected accounts, to help you check that the users cannot impersonate each other.

 -- Class: <protected-account> (<account>) USERNAME ENCRYPTED-PASSWORD
     This superclass of <account> is protected by a username and password. It is constructed with the initializer keywords #:username and #:encrypted-password.

 -- Generic method: <string> username (PROTECTED-ACCOUNT <protected-account>)
 -- Generic method: <protected-account> set-username (PROTECTED-ACCOUNT <protected-account>) (USERNAME <string>)
 -- Generic method: <string> encrypted-password (PROTECTED-ACCOUNT <protected-account>)
 -- Generic method: <protected-account> set-encrypted-password (PROTECTED-ACCOUNT <protected-account>) (ENCRYPTED-PASSWORD <string>)
     Slot accessors and functional setters for PROTECTED-ACCOUNT.

 -- Generic method: <account> invalidate-access-token (ACCOUNT <account>)
     Indicate that the access token in ACCOUNT cannot be used. Before using ACCOUNT again, you will need to refresh the access token. This function does not mutate ACCOUNT.

 -- Generic method: <account> invalidate-refresh-token (ACCOUNT <account>)
     Indicate that the refresh token has been revoked for ACCOUNT. This is usually an indication that the user don’t want your application to access her private data.  This function does not mutate ACCOUNT.

 -- Generic method: <account> refresh (ACCOUNT <account>)
     Refresh the access token.

 -- Exception type: &authorization-code-required URI
     If the login process requires the user to send an authorization code, an exception of this type will be raised, with an implicit invitation for the user to browse URI and follow the instructions.

     The instructions will be handled by the REDIRECT-URI in the login function. If your client is a traditional web application, the user will be redirected to this URI with an authorization code. If your client is a native application, then maybe that redirection URI should display the authorization code and invite the user to paste it in the appropriate place in the application.

     When an exception of this type is raised during the login function, it is continuable, meaning that the login function will resume. You need to create an exception handler for an exception of this type, look up the URI, direct the user to browse it, get the authorization code back, and return the authorization code from the exception handler.

 -- function: make-authorization-code-required URI
 -- function: authorization-code-required? ERROR
 -- function: authorization-code-required-uri ERROR
     Constructor, predicate, and accessor for the &authorization-code-required exception type.

 -- Exception type: &refresh-token-expired
     The refresh token can be used to still perform requests on behalf of the user when perse is offline. However, if the refresh token expires while the user is offline, it is not possible to log in again, because it requires a new authorization code. So, it is not possible to recover from this error, and the refresh token is immediately discarded.

 -- function: make-refresh-token-expired
 -- function: refresh-token-expired? ERROR
     Constructor and predicate for the &refresh-token-expired exception type.

 -- function: invalidate-access-token ACCOUNT
     Discard the access token for ACCOUNT. It is not saved in the user database yet. This is roughly equivalent to log out.

 -- function: invalidate-refresh-token ACCOUNT
     Discard the refresh token for ACCOUNT. You still need to save the ACCOUNT.

 -- Exception type: &token-request-failed RESPONSE RESPONSE-BODY
     If the token endpoint is unable to deliver an identity token and an access token, this exception is raised with the identity provider RESPONSE and RESPONSE BODY. This exception cannot be continued.

 -- function: make-token-request-failed RESPONSE RESPONSE-BODY
 -- function: token-request-failed? ERROR
 -- function: token-request-response ERROR
 -- function: token-request-response-body ERROR
     Constructor, predicate, and accessors for the &token-request-failed exception type.

The (webid-oidc client) module provides the most useful function for a client.

 -- function: request ACCOUNT URI . ARGS
     Perform a request on behalf of ACCOUNT, with the current value of the CLIENT parameter as the client, using as a backend the current value of ANONYMOUS-HTTP-REQUEST.

Finally, to implement your application, there needs to be a public endpoint for the resource server to check that you are not impersonating another application. This endpoint can be served by any web server, but a convenience procedure is made available here:

 -- function: serve-application ID REDIRECT-URI [#CLIENT-NAME] [#CLIENT-URI]
     Return a handler for web requests to serve the application manifest and the redirection to transmit the authorization code. You should set the CLIENT-NAME to your application name and CLIENT-URI to point to where to a presentation of your application.

Serialization to (S)XML
***********************

The (webid-oidc serializable) module provides tools to have serialization to SXML and deserialization from XML.

 -- Class: <plugin-class> (<class>) MODULE-NAME DIRECT-NAME
     This metaclass permits to register plugins. MODULE-NAME is the name of a module that defines the class, and DIRECT-NAME is the class name without the surrounding angle brackets. Please note that all plugin classes should be surrounded by angle brackets.

     Most GOOPS classes defined in this program are actually plugin classes.

     Serialization works for each slot by serializing other plugin classes the normal way, and other values are simply represented as strings with display.

     Deserialization works by loading the module containing the target class, collecting a value for each slot (a string for non-plugin-class-valued slots), and making an instance of that class with all collected values. The initialization function should accept strings values, for objects that are not of a plugin class.

     Since most scheme data types written by display cannot be read in a meaningful way, you may add a #:->sxml slot option with a function taking the slot value and either returning a string that the initialization function can parse, or an SXML fragment. For instance, if a slot should contain an URI value, you would pass #:->sxml uri->string as options to the slot definition, and accept a string value in the initialization function, that you would convert to an URI with string->uri.

     Sometimes slots contain functional data that cannot be serialized. In this case, pass #:->sxml 'ignore to avoid serialization.

 -- function: read/xml PORT
     Read the XML document at PORT and deserialize it.

 -- function: ->sxml OBJECT
     Convert OBJECT to an SXML fragment.

Exceptional conditions
**********************

The library will raise an exception whenever something fishy occurs. For instance, if a signature is invalid, or the expiration date has passed.

When the client is responsible for an error, such as presenting an invalid access token, a compound exception is raised. It is sometimes useful for the user to understand what happened, because it could indicate a problem in a part of the web they need to change. For instance, if the access token cannot be decoded because the identity provider is down, then maybe informing the user of that fact is useful.

However, presenting too much information is a security risk. For instance, if the system administrator also runs a private server on the same machine, and a malicious client tries to pretend that this private server is an identity provider, then the public server will try to query the private server. If an error happens and the public server displays some information to the client, then a part of the information comes from the private server. Thus, a balance needs to be found so that not too much is revealed.

The module (webid-oidc errors) defines an exception type that indicates a message that is safe to display to the user.

 -- Exception type: &message-for-the-user MESSAGE
     Indicate that MESSAGE can be safely displayed to the user. It is an XHTML paragraph (or equivalent), presented as SXML.

 -- function: make-message-for-the-user MESSAGE
 -- user-message: EXCEPTION
     Constructor and accessor for the &message-for-the-user exception type.

GNU Free Documentation License
******************************

     [file fdl.texi not included]