     [file version.texi not included]

This is the manual of webid-oidc (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''

Webid-oidc
**********

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.

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.

Decoded JWT are represented as a pair. The car of the pair is the header, and the cdr is the payload. Both the header and the payload use the JSON representation from srfi-180: objects are alists of symbols to values, arrays are vectors. It is unfortunate that guile-json has a slightly different representation, where alist keys are strings, but we hope that in the future SRFI-180 will be more closely respected.

The ID token
============

The ID token is a special JWT that the application keeps for itself. It is signed by the identity provider, and contains the following claims:

webid
     the URI of the user’s webid;

iss
     the URI of the identity provider (issuer);

sub
     the username (the webid-oidc issuer puts the webid again here, but it could be any string);

aud
     the ID of the client application that is intended to receive the ID token;

nonce
     some random data to change the signature;

exp
     an UTC time (in seconds) for when the token expires;

iat
     the time when it was issued.

There are functions to work with ID tokens in (webid-oidc oidc-id-token).

 -- function: id-token? OBJECT
     Check that OBJECT is a decoded ID token.

The following helper functions convert URIs to the URIs from (web uri) and times to (srfi srfi-19) dates.

 -- function: id-token-webid TOKEN
 -- function: id-token-iss TOKEN
 -- function: id-token-sub TOKEN
 -- function: id-token-aud TOKEN
 -- function: id-token-nonce TOKEN
 -- function: id-token-exp TOKEN
 -- function: id-token-iat TOKEN
     Get the suitable field from the payload of TOKEN.

ID tokens can be signed and encoded as a string, or decoded.

 -- function: id-token-decode TOKEN [#HTTP-GET]
     Decode TOKEN, as a string, into a decoded token. The signature verification will need to fetch the oidc configuration of the claimed issuer, and check the signature against the published keys. The http-get optional keyword argument can set a different implementation of http-get from (web client). Return #f if it failed, or the decoded token otherwise.

 -- function: id-token-encode TOKEN KEY
     Encode TOKEN and sign it with the issuer’s KEY.

 -- function: issue-id-token ISSUER-KEY #ALG #WEBID #ISS #SUB #AUD #EXP #IAT
     Create an ID token, and encode it with ISSUER-KEY.

The access token
================

The access token is obtained by the client through a token request, and is presented to the server on each authenticated request. It is signed by the identity provider, and it contains enough information so that the server knows who the user is and who the agent is, and most importantly the fingerprint of the key that the client should use in a DPoP proof.

The API is defined in (webid-oidc access-token).

 -- function: access-token? OBJECT
     Check that OBJECT is a decoded access token.

There are field getters for the access token:

 -- function: access-token-webid TOKEN
 -- function: access-token-iss TOKEN
 -- function: access-token-aud TOKEN
 -- function: access-token-exp TOKEN
 -- function: access-token-iat TOKEN
 -- function: access-token-cnf/jkt TOKEN
 -- function: access-token-client-id TOKEN
     Get the suitable field from the payload of TOKEN.

Access tokens can be signed and encoded as a string, or decoded.

 -- function: access-token-decode TOKEN [#HTTP-GET]
     Decode TOKEN, as a string, into a decoded token. As with the ID token, the signature verification will need to fetch the oidc configuration of the claimed issuer, and check the signature against the published keys. The http-get optional keyword argument can set a different implementation of http-get from (web client), for instance to re-use the what has been obtained by the ID token validation. Return #f if it failed, or the decoded token otherwise.

 -- function: access-token-encode TOKEN KEY
     Encode TOKEN and sign it with the issuer’s KEY.

 -- function: issue-access-token ISSUER-KEY #ALG #WEBID #ISS #EXP #IAT [#CLIENT-KEY | #CNF/JKT] #CLIENT-ID
     Create an access token, and encode it with ISSUER-KEY. You can either set the #:cnf/jkt keyword argument with the fingerprint of the client key, or set #:client-key directly, in which case the fingerprint will be computed for you.

The DPoP proof
==============

This is a special JWT, that is signed by a key controlled by the application. The access token certifies that the key used to sign the proof is approved by the identity provider.

 -- function: dpop-proof? PROOF
     Check that the PROOF is a decoded DPoP proof. The validity of the proof is not checked by this function.

 -- function: dpop-proof-alg PROOF
 -- function: dpop-proof-jwk PROOF
 -- function: dpop-proof-jti PROOF
 -- function: dpop-proof-htm PROOF
 -- function: dpop-proof-htu PROOF
 -- function: dpop-proof-iat PROOF
     Get the corresponding field of the proof.

 -- function: dpop-proof-decode CURRENT-TIME JTI-LIST METHOD URI STR CNF/CHECK
     Check and decode a DPoP proof encoded as STR.

     The CURRENT-TIME is passed as a date, time or number (of seconds).

     In order to prevent replay attacks, each proof has a unique random string that is remembered in JTI-LIST until its expiration date is reached. See the make-jti-list function.

     The proof is limited to the scope of one URI and one METHOD ('GET, 'POST and so on).

     Finally, the key that is used to sign the proof should be confirmed by the identity provider. To this end, the CNF/CHECK function is called with the fingerprint of the key. The function should check that the fingerprint is OK (return a boolean).

 -- function: make-jti-list
     This function in (webid-oidc jti-list) creates an in-memory, async-safe, thread-safe cache for the proof IDs.

 -- function: dpop-proof-encode PROOF KEY
     Encode the proof and sign it with KEY. To generate valid proofs, KEY should be the private key corresponding to the jwk field of the proof.

 -- function: issue-dpop-proof CLIENT-KEY #ALG #HTM #HTU #IAT
     Create a proof, sign it and encode it with CLIENT-KEY. CLIENT-KEY should contain both the private and public key, because the public part is written in the proof and the private part is used to sign it.

Generic JWTs
============

You can parse generic JWTs signed with JWS with the following functions from (webid-oidc jws).

 -- function: jws? JWT
     Check that JWT is a decoded JWT signed with JWS.

 -- function: jws-alg JWT
     Get the algorithm used to sign JWT.

 -- function: jws-decode STR LOOKUP-KEYS
     Check and decode a JWT signed with JWS and encoded as STR.

     Since the decoding and signature verification happen at the same time (for user friendliness), the LOOKUP-KEYS function is used. It is passed as arguments the decoded JWT (but the signature is not checked yet), and it should return a public key, a public key set or a list of public keys. If the key lookup failed, this function should raise an exception.

 -- function: jws-encode JWT KEY
     Encode the JWT and sign it with KEY.

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] [#DIR]
     Drop PERCENTS% of the cache right now, in DIR (defaults to some place within XDG_CACHE_HOME).

 -- function: with-cache [#CURRENT-TIME] [#HTTP-GET] [#DIR]
     Return a function acting as http-get from (web client) (takes an URI as the first parameter, and an optional #:HEADERS set, and returns 2 values, the response and its body).

     The cache will be read and written in DIR (defaults to some place within XDG_CACHE_HOME), and the CURRENT-TIME number of seconds, SRFI-19 time or date, or time-returning thunk will be used to check for the validity of responses.

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

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.

Running webid-oidc reverse-proxy
================================

The webid-oidc program comes with a reverse proxy command, aptly named reverse-proxy, to listen to an interface, take requests, authenticate them, and pass them to a backend with an additional header containing the webid of the agent, if authenticated.

The reverse proxy is invoked with the following arguments:

-pPORT, --port=PORT
     the port on which the reverse proxy listens;

-nURI, --server-name=URI
     the public name of the server;

-bURI, --backend-uri=URI
     the address of the backend;

-HHEADER, --header=HEADER
     replace the name of the header that will contain the webid of the user. Defaults to XXX-Agent. Please note that this value should be ASCII, otherwise it’s not guaranteed that the reverse proxy will drop other capitalizations of the header in malicious requests;

-l FILE.LOG, or --log-file=FILE.LOG
     let the server dump all its output to FILE.LOG. See the identity provider comment;

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

You can localize the interface by setting the LANG environment variable.

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

In (webid-oidc jws), 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.

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.

 -- function: with-session F [#:DIR]
     Call F with 5 arguments:

       * a function to get the content-type of a given etag;

       * a function to list the paths contained within the resource;

       * a function to load the content of a given etag;

       * a function to create a new content;

       * a function to remove a content from the file system. It is still possible to query it with the first 3 functions, but new sessions will not see it.

     Since the contents are read-only, it is possible to cache the value of the content in memory. This is why F should run within a session with memoization.

     Resources only store static content, because the membership triples for containers is considered dynamic and not included in the representation.

     The first 3 functions as well as the last one are called with an etag, and the function to create a content is called with the content-type, list of contained paths, and (static) content.

     By default, the contents are stored within XDG_DATA_HOME, but it can be overriden by #:DIR.

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 ETag of the main resource;

      2. an alist where keys are auxiliary resource type URIs (the type is from (web uri)), and the values are ETags 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.

     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 ETags. If this is the case, then you must call this function again to read the updated path.

 -- function: update-path PATH F CONTENT-TYPE CONTAINED STATIC-CONTENT CREATE DELETE [#:CREATE-INTERMEDIATE-CONTAINERS?=#f]
     Read PATH, call F with two values: the ETag and the auxiliary ETags (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 an ETag as the first returned value and an alist of auxiliary resource ETags as the second value, then the resource is updated.

     The last functions are from the content API. Since creating or deleting children requires updating the parent, we need them.

     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
****************

To run a client, you need to proceed in two steps. First, acquire an OIDC ID token and an access token from the identity provider, and then present the access token and a proof of possession of the linked key in each request, in a DPoP HTTP header.

The first operation is performed by the (webid-oidc client) module.

 -- function: authorize HOST/WEBID #CLIENT-ID #REDIRECT-URI [#STATE] [#HTTP-GET]
     The user enters a valid webid or a host name, and then this function will query it (with the HTTP-GET parameter, by default the web client from (web client)) to determine the authorization endpoint. The function will return an alist of authorization URIs, indexed by approved identity provider URIs, that the user should browse with a traditional web browser.

     Each application should have its own webid, or in that case CLIENT-ID, that can be dereferenced by the identity provider.

     Once the user has given authorization, the user’s agent will be redirected to REDIRECT-URI, with the authorization code as a GET parameter. It is possible to pass a STATE, but this is optional.

Once the client gets the authorization code, it is necessary to create an access token and ID token.

 -- function: token HOST CLIENT-KEY [#AUTHORIZATION-CODE] [#REFRESH-TOKEN] [#HTTP-GET] [#HTTP-POST] [#CURRENT-TIME]
     Trade an AUTHORIZATION-CODE, or a REFRESH-TOKEN, for an ID token and an access token bound to the CLIENT-KEY issued by HOST, the identity provider.

     You can override the HTTP client used (HTTP-GET and HTTP-POST), and how to compute the time (CURRENT-TIME).

In an application, you would have a list of profiles in XDG_DATA_HOME, consisting of triples (webid, issuer, refresh token).

 -- function: list-profiles [#DIR]
     Read the list of available profiles. Returns a list of triples, webid, issuer, reresh token.

     By default, this function will look for the profiles file in XDG_DATA_HOME. You can bypass it by providing the #DIR optional keyword argument.

 -- function: setup GET-HOST/WEBID CHOOSE-PROVIDER BROWSE-AUTHORIZATION-URI #CLIENT-ID #REDIRECT-URI [#DIR] [#HTTP-GET] [#HTTP-POST] [#CURRENT-TIME]
     Negociate a refresh token, and save it. The function returns 3 values: the decoded ID token pyload, the encoded access token and the key pair.

     The GET-HOST/WEBID thunk should ask the user’s webid or identity provider, and return it. CHOOSE-PROVIDER is called with a list of possible identity providers as host names (strings), and the user should choose one. The chosen one is returned. Finally, BROWSE-AUTHORIZATION-URI should ask or let the user browse an URI as its argument, and return the authorization code taken from the redirect URI.

     The refresh token is saved to disk, as a profile, in XDG_DATA_HOME. Pass the optional #DIR keyword argument to override the location.

     You need to set CLIENT-ID to the public webid of the app, and REDIRECT-URI to one of the approved redirection URIs for the application ID.

 -- function: login WEBID ISSUER REFRESH-TOKEN KEY [#DIR] [#HTTP-GET] [#HTTP-POST] [#CURRENT-TIME]
     If you have already a known profile, you can use it to automatically log in. This function might update the refresh token if it changed, so you can again set #DIR. Please note that the REFRESH-TOKEN is bound to the client KEY on server side, so you must always use the same KEY.

 -- function: refresh ID-TOKEN KEY [#DIR] [#HTTP-GET] [#HTTP-POST] [#CURRENT-TIME]
     If you have an ID token bound to a known profile, this helper function will look up the associated refresh token and log in.

 -- function: make-client ID-TOKEN ACCESS-TOKEN KEY [#DIR] [#HTTP-GET] [#HTTP-POST] [#HTTP-REQUEST] [#CURRENT-TIME]
     Return a replacement of http-request from (web client), that automatically signs requests and refresh the tokens when needed.

     #HTTP-GET and #HTTP-POST are only used to refresh the tokens, while #HTTP-REQUEST is used as a back-end for the requests.

     #CURRENT-TIME is set to a thunk that returns the time. It is used to issue DPoP proofs.

An example application is provided as the webid-oidc-example-app program. It demonstrates how authentication is done. It should help you understand how webid-oidc works.

The identity provider needs to call the application on the web. So, your client should have a public endpoint on the web.

 -- 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.

The webid-oidc client-service command can run a server to serve these resources. It is invoked with the following options:

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

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

-c URI, or --client-id=URI
     sets the global identitifier of the application, which is dereferenced to a semantic resource.

-r URI, or --redirect-uri=URI
     sets the redirection URI.

-C NAME, or --client-name=NAME
     sets the name of your application, so that it is shown when the user gets an authorization. The webid-oidc issuer program that comes with this package does not display it, because it could be dishonest, but other implementations might.

-u URI, or --client-uri=URI
     sets an URI for the identity provider to learn more about your app.

-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.

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

The command is sensitive to the environment variable LANG, which influences how the program is internationalized to the server administrator. This changes the long form of the options, and the language in the log files.

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. All exception types are defined in (webid-oidc errors).

 -- function: error->str ERROR [#DEPTH]
     Return a string explaining the ERROR. You can limit the DEPTH of the explanation as an integer.

Invalid data format
===================

There are a few JSON objects with required fields. This exceptions usually occur as the cause of a higher-level exception.

 -- exception type: &not-base64 VALUE CAUSE
     This exception is raised when the base64 decoding function failed. VALUE is the incorrect input, and CAUSE is a low-level error.

 -- exception type: &not-json VALUE CAUSE
     Cannot decode VALUE to a JSON object.

 -- exception type: &not-turtle VALUE CAUSE
     Cannot decode VALUE to a RDF graph.

 -- exception type: &incorrect-webid-field VALUE
     The VALUE of the webid field in the JWT is missing (if #f), or not an acceptable value.

 -- exception type: &incorrect-iss-field VALUE
     The VALUE of the iss field is incorrect.

 -- exception type: &incorrect-aud-field VALUE
     The VALUE of the aud field is incorrect.

 -- exception type: &incorrect-iat-field VALUE
     The VALUE of the iat field is incorrect.

 -- exception type: &incorrect-exp-field VALUE
     The VALUE of the exp field is incorrect.

 -- exception type: &incorrect-cnf/jkt-field VALUE
     The VALUE of the cnf/jkt field is incorrect.

 -- exception type: &incorrect-client-id-field VALUE
     The VALUE of the client-id field is incorrect.

 -- exception type: &incorrect-typ-field VALUE
     The VALUE of the typ field in the DPoP proof header is incorrect.

 -- exception type: &incorrect-jwk-field VALUE CAUSE
     The VALUE of the jwk field in the DPoP proof header is incorrect.

 -- exception type: &incorrect-jti-field VALUE
     The VALUE of the jti field in the DPoP proof is incorrect.

 -- exception type: &incorrect-htm-field VALUE
     The VALUE of the htm field in the DPoP proof is incorrect.

 -- exception type: &incorrect-htu-field VALUE
     The VALUE of the htu field in the DPoP proof is incorrect.

 -- exception type: &incorrect-redirect-uris-field VALUE
     The VALUE of the redirect-uris field of a client manifest is incorrect.

 -- exception type: &incorrect-typ-field VALUE
     The VALUE of the typ field in the DPoP proof header is incorrect.

 -- exception type: &incorrect-sub-field VALUE
     The VALUE of the sub field is incorrect.

 -- exception type: &incorrect-iss-field VALUE
     The VALUE of the iss field is incorrect.

 -- exception type: &incorrect-nonce-field VALUE
     The VALUE of the nonce field in the DPoP proof is incorrect.

 -- exception type: &incorrect-htm-field VALUE
     The VALUE of the htm field in the DPoP proof is incorrect.

 -- exception type: &not-a-client-manifest VALUE CAUSE
     The CLIENT-MANIFEST is incorrect.

Invalid JWT
===========

Each JWT type – access token, DPoP proof, ID token, authorization code (this is internal to the identity provider) has different validation rules, and can fail in different ways.

 -- exception type: &unsupported-crv CRV
     The identifier CRV does not identify an elliptic curve.

 -- exception type: &not-a-jwk VALUE CAUSE
     VALUE does not identify a JWK.

 -- exception type: &not-a-public-jwk VALUE CAUSE
     VALUE does not identify a public JWK.

 -- exception type: &not-a-private-jwk VALUE CAUSE
     VALUE does not identify a private JWK.

 -- exception type: &not-a-jwks VALUE CAUSE
     VALUE does not identify a set of public keys.

 -- exception type: &unsupported-alg VALUE
     VALUE does not identify a valid hash algorithm.

 -- exception type: &invalid-signature KEY PAYLOAD SIGNATURE
     KEY has not signed PAYLOAD with SIGNATURE.

 -- exception type: &missing-alist-key VALUE KEY
     VALUE isn’t an alist, or is missing a value with KEY.

 -- exception type: &not-a-jws-header VALUE CAUSE
     VALUE does not identify a decoded JWS header.

 -- exception type: &not-a-jws-payload VALUE CAUSE
     VALUE does not identify a decoded JWS payload.

 -- exception type: &not-a-jws VALUE CAUSE
     VALUE does not identify a decoded JWS.

 -- exception type: &not-in-3-parts STRING SEPARATOR
     STRING cannot be split into 3 parts with SEPARATOR.

 -- exception type: &no-matching-key CANDIDATES ALG PAYLOAD SIGNATURE
     No key among CANDIDATES could verify SIGNATURE signed with ALG for PAYLOAD, because the signature mismatched for all keys.

 -- exception type: &cannot-decode-jws VALUE CAUSE
     The VALUE string is not an encoding of a valid JWS.

 -- exception type: &cannot-encode-jws JWS KEY CAUSE
     The JWS cannot be signed.

 -- exception type: &not-an-access-token VALUE CAUSE
     The VALUE is not an access token.

 -- exception type: &not-an-access-token-header VALUE CAUSE
     The VALUE is not an access token header.

 -- exception type: &not-an-access-token-payload VALUE CAUSE
     The VALUE is not an access token payload.

 -- exception type: &cannot-decode-access-token VALUE CAUSE
     The VALUE string is not an encoding of a valid access token.

 -- exception type: &cannot-encode-access-token ACCESS-TOKEN KEY CAUSE
     The ACCESS-TOKEN cannot be signed.

 -- exception type: &not-a-dpop-proof VALUE CAUSE
     The VALUE is not a DPoP proof.

 -- exception type: &not-a-dpop-proof-header VALUE CAUSE
     The VALUE is not a DPoP proof header.

 -- exception type: &not-a-dpop-proof-payload VALUE CAUSE
     The VALUE is not a DPoP proof payload.

 -- exception type: &cannot-decode-dpop-proof VALUE CAUSE
     The VALUE string is not an encoding of a valid DPoP proof.

 -- exception type: &cannot-encode-dpop-proof DPOP-PROOF KEY CAUSE
     The DPOP-PROOF cannot be signed.

 -- exception type: &not-an-authorization-code VALUE CAUSE
     The VALUE is not an authorization code.

 -- exception type: &not-an-authorization-code-header VALUE CAUSE
     The VALUE is not an authorization code header.

 -- exception type: &not-an-authorization-code-payload VALUE CAUSE
     The VALUE is not an authorization code payload.

 -- exception type: &cannot-decode-authorization-code VALUE CAUSE
     The VALUE string is not an encoding of a valid authorization code.

 -- exception type: &cannot-encode-authorization-code AUTHORIZATION-CODE KEY CAUSE
     The AUTHORIZATION-CODE cannot be signed.

 -- exception type: &not-an-id-token VALUE CAUSE
     The VALUE is not an ID token.

 -- exception type: &not-an-id-token-header VALUE CAUSE
     The VALUE is not an ID token header.

 -- exception type: &not-an-id-token-payload VALUE CAUSE
     The VALUE is not an ID token payload.

 -- exception type: &cannot-decode-id-token VALUE CAUSE
     The VALUE string is not an encoding of a valid ID token.

 -- exception type: &cannot-encode-id-token ID-TOKEN KEY CAUSE
     The ID-TOKEN cannot be signed.

Cannot fetch data on the web
============================

In the client (local and public parts), resource server and identity provider, the protocol requires to fetch data on the web.

 -- exception type: &request-failed-unexpectedly RESPONSE-CODE RESPONSE-REASON-PHRASE
     We expected the request to succeed, but the server sent a non-OK RESPONSE-CODE.

 -- exception type: &unexpected-header-value HEADER VALUE
     We did not expect the server to respond with HEADER set to VALUE.

 -- exception type: &unexpected-response RESPONSE CAUSE
     The RESPONSE (from (web response)) is not appropriate.

 -- exception type: &not-an-oidc-configuration VALUE CAUSE
     The VALUE is not appropriate an OIDC configuration.

 -- exception type: &cannot-fetch-issuer-configuration ISSUER CAUSE
     It is impossible to fetch the configuration of ISSUER.

 -- exception type: &cannot-fetch-jwks ISSUER URI CAUSE
     It is impossible to fetch the keys of ISSUER at URI.

 -- exception type: &cannot-fetch-linked-data URI CAUSE
     Could not fetch the graph referenced by URI.

 -- exception type: &cannot-fetch-client-manifest ID CAUSE
     Could not fetch a client manifest at ID.

Other errors in the protocol or from a reasonable implementation
================================================================

The protocol does not rely solely on JWT validation, so these errors may happen too.

 -- exception type: &dpop-method-mismatch SIGNED REQUESTED
     The method value SIGNED in the DPoP proof does not match the method that is REQUESTED on the server.

 -- exception type: &dpop-uri-mismatch SIGNED REQUESTED
     The URI value SIGNED in the DPoP proof does not match the URI that is REQUESTED on the server.

 -- exception type: &dpop-signed-in-future SIGNED CURRENT
     The proof is SIGNED for a date which is too much ahead of the CURRENT time.

 -- exception type: &dpop-too-old SIGNED CURRENT
     The proof was SIGNED at a past date of CURRENT.

 -- exception type: &dpop-unconfirmed-key KEY EXPECTED CAUSE
     The confirmation of KEY is not what is EXPECTED, or (if a function was passed as CNF/CHECK) the CAUSE exception occurred while confirming.

 -- exception type: &jti-found JTI CAUSE
     The JTI of the proof has already been issued in a recent past.

 -- exception type: &unauthorized-redirection-uri MANIFEST URI
     The authorization URI is not advertised in MANIFEST.

 -- exception type: &cannot-serve-public-manifest
     You cannot serve the public client manifest.

 -- exception type: &no-client-manifest-registration ID
     The ID client manifest does not have a registration triple in its document.

 -- exception type: &inconsistent-client-manifest-id ID ADVERTISED-ID
     The client MANIFEST is being fetched at ID, but it is valid for another client ADVERTISED-ID.

 -- exception type: &authorization-code-expired EXP CURRENT-TIME
     The authorization code has expired at EXP, it is now CURRENT-TIME.

 -- exception type: &invalid-refresh-token REFRESH-TOKEN
     The REFRESH-TOKEN is unknown to the identity provider.

 -- exception type: &invalid-key-for-refresh-token KEY JKT
     The refresh token was issued for JKT, but it is used with KEY.

 -- exception type: &unknown-client-locale WEB-LOCALE C-LOCALE
     The WEB-LOCALE of the client, translated to C as C-LOCALE, cannot be set. This exception is always continuable; if the handler returns, then the page will be served in the english locale.

 -- exception type: &unsupported-grant-type VALUE
     The token request failed to indicate a VALUE for the grant type, or indicated an unsupported grant type.

 -- exception type: &no-authorization-code
     The token request forgot to put an authorization code.

 -- exception type: &no-refresh-token
     The token request forgot to put a refresh token with the request.

 -- exception type: &unconfirmed-provider SUBJECT PROVIDER
     PROVIDER is not confirmed by SUBJECT as an identity provider.

 -- exception type: &no-provider-candidates WEBID CAUSES
     The WEBID cannot be certified by any identity providers. The CAUSES alist indicates an error for each candidates.

 -- exception type: &neither-identity-provider-nor-webid URI WHY-NOT-IDENTITY-PROVIDER WHY-NOT-WEBID
     The URI you passed to get an authorization code is neither an identity provider (because WHY-NOT-IDENTITY-PROVIDER) nor a webid (because WHY-NOT-WEBID).

 -- exception type: &token-request-failed CAUSE
     The token request failed on the server.

 -- exception type: &profile-not-found WEBID ISS DIR
     The WEBID, as certified by ISS, cannot be refreshed because we don’t have a refresh token stored in DIR.

Server-side errors
==================

The resource server implementation may encounter some more exceptional conditions.

 -- exception type: &path-not-found PATH
     There is no registered resource at PATH.

 -- exception type: &auxiliary-resource-absent PATH KIND
     The auxiliary resource of given KIND is not instanciated on the server for the base resource PATH.

 -- exception type: &uri-slash-semantics-error PATH EXPECTED-PATH
     While the resource at PATH does not exist, the resource at EXPECTED-PATH does, and PATH and EXPECTED-PATH differ only by a trailing slash. This exception may be raised along with &path-not-found.

     Beware that even if it is true at the time when the exception is created, maybe the resource has been created by the time it is handled.

 -- exception type: &cannot-delete-root
     There was a request to delete the root storage, which is an error.

 -- exception type: &container-not-empty PATH
     There was a request to delete a non-empty container.

 -- exception type: &not-a-container PATH CONTENT-TYPE
     There was a request to create a resource in something that is not a container.

 -- exception type: &cannot-fetch-group GROUP-URI CAUSE
     The access control could not fetch the group GROUP-URI (with a known CAUSE). This warning is continuable every time it is raised. If the handler returns, then the group will be considered empty.

 -- exception type: &incorrect-containment-triples PATH
     The client wanted to create or update a resource, and by that it tried to change the containment triples at PATH.

 -- exception type: &unsupported-media-type CONTENT-TYPE
     The client wanted to create a resource with the given CONTENT-TYPE, but it is not accepted, because CONTENT-TYPE is not recognized as an RDF content type.

 -- exception type: &path-is-auxiliary PATH
     The client wanted to create a resource that targets an auxiliary resource, at PATH.

 -- exception type: &forbidden PATH USER OWNER MODE
     The USER wanted to do something under PATH requiring MODE, but it is not the OWNER and it is forbidden by WAC.

 -- exception type: &precondition-failed PATH IF-MATCH IF-NONE-MATCH REAL-ETAG
     The resource under PATH has a REAL-ETAG that does not match the request headers IF-MATCH and IF-NONE-MATCH.

     If the resource does not exist, REAL-ETAG is set to #f. In this case, an exception of type &path-not-found is also thrown.

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

     [file fdl.texi not included]