summaryrefslogtreecommitdiff
path: root/doc/disfluid.texi
diff options
context:
space:
mode:
Diffstat (limited to 'doc/disfluid.texi')
-rw-r--r--doc/disfluid.texi1428
1 files changed, 1428 insertions, 0 deletions
diff --git a/doc/disfluid.texi b/doc/disfluid.texi
new file mode 100644
index 0000000..bebc61b
--- /dev/null
+++ b/doc/disfluid.texi
@@ -0,0 +1,1428 @@
+\input texinfo @c -*-texinfo-*-
+@comment $Id@w{$}
+@documentlanguage en
+@comment %**start of header
+@include version.texi
+@settitle Demanding Interoperability to Strengthen the Free (Libre) Web: Introducing Disfluid
+@syncodeindex pg cp
+@syncodeindex fn cp
+@syncodeindex vr cp
+@syncodeindex tp cp
+@comment %**end of header
+
+@copying
+This is the manual of disfluid (version @value{VERSION}, @value{UPDATED}), an implementation of the Solid authentication protocol for guile, client and server.
+
+Copyright @copyright{} 2020, 2021 Vivien Kraus
+@quotation
+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''
+@end quotation
+@end copying
+
+@dircategory Software libraries
+
+@direntry
+* disfluid: (disfluid)Interoperability on the web
+@end direntry
+
+@titlepage
+@title Demanding Interoperability to Strengthen the Free (Libre) Web: Introducing Disfluid
+@subtitle for version @value{VERSION}, @value{UPDATED}
+@author Vivien Kraus (@email{vivien@@planete-kraus.eu})
+@page
+@vskip 0pt plus 1fill
+@insertcopying
+@end titlepage
+
+@contents
+@ifnottex
+@node Top
+@top Disfluid
+@end ifnottex
+
+@menu
+* Decentralized Authentication on the Web::
+* Invoking disfluid::
+* The Json Web Token::
+* Caching on server side::
+* Content negociation::
+* Running an Identity Provider::
+* Running a Resource Server::
+* Running a client::
+* Exceptional conditions::
+* GNU Free Documentation License::
+* Index::
+@end menu
+
+@node Decentralized Authentication on the Web
+@chapter 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 @var{useful-program} wants
+to authenticate @var{MegaCorp} users, then @var{useful-program} has to
+register to @var{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.
+
+@node Invoking disfluid
+@chapter Invoking disfluid
+
+The @samp{disfluid} program provides different modes of operations:
+
+@table @samp
+@item 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.
+@item identity-provider
+Run the identity provider only.
+@item client-service
+The client applications must serve some resources: namely, the client
+manifest and the redirect URI.
+@item server
+Run both an identity provider and a resource server.
+@end table
+
+The server is configured with command-line arguments, and environment
+variables.
+
+@menu
+* General options::
+* General server configuration::
+* Configuration for the resource server::
+* Configuration for the identity provider::
+* Configuration for the client service::
+@end menu
+
+@node General options
+@section General options
+The server will respond to @samp{-h} and @samp{-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 @samp{LANG} environment
+variable. So if your locale is not English, you can have the same
+commands as in this manual by running with @code{LANG=C}.
+
+The programs respect the @samp{XDG_DATA_HOME} and
+@samp{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 @samp{HOME}
+environment variable.
+
+@node General server configuration
+@section 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 @samp{disfluid} command provides a
+@samp{--complete-corresponding-source} option so that the system
+administrator can specify a means to download the source.
+
+The servers will add a @samp{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 @samp{--log-file} and
+@samp{--error-file} options.
+
+The server will listen to port 8080 by default, but this may be
+configured with @samp{--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 @samp{--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 @samp{XML_CATALOG_FILES} to a space-separated
+list of URIs (can be @code{file:} URIs). The requests cannot be
+directed to the file system.
+
+@node Configuration for the resource server
+@section Configuration for the resource server
+The reverse proxy sets an identity header to authenticated
+requests. By default, it is @samp{XXX-Agent}, but it can be configured
+with @samp{--header}.
+
+The reverse proxy is configured to contact a backend URI with
+@samp{--backend-uri}. This backend URI should not be directly exposed,
+because a malicious user could set the identity header.
+
+@node Configuration for the identity provider
+@section 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 @samp{--subject}, and the user’s
+password with @samp{--encrypted-password}.
+
+The encrypted password format is defined by the crypt function in the
+C library. For glibc, it looks like this:
+@code{$@var{N}$@var{salt}$@var{hash}}, where @var{N} is the algorithm
+identifier, @var{salt} is the password salt annd @var{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
+@samp{--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 (@samp{--jwks-uri}), authorization endpoint URI
+(@samp{--authorization-endpoint-uri}) and token endpoint URI
+(@samp{--token-endpoint-uri}). The identity provider will publish the
+full URIs, but will respond to their path, regardless of the host.
+
+@node Configuration for the client service
+@section 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
+@samp{--redirect-uri}.
+
+The client ID is set with @samp{--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.
+
+@table @samp
+@item --client-name
+set the name of the application.
+@item --client-uri
+set an URI where to find more information about the client.
+@end table
+
+@node The Json Web Token
+@chapter The Json Web Token
+
+The Json Web Token, or @dfn{JWT}, is a terse representation of a pair
+of JSON objects: the @dfn{header}, and the @dfn{payload}. The JWT can
+be @dfn{encoded} as a Json Web Signature (@dfn{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
+@strong{symbols} to values, arrays are vectors. It is unfortunate that
+guile-json has a slightly different representation, where alist keys
+are @emph{strings}, but we hope that in the future SRFI-180 will be
+more closely respected.
+
+@menu
+* The ID token::
+* The access token::
+* The DPoP proof::
+* Generic JWTs::
+@end menu
+
+@node The ID token
+@section 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:
+
+@table @emph
+@item webid
+the URI of the user’s webid;
+@item iss
+the URI of the identity provider (issuer);
+@item sub
+the username (the webid-oidc issuer puts the webid again here, but it
+could be any string);
+@item aud
+the ID of the client application that is intended to receive the ID
+token;
+@item nonce
+some random data to change the signature;
+@item exp
+an UTC time (in seconds) for when the token expires;
+@item iat
+the time when it was issued.
+@end table
+
+There are functions to work with ID tokens in
+@emph{(webid-oidc oidc-id-token)}.
+
+@deffn function id-token? @var{object}
+Check that @var{object} is a decoded ID token.
+@end deffn
+
+The following helper functions convert URIs to the URIs from
+@emph{(web uri)} and times to @emph{(srfi srfi-19)} dates.
+
+@deffn function id-token-webid @var{token}
+@deffnx function id-token-iss @var{token}
+@deffnx function id-token-sub @var{token}
+@deffnx function id-token-aud @var{token}
+@deffnx function id-token-nonce @var{token}
+@deffnx function id-token-exp @var{token}
+@deffnx function id-token-iat @var{token}
+Get the suitable field from the payload of @var{token}.
+@end deffn
+
+ID tokens can be signed and encoded as a string, or decoded.
+
+@deffn function id-token-decode @var{token} @var{[#http-get]}
+Decode @var{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
+@code{http-get} optional keyword argument can set a different
+implementation of @code{http-get} from @emph{(web client)}. Return
+@code{#f} if it failed, or the decoded token otherwise.
+@end deffn
+
+@deffn function id-token-encode @var{token} @var{key}
+Encode @var{token} and sign it with the issuer’s @var{key}.
+@end deffn
+
+@deffn function issue-id-token @var{issuer-key} @var{#alg} @var{#webid} @var{#iss} @var{#sub} @var{#aud} @var{#exp} @var{#iat}
+Create an ID token, and encode it with @var{issuer-key}.
+@end deffn
+
+@node The access token
+@section 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 @emph{(webid-oidc access-token)}.
+
+@deffn function access-token? @var{object}
+Check that @var{object} is a decoded access token.
+@end deffn
+
+There are field getters for the access token:
+
+@deffn function access-token-webid @var{token}
+@deffnx function access-token-iss @var{token}
+@deffnx function access-token-aud @var{token}
+@deffnx function access-token-exp @var{token}
+@deffnx function access-token-iat @var{token}
+@deffnx function access-token-cnf/jkt @var{token}
+@deffnx function access-token-client-id @var{token}
+Get the suitable field from the payload of @var{token}.
+@end deffn
+
+Access tokens can be signed and encoded as a string, or decoded.
+
+@deffn function access-token-decode @var{token} @var{[#http-get]}
+Decode @var{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 @code{http-get} optional keyword argument can
+set a different implementation of @code{http-get} from
+@emph{(web client)}, for instance to re-use the what has been obtained
+by the ID token validation. Return @code{#f} if it failed, or the
+decoded token otherwise.
+@end deffn
+
+@deffn function access-token-encode @var{token} @var{key}
+Encode @var{token} and sign it with the issuer’s @var{key}.
+@end deffn
+
+@deffn function issue-access-token @var{issuer-key} @var{#alg} @var{#webid} @var{#iss} @var{#exp} @var{#iat} @var{[#client-key} @var{|} @var{#cnf/jkt]} @var{#client-id}
+Create an access token, and encode it with @var{issuer-key}. You can
+either set the @code{#:cnf/jkt} keyword argument with the fingerprint
+of the client key, or set @code{#:client-key} directly, in which case
+the fingerprint will be computed for you.
+@end deffn
+
+@node The DPoP proof
+@section 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.
+
+@deffn function dpop-proof? @var{proof}
+Check that the @var{proof} is a decoded DPoP proof. The validity of
+the proof is not checked by this function.
+@end deffn
+
+@deffn function dpop-proof-alg @var{proof}
+@deffnx function dpop-proof-jwk @var{proof}
+@deffnx function dpop-proof-jti @var{proof}
+@deffnx function dpop-proof-htm @var{proof}
+@deffnx function dpop-proof-htu @var{proof}
+@deffnx function dpop-proof-iat @var{proof}
+@deffnx function dpop-proof-ath @var{proof}
+Get the corresponding field of the proof.
+@end deffn
+
+@deffn function dpop-proof-decode @var{current-time} @var{jti-list} @var{method} @var{uri} @var{str} @var{cnf/check} @var{[#:access-token]}
+Check and decode a DPoP proof encoded as @var{str}.
+
+The @var{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 @var{jti-list} until its expiration date
+is reached. See the @code{make-jti-list} function.
+
+The proof is limited to the scope of one @var{uri} and one
+@var{method} (@code{'GET}, @code{'POST} and so on).
+
+The key that is used to sign the proof should be confirmed by the
+identity provider. To this end, the @var{cnf/check} function is called
+with the fingerprint of the key. The function should check that the
+fingerprint is OK (return a boolean).
+
+Finally, when the DPoP proof is tied to an access token (so, for all
+uses except requesting an access token or a refresh token), it must be
+bound to an @var{access-token}.
+@end deffn
+
+@deffn function make-jti-list
+This function in @emph{(webid-oidc jti-list)} creates an in-memory,
+async-safe, thread-safe cache for the proof IDs.
+@end deffn
+
+@deffn function dpop-proof-encode @var{proof} @var{key}
+Encode the proof and sign it with @var{key}. To generate valid proofs,
+@var{key} should be the private key corresponding to the @code{jwk}
+field of the proof.
+@end deffn
+
+@deffn function issue-dpop-proof @var{client-key} @var{#alg} @var{#htm} @var{#htu} @var{#iat} {[#:@var{access-token}=#f]}
+Create a proof, sign it and encode it with
+@var{client-key}. @var{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. For most uses, the DPoP proof should
+be encoded for a specific access token. Only token requests should
+omit the @samp{access-token} field.
+@end deffn
+
+@node Generic JWTs
+@section Generic JWTs
+
+You can parse generic JWTs signed with JWS with the following
+functions from @emph{(webid-oidc jws)}.
+
+@deffn function jws? @var{jwt}
+Check that @var{jwt} is a decoded JWT signed with JWS.
+@end deffn
+
+@deffn function jws-alg @var{jwt}
+Get the algorithm used to sign @var{jwt}.
+@end deffn
+
+@deffn function jws-decode @var{str} @var{lookup-keys}
+Check and decode a JWT signed with JWS and encoded as @var{str}.
+
+Since the decoding and signature verification happen at the same time
+(for user friendliness), the @var{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.
+@end deffn
+
+@deffn function jws-encode @var{jwt} @var{key}
+Encode the JWT and sign it with @var{key}.
+@end deffn
+
+@node Caching on server side
+@chapter 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 @emph{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 @emph{(webid-oidc cache)} module exports two functions to deal
+with the cache.
+
+@deffn function clean-cache @var{[#percents]} @var{[#dir]}
+Drop @var{percents}% of the cache right now, in @var{dir} (defaults to
+some place within @emph{XDG_CACHE_HOME}).
+@end deffn
+
+@deffn function with-cache @var{[#current-time]} @var{[#http-get]} @var{[#dir]}
+Return a function acting as @emph{http-get} from @emph{(web client)}
+(takes an URI as the first parameter, and an optional @var{#:headers}
+set, and returns 2 values, the response and its body).
+
+The cache will be read and written in @var{dir} (defaults to some
+place within @emph{XDG_CACHE_HOME}), and the @var{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, @var{http-get}, defaults to that of
+@emph{(web client)}.
+@end deffn
+
+@node Content negociation
+@chapter Content negociation
+There are a number of different available syntaxes for RDF, some being
+simple and human readable like @emph{turtle}, and others more adapted
+to the JavaScript ecosystem like @emph{json-ld}. To help clients both
+from and outside of the JS ecosystem, the server needs to perform
+@dfn{content negociation}, i.e. convert from one content-type to
+another.
+
+@deffn {function from @code{(webid-oidc serve)}} convert @var{client-accepts} @var{server-name} @var{path} @var{content-type} @var{content}
+Convert the resource representation under @var{path} on
+@var{server-name}, which has a given @var{content-type} and
+@var{content}, to a content-type that the @var{client accepts}.
+
+Return 2 values:
+@enumerate
+@item
+the accepted content-type;
+@item
+the content in the given content-type.
+@end enumerate
+
+Currently, the only conversions are from and to @emph{Turtle} and
+@emph{N-Quads}.
+@end deffn
+
+@node Running an Identity Provider
+@chapter 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 @code{webid-oidc} program with the
+@code{issuer} command, with the following options:
+
+@table @asis
+@item @code{-h}, or @code{--help}
+prints a summary of options and exit.
+@item @code{-v}, or @code{--version}
+prints the version of the program and exits.
+@item @code{-n @var{URI}}, or @code{--server-name=@var{URI}}
+sets the global server name of the identity provider. It should have
+an empty path.
+@item @code{-k @var{FILE.jwk}}, or @code{--key-file=@var{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.
+@item @code{-s @var{WEBID}}, or @code{--subject=@var{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.
+@item @code{-w @var{PASSWORD}}, or @code{--password=@var{PASSWORD}}
+sets the password that the user must enter to authorize an
+application.
+@item @code{-j @var{URI}}, or @code{--jwks-uri=@var{URI}}
+tells the server that requests to @var{URI} should be responded with
+the public key used to sign the tokens.
+@item @code{-a @var{URI}}, or @code{--authorization-endpoint-uri=@var{URI}}
+tells the server that requests to @var{URI} should be treated as
+authorization requests.
+@item @code{-t @var{URI}}, or @code{--token-endpoint-uri=@var{URI}}
+tells the server that requests to @var{URI} should be treated as token
+negociation requests.
+@item @code{-p @var{PORT}}, or @code{--port=@var{PORT}}
+change the port number used by the server. By default, it is set to
+8080.
+@item @code{-l @var{FILE.log}}, or @code{--log-file=@var{FILE.log}}
+let the server dump all its output to @var{FILE.log}. Since I don’t
+know how to deal with syslog, this is the only way to keep logs with a
+shepherd service.
+@item @code{-e @var{FILE.err}}, or @code{--error-file=@var{FILE.err}}
+let the server dump all its errors to @var{FILE.err}.
+@end table
+
+The program is sensitive to the environment variables. The most
+important one is @emph{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 @emph{XDG_DATA_HOME} should point to some place where the program
+will store refresh tokens, under the @code{webid-oidc} directory. For
+a system service, you might want to define that environment to
+@code{/var/lib}, for instance.
+
+The @emph{XDG_CACHE_HOME} should point to a directory where to store
+the seed of the random number generator (under a @code{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.
+
+@node Running a Resource Server
+@chapter Running a Resource Server
+
+@menu
+* The authenticator::
+* The full server::
+* Resources stored on the server::
+@end menu
+
+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.
+
+@node The authenticator
+@section The authenticator
+
+In @emph{(webid-oidc resource-server)}, the following function gives a
+simple API for a web server:
+
+@deffn function make-authenticator @var{jti-list} @var{[#server-uri]} @var{[#current-time]} @var{[#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
+@code{#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
+@code{current-time} optional keyword argument, to pass a thunk
+returning a time from @emph{(srfi srfi-19)}.
+
+You may want to customize the @var{http-get} optional keyword argument
+to pass a function to replace @code{http-get} from @emph{(http
+client)}. This function takes an URI and optional @code{#:headers}
+arguments, makes the request, and return two values: the response, and
+the response body.
+
+This function, in @emph{(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.
+@end deffn
+
+@node The full server
+@section The full server
+
+@deffn {function from @emph{(webid-oidc resource-server)}} make-server @var{[#:server-uri]} @var{[#:owner]} @var{[#:authenticator]} @var{[#:current-time]} @var{[#: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 @var{[#:authenticator]} argument defaults to the
+webid-oidc authenticator, @var{[#:current-time]} defaults to a thunk
+returning the system time and @var{[#:http-get]} to the web client
+from @emph{(web client)}.
+@end deffn
+
+@node Resources stored on the server
+@section Resources stored on the server
+
+To store and serve resources, the server has two distinct
+mechanisms. A @dfn{content} is a read-only possible value for a
+resource, indexed by etags, and a @dfn{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 @emph{path}. It is also possible to
+mutate separately the ACL and the resource itself without writing a
+copy for both.
+
+The @emph{content} API is contained in the
+@code{(webid-oidc server resource content)} module.
+
+@deffn function with-session @var{f} [@var{#:dir}]
+Call @var{f} with 5 arguments:
+@itemize
+@item
+a function to get the content-type of a given etag;
+@item
+a function to list the paths contained within the resource;
+@item
+a function to load the content of a given etag;
+@item
+a function to create a new content;
+@item
+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.
+@end itemize
+
+Since the contents are read-only, it is possible to cache the value of
+the content in memory. This is why @var{f} should run within a session
+with memoization.
+
+Resources only store @emph{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 @var{XDG_DATA_HOME}, but it
+can be overriden by @var{#:dir}.
+@end deffn
+
+The @emph{path} API is defined in
+@code{(webid-oidc server resource path)}.
+
+@deffn function read-path @var{path}
+Read the resource at @var{path}, and return 2 values:
+@enumerate
+@item
+the ETag of the main resource;
+@item
+an alist where keys are auxiliary resource type URIs (the type is from
+@code{(web uri)}), and the values are ETags of the corresponding
+resource.
+@end enumerate
+
+If the resource is not found, raise an exception with type
+@code{&path-not-found}, and maybe @code{&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.
+@end deffn
+
+@deffn function update-path @var{path} @var{f} @var{content-type} @var{contained} @var{static-content} @var{create} @var{delete} [@var{#:create-intermediate-containers?}=@code{#f}]
+Read @var{path}, call @var{f} with two values: the ETag and the
+auxiliary ETags (as returned by @var{read-path}), and update the path
+accordingly. If @var{path} does not exist, then the first argument is
+@code{#f} and the second one is the empty list.
+
+If @var{f} returns @code{#f}, then the resource is deleted.
+
+If @var{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 @code{PUT} HTTP verb. For @code{POST},
+the parent should exist. The @var{#: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 @var{X}/.lock, where @var{X} is the
+first character of the base64-url sha-256 hash of the
+path. @strong{The lock file is not meant to be removed} when the
+resource is unlocked. It should be locked with @code{flock}
+instead. @strong{Like other forms of lock-based synchronization, this
+function is not composable}. This means that you cannot call this
+function within @var{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.
+@end deffn
+
+The Web Access Control specification defines an RDF vocabulary to
+check whether a given user is allowed to perform some operations. The
+@code{(webid-oidc server resource wac)} helps you do that.
+
+@deffn function wac-get-modes @var{server-name} @var{path} @var{user} @var{[#:http-get]}
+Return the list of modes that are allowed for @var{user} accessing
+@var{path}. The @var{server-name} URI is required to find the relevant
+triples in the ACL. If @var{user} is unauthenticated, pass @code{#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.
+@end deffn
+
+@deffn function check-acl-can-read @var{server-name} @var{path} @var{owner} @var{user} @var{[#:http-get]}
+@deffnx function check-acl-can-write @var{server-name} @var{path} @var{owner} @var{user} @var{[#:http-get]}
+@deffnx function check-acl-can-append @var{server-name} @var{path} @var{owner} @var{user} @var{[#:http-get]}
+@deffnx function check-acl-can-control @var{server-name} @var{path} @var{owner} @var{user} @var{[#:http-get]}
+Assert that the resource at @var{path} on @var{server-name} is owned
+by @var{owner}, and check that @var{user} has the proper
+authorization. Otherwise, raise an exception of type
+@code{&forbidden}.
+@end deffn
+
+@node Running a client
+@chapter 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 @emph{(webid-oidc client)}
+module.
+
+@deffn function authorize @var{host/webid} @var{#client-id} @var{#redirect-uri} @var{[#state]} @var{[#http-get]}
+The user enters a valid webid or a host name, and then this function
+will query it (with the @var{http-get} parameter, by default the web
+client from @emph{(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
+@var{client-id}, that can be dereferenced by the identity provider.
+
+Once the user has given authorization, the user’s agent will be
+redirected to @var{redirect-uri}, with the authorization code as a GET
+parameter. It is possible to pass a @var{state}, but this is optional.
+@end deffn
+
+Once the client gets the authorization code, it is necessary to create
+an access token and ID token.
+
+@deffn function token @var{host} @var{client-key} @var{[#authorization-code]} @var{[#refresh-token]} @var{[#http-get]} @var{[#http-post]} @var{[#current-time]}
+Trade an @var{authorization-code}, or a @var{refresh-token}, for an ID
+token and an access token bound to the @var{client-key} issued by
+@var{host}, the identity provider.
+
+You can override the HTTP client used (@var{http-get} and
+@var{http-post}), and how to compute the time (@var{current-time}).
+@end deffn
+
+In an application, you would have a list of profiles in XDG_DATA_HOME,
+consisting of triples (webid, issuer, refresh token).
+
+@deffn function list-profiles @var{[#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
+@var{XDG_DATA_HOME}. You can bypass it by providing the @var{#dir}
+optional keyword argument.
+@end deffn
+
+@deffn function setup @var{get-host/webid} @var{choose-provider} @var{browse-authorization-uri} @var{#client-id} @var{#redirect-uri} @var{[#dir]} @var{[#http-get]} @var{[#http-post]} @var{[#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 @var{get-host/webid} thunk should ask the user’s webid or identity
+provider, and return it. @var{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,
+@var{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 @var{#dir} keyword argument to
+override the location.
+
+You need to set @var{client-id} to the public webid of the app, and
+@var{redirect-uri} to one of the approved redirection URIs for the
+application ID.
+@end deffn
+
+@deffn function login @var{webid} @var{issuer} @var{refresh-token} @var{key} @var{[#dir]} @var{[#http-get]} @var{[#http-post]} @var{[#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 @var{#dir}. Please note that the @var{refresh-token}
+is bound to the client @var{key} on server side, so you must always
+use the same @var{key}.
+@end deffn
+
+@deffn function refresh @var{id-token} @var{key} @var{[#dir]} @var{[#http-get]} @var{[#http-post]} @var{[#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.
+@end deffn
+
+@deffn function make-client @var{id-token} @var{access-token} @var{key} @var{[#dir]} @var{[#http-get]} @var{[#http-post]} @var{[#http-request]} @var{[#current-time]}
+Return a replacement of @code{http-request} from @emph{(web client)},
+that automatically signs requests and refresh the tokens when needed.
+
+@var{#http-get} and @var{#http-post} are only used to refresh the
+tokens, while @var{#http-request} is used as a back-end for the
+requests.
+
+@var{#current-time} is set to a thunk that returns the time. It is
+used to issue DPoP proofs.
+@end deffn
+
+An example application is provided as the
+@code{disfluid-example-app} program. It demonstrates how
+authentication is done. It should help you understand how Solid-OIDC
+works.
+
+The identity provider needs to call the application on the web. So,
+your client should have a public endpoint on the web.
+
+@deffn function serve-application @var{id} @var{redirect-uri} @var{[#client-name]} @var{[#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 @var{client-name} to your application name and @var{client-uri} to
+point to where to a presentation of your application.
+@end deffn
+
+@node Exceptional conditions
+@chapter 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
+@code{(webid-oidc errors)}.
+
+@deffn function error->str @var{error} @var{[#depth]}
+Return a string explaining the @var{error}. You can limit the
+@var{depth} of the explanation as an integer.
+@end deffn
+
+@menu
+* Invalid data format::
+* Invalid JWT::
+* Cannot fetch data on the web::
+* Other errors in the protocol or from a reasonable implementation::
+* Server-side errors::
+@end menu
+
+@node Invalid data format
+@section Invalid data format
+There are a few JSON objects with required fields. This exceptions
+usually occur as the cause of a higher-level exception.
+
+@deftp {exception type} &not-base64 @var{value} @var{cause}
+This exception is raised when the base64 decoding function
+failed. @var{value} is the incorrect input, and @var{cause} is a
+low-level error.
+@end deftp
+
+@deftp {exception type} &not-json @var{value} @var{cause}
+Cannot decode @var{value} to a JSON object.
+@end deftp
+
+@deftp {exception type} &not-turtle @var{value} @var{cause}
+Cannot decode @var{value} to a RDF graph.
+@end deftp
+
+@deftp {exception type} &incorrect-webid-field @var{value}
+The @var{value} of the webid field in the JWT is missing (if
+@code{#f}), or not an acceptable value.
+@end deftp
+
+@deftp {exception type} &incorrect-iss-field @var{value}
+The @var{value} of the iss field is incorrect.
+@end deftp
+
+@deftp {exception type} &incorrect-aud-field @var{value}
+The @var{value} of the aud field is incorrect.
+@end deftp
+
+@deftp {exception type} &incorrect-iat-field @var{value}
+The @var{value} of the iat field is incorrect.
+@end deftp
+
+@deftp {exception type} &incorrect-exp-field @var{value}
+The @var{value} of the exp field is incorrect.
+@end deftp
+
+@deftp {exception type} &incorrect-cnf/jkt-field @var{value}
+The @var{value} of the cnf/jkt field is incorrect.
+@end deftp
+
+@deftp {exception type} &incorrect-client-id-field @var{value}
+The @var{value} of the client-id field is incorrect.
+@end deftp
+
+@deftp {exception type} &incorrect-typ-field @var{value}
+The @var{value} of the typ field in the DPoP proof header is
+incorrect.
+@end deftp
+
+@deftp {exception type} &incorrect-jwk-field @var{value} @var{cause}
+The @var{value} of the jwk field in the DPoP proof header is
+incorrect.
+@end deftp
+
+@deftp {exception type} &incorrect-jti-field @var{value}
+The @var{value} of the jti field in the DPoP proof is incorrect.
+@end deftp
+
+@deftp {exception type} &incorrect-htm-field @var{value}
+The @var{value} of the htm field in the DPoP proof is incorrect.
+@end deftp
+
+@deftp {exception type} &incorrect-htu-field @var{value}
+The @var{value} of the htu field in the DPoP proof is incorrect.
+@end deftp
+
+@deftp {exception type} &incorrect-ath-field @var{value}
+The @var{value} of the ath field is not the hash of the access token.
+@end deftp
+
+@deftp {exception type} &incorrect-redirect-uris-field @var{value}
+The @var{value} of the redirect-uris field of a client manifest is
+incorrect.
+@end deftp
+
+@deftp {exception type} &incorrect-typ-field @var{value}
+The @var{value} of the typ field in the DPoP proof header is
+incorrect.
+@end deftp
+
+@deftp {exception type} &incorrect-sub-field @var{value}
+The @var{value} of the sub field is incorrect.
+@end deftp
+
+@deftp {exception type} &incorrect-iss-field @var{value}
+The @var{value} of the iss field is incorrect.
+@end deftp
+
+@deftp {exception type} &incorrect-nonce-field @var{value}
+The @var{value} of the nonce field in the DPoP proof is incorrect.
+@end deftp
+
+@deftp {exception type} &incorrect-htm-field @var{value}
+The @var{value} of the htm field in the DPoP proof is incorrect.
+@end deftp
+
+@deftp {exception type} &not-a-client-manifest @var{value} @var{cause}
+The @var{client-manifest} is incorrect.
+@end deftp
+
+@node Invalid JWT
+@section 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.
+
+@deftp {exception type} &unsupported-crv @var{crv}
+The identifier @var{crv} does not identify an elliptic curve.
+@end deftp
+
+@deftp {exception type} &not-a-jwk @var{value} @var{cause}
+@var{value} does not identify a JWK.
+@end deftp
+
+@deftp {exception type} &not-a-public-jwk @var{value} @var{cause}
+@var{value} does not identify a public JWK.
+@end deftp
+
+@deftp {exception type} &not-a-private-jwk @var{value} @var{cause}
+@var{value} does not identify a private JWK.
+@end deftp
+
+@deftp {exception type} &not-a-jwks @var{value} @var{cause}
+@var{value} does not identify a set of public keys.
+@end deftp
+
+@deftp {exception type} &unsupported-alg @var{value}
+@var{value} does not identify a valid hash algorithm.
+@end deftp
+
+@deftp {exception type} &invalid-signature @var{key} @var{payload} @var{signature}
+@var{key} has not signed @var{payload} with @var{signature}.
+@end deftp
+
+@deftp {exception type} &missing-alist-key @var{value} @var{key}
+@var{value} isn’t an alist, or is missing a value with @var{key}.
+@end deftp
+
+@deftp {exception type} &not-a-jws-header @var{value} @var{cause}
+@var{value} does not identify a decoded JWS header.
+@end deftp
+
+@deftp {exception type} &not-a-jws-payload @var{value} @var{cause}
+@var{value} does not identify a decoded JWS payload.
+@end deftp
+
+@deftp {exception type} &not-a-jws @var{value} @var{cause}
+@var{value} does not identify a decoded JWS.
+@end deftp
+
+@deftp {exception type} &not-in-3-parts @var{string} @var{separator}
+@var{string} cannot be split into 3 parts with @var{separator}.
+@end deftp
+
+@deftp {exception type} &no-matching-key @var{candidates} @var{alg} @var{payload} @var{signature}
+No key among @var{candidates} could verify @var{signature} signed with
+@var{alg} for @var{payload}, because the signature mismatched for all
+keys.
+@end deftp
+
+@deftp {exception type} &cannot-decode-jws @var{value} @var{cause}
+The @var{value} string is not an encoding of a valid JWS.
+@end deftp
+
+@deftp {exception type} &cannot-encode-jws @var{jws} @var{key} @var{cause}
+The @var{jws} cannot be signed.
+@end deftp
+
+@deftp {exception type} &not-an-access-token @var{value} @var{cause}
+The @var{value} is not an access token.
+@end deftp
+
+@deftp {exception type} &not-an-access-token-header @var{value} @var{cause}
+The @var{value} is not an access token header.
+@end deftp
+
+@deftp {exception type} &not-an-access-token-payload @var{value} @var{cause}
+The @var{value} is not an access token payload.
+@end deftp
+
+@deftp {exception type} &cannot-decode-access-token @var{value} @var{cause}
+The @var{value} string is not an encoding of a valid access token.
+@end deftp
+
+@deftp {exception type} &cannot-encode-access-token @var{access-token} @var{key} @var{cause}
+The @var{access-token} cannot be signed.
+@end deftp
+
+@deftp {exception type} &not-a-dpop-proof @var{value} @var{cause}
+The @var{value} is not a DPoP proof.
+@end deftp
+
+@deftp {exception type} &not-a-dpop-proof-header @var{value} @var{cause}
+The @var{value} is not a DPoP proof header.
+@end deftp
+
+@deftp {exception type} &not-a-dpop-proof-payload @var{value} @var{cause}
+The @var{value} is not a DPoP proof payload.
+@end deftp
+
+@deftp {exception type} &cannot-decode-dpop-proof @var{value} @var{cause}
+The @var{value} string is not an encoding of a valid DPoP proof.
+@end deftp
+
+@deftp {exception type} &cannot-encode-dpop-proof @var{dpop-proof} @var{key} @var{cause}
+The @var{dpop-proof} cannot be signed.
+@end deftp
+
+@deftp {exception type} &not-an-authorization-code @var{value} @var{cause}
+The @var{value} is not an authorization code.
+@end deftp
+
+@deftp {exception type} &not-an-authorization-code-header @var{value} @var{cause}
+The @var{value} is not an authorization code header.
+@end deftp
+
+@deftp {exception type} &not-an-authorization-code-payload @var{value} @var{cause}
+The @var{value} is not an authorization code payload.
+@end deftp
+
+@deftp {exception type} &cannot-decode-authorization-code @var{value} @var{cause}
+The @var{value} string is not an encoding of a valid authorization
+code.
+@end deftp
+
+@deftp {exception type} &cannot-encode-authorization-code @var{authorization-code} @var{key} @var{cause}
+The @var{authorization-code} cannot be signed.
+@end deftp
+
+@deftp {exception type} &not-an-id-token @var{value} @var{cause}
+The @var{value} is not an ID token.
+@end deftp
+
+@deftp {exception type} &not-an-id-token-header @var{value} @var{cause}
+The @var{value} is not an ID token header.
+@end deftp
+
+@deftp {exception type} &not-an-id-token-payload @var{value} @var{cause}
+The @var{value} is not an ID token payload.
+@end deftp
+
+@deftp {exception type} &cannot-decode-id-token @var{value} @var{cause}
+The @var{value} string is not an encoding of a valid ID token.
+@end deftp
+
+@deftp {exception type} &cannot-encode-id-token @var{id-token} @var{key} @var{cause}
+The @var{id-token} cannot be signed.
+@end deftp
+
+@node Cannot fetch data on the web
+@section 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.
+
+@deftp {exception type} &request-failed-unexpectedly @var{response-code} @var{response-reason-phrase}
+We expected the request to succeed, but the server sent a non-OK
+@var{response-code}.
+@end deftp
+
+@deftp {exception type} &unexpected-header-value @var{header} @var{value}
+We did not expect the server to respond with @var{header} set to
+@var{value}.
+@end deftp
+
+@deftp {exception type} &unexpected-response @var{response} @var{cause}
+The @var{response} (from @emph{(web response)}) is not appropriate.
+@end deftp
+
+@deftp {exception type} &not-an-oidc-configuration @var{value} @var{cause}
+The @var{value} is not appropriate an OIDC configuration.
+@end deftp
+
+@deftp {exception type} &cannot-fetch-issuer-configuration @var{issuer} @var{cause}
+It is impossible to fetch the configuration of @var{issuer}.
+@end deftp
+
+@deftp {exception type} &cannot-fetch-jwks @var{issuer} @var{uri} @var{cause}
+It is impossible to fetch the keys of @var{issuer} at @var{uri}.
+@end deftp
+
+@deftp {exception type} &cannot-fetch-linked-data @var{uri} @var{cause}
+Could not fetch the graph referenced by @var{uri}.
+@end deftp
+
+@deftp {exception type} &cannot-fetch-client-manifest @var{id} @var{cause}
+Could not fetch a client manifest at @var{id}.
+@end deftp
+
+@node Other errors in the protocol or from a reasonable implementation
+@section 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.
+
+@deftp {exception type} &dpop-method-mismatch @var{signed} @var{requested}
+The method value @var{signed} in the DPoP proof does not match the
+method that is @var{requested} on the server.
+@end deftp
+
+@deftp {exception type} &dpop-uri-mismatch @var{signed} @var{requested}
+The URI value @var{signed} in the DPoP proof does not match the URI
+that is @var{requested} on the server.
+@end deftp
+
+@deftp {exception type} &dpop-signed-in-future @var{signed} @var{current}
+The proof is @var{signed} for a date which is too much ahead of the
+@var{current} time.
+@end deftp
+
+@deftp {exception type} &dpop-too-old @var{signed} @var{current}
+The proof was @var{signed} at a past date of @var{current}.
+@end deftp
+
+@deftp {exception type} &dpop-unconfirmed-key @var{key} @var{expected} @var{cause}
+The confirmation of @var{key} is not what is @var{expected}, or (if a
+function was passed as @var{cnf/check}) the @var{cause} exception
+occurred while confirming.
+@end deftp
+
+@deftp {exception type} &dpop-invalid-access-token-hash @var{hash} @var{access-token}
+The @var{access-token} passed to the resource server does not match
+the @var{hash} provided in the DPoP proof.
+@end deftp
+
+@deftp {exception type} &jti-found @var{jti} @var{cause}
+The @var{jti} of the proof has already been issued in a recent past.
+@end deftp
+
+@deftp {exception type} &unauthorized-redirection-uri @var{manifest} @var{uri}
+The authorization @var{uri} is not advertised in @var{manifest}.
+@end deftp
+
+@deftp {exception type} &cannot-serve-public-manifest
+You cannot serve the public client manifest.
+@end deftp
+
+@deftp {exception type} &no-client-manifest-registration @var{id}
+The @var{id} client manifest does not have a registration triple in
+its document.
+@end deftp
+
+@deftp {exception type} &inconsistent-client-manifest-id @var{id} @var{advertised-id}
+The client @var{manifest} is being fetched at @var{id}, but it is
+valid for another client @var{advertised-id}.
+@end deftp
+
+@deftp {exception type} &authorization-code-expired @var{exp} @var{current-time}
+The authorization code has expired at @var{exp}, it is now
+@var{current-time}.
+@end deftp
+
+@deftp {exception type} &invalid-refresh-token @var{refresh-token}
+The @var{refresh-token} is unknown to the identity provider.
+@end deftp
+
+@deftp {exception type} &invalid-key-for-refresh-token @var{key} @var{jkt}
+The refresh token was issued for @var{jkt}, but it is used with
+@var{key}.
+@end deftp
+
+@deftp {exception type} &unknown-client-locale @var{web-locale} @var{c-locale}
+The @var{web-locale} of the client, translated to C as @var{c-locale},
+cannot be set. This exception is always continuable; if the handler
+returns, then the page will be served in the english locale.
+@end deftp
+
+@deftp {exception type} &unsupported-grant-type @var{value}
+The token request failed to indicate a @var{value} for the grant type,
+or indicated an unsupported grant type.
+@end deftp
+
+@deftp {exception type} &no-authorization-code
+The token request forgot to put an authorization code.
+@end deftp
+
+@deftp {exception type} &no-refresh-token
+The token request forgot to put a refresh token with the request.
+@end deftp
+
+@deftp {exception type} &unconfirmed-provider @var{subject} @var{provider}
+@var{provider} is not confirmed by @var{subject} as an identity
+provider.
+@end deftp
+
+@deftp {exception type} &no-provider-candidates @var{webid} @var{causes}
+The @var{webid} cannot be certified by any identity providers. The
+@var{causes} alist indicates an error for each candidates.
+@end deftp
+
+@deftp {exception type} &neither-identity-provider-nor-webid @var{uri} @var{why-not-identity-provider} @var{why-not-webid}
+The @var{uri} you passed to get an authorization code is neither an
+identity provider (because @var{why-not-identity-provider}) nor a
+webid (because @var{why-not-webid}).
+@end deftp
+
+@deftp {exception type} &token-request-failed @var{cause}
+The token request failed on the server.
+@end deftp
+
+@deftp {exception type} &profile-not-found @var{webid} @var{iss} @var{dir}
+The @var{webid}, as certified by @var{iss}, cannot be refreshed
+because we don’t have a refresh token stored in @var{dir}.
+@end deftp
+
+@node Server-side errors
+@section Server-side errors
+The resource server implementation may encounter some more exceptional
+conditions.
+
+@deftp {exception type} &path-not-found @var{path}
+There is no registered resource at @var{path}.
+@end deftp
+
+@deftp {exception type} &auxiliary-resource-absent @var{path} @var{kind}
+The auxiliary resource of given @var{kind} is not instanciated on the
+server for the base resource @var{path}.
+@end deftp
+
+@deftp {exception type} &uri-slash-semantics-error @var{path} @var{expected-path}
+While the resource at @var{path} does not exist, the resource at
+@var{expected-path} does, and @var{path} and @var{expected-path}
+differ only by a trailing slash. This exception may be raised along
+with @code{&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.
+@end deftp
+
+@deftp {exception type} &cannot-delete-root
+There was a request to delete the root storage, which is an error.
+@end deftp
+
+@deftp {exception type} &container-not-empty @var{path}
+There was a request to delete a non-empty container.
+@end deftp
+
+@deftp {exception type} &cannot-fetch-group @var{group-uri} @var{cause}
+The access control could not fetch the group @var{group-uri} (with a
+known @var{cause}). This warning is continuable every time it is
+raised. If the handler returns, then the group will be considered
+empty.
+@end deftp
+
+@deftp {exception type} &incorrect-containment-triples @var{path}
+The client wanted to create or update a resource, and by that it tried
+to change the containment triples at @var{path}.
+@end deftp
+
+@deftp {exception type} &unsupported-media-type @var{content-type}
+The client wanted to create a resource with the given
+@var{content-type}, but it is not accepted, because @var{content-type}
+is not recognized as an RDF content type.
+@end deftp
+
+@deftp {exception type} &path-is-auxiliary @var{path}
+The client wanted to create a resource that targets an auxiliary
+resource, at @var{path}.
+@end deftp
+
+@deftp {exception type} &forbidden @var{path} @var{user} @var{owner} @var{mode}
+The @var{user} wanted to do something under @var{path} requiring
+@var{mode}, but it is not the @var{owner} and it is forbidden by WAC.
+@end deftp
+
+@deftp {exception type} &precondition-failed @var{path} @var{if-match} @var{if-none-match} @var{real-etag}
+The resource under @var{path} has a @var{real-etag} that does not
+match the request headers @var{if-match} and @var{if-none-match}.
+
+If the resource does not exist, @var{real-etag} is set to
+@code{#f}. In this case, an exception of type @code{&path-not-found}
+is also thrown.
+@end deftp
+
+@deftp {exception type} &not-acceptable @var{client-accepts} @var{path} @var{content-type}
+The client wanted a response with a specific set of
+@var{client-accept}ed content-types, but the real @var{content-type}
+of the resource under @var{path} cannot be converted to one of them.
+@end deftp
+@node GNU Free Documentation License
+@appendix GNU Free Documentation License
+
+@include fdl.texi
+
+@node Index
+@unnumbered Index
+
+@printindex cp
+
+@bye