diff options
Diffstat (limited to 'doc/manual.html')
-rw-r--r-- | doc/manual.html | 1337 |
1 files changed, 0 insertions, 1337 deletions
diff --git a/doc/manual.html b/doc/manual.html deleted file mode 100644 index 20a6bc0..0000000 --- a/doc/manual.html +++ /dev/null @@ -1,1337 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<html xmlns="http://www.w3.org/1999/xhtml" - xmlns:info="http://planete-kraus.eu/ns/info" - xml:lang="en"> - <head> - <link rel="stylesheet" type="text/css" href="style.css"/> - <title>Webid-oidc manual</title> - <info:subtitle>for version <info:version />, - <info:updated /></info:subtitle> - <info:copying> - <p> - This is the manual of webid-oidc (version <info:version />, - <info:updated />), an implementation of the Solid - authentication protocol for guile, client and server. - </p> - <p>Copyright <info:copyright-symbol /> 2020, 2021 Vivien - Kraus</p> - <info:quotation> - <p> - 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'' - </p> - </info:quotation> - </info:copying> - <info:author> - <a href="mailto:vivien@planete-kraus.eu">Vivien Kraus</a> - </info:author> - <info:dircategory> - Software libraries - </info:dircategory> - <info:direntry> - <info:direntry-entry name="webid-oidc"> - Decentralized Authentication on the Web. - </info:direntry-entry> - </info:direntry> - </head> - <body> - <h1>Decentralized Authentication on the Web</h1> - <p> - 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 - <info:var>useful-program</info:var> wants to authenticate - <info:var>MegaCorp</info:var> users, then - <info:var>useful-program</info:var> has to register to - <info:var>MegaCorp</info:var> first, and get approved. This goes - against the principle of permission-less innovation, which is at - the heart of the web. - </p> - <p> - 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. - </p> - <p> - 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. - </p> - <p> - 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. - </p> - <p> - 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. - </p> - <h1>The Json Web Token</h1> - <p> - The Json Web Token, or <info:dfn>JWT</info:dfn>, is a terse - representation of a pair of JSON objects: the - <info:dfn>header</info:dfn>, and the - <info:dfn>payload</info:dfn>. The JWT can be - <info:dfn>encoded</info:dfn> as a Json Web Signature - (<info:dfn>JWS</info:dfn>), 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. - </p> - <p> - 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</strong> to values, arrays are - vectors. It is unfortunate that guile-json has a slightly - different representation, where alist keys are - <emph>strings</emph>, but we hope that in the future SRFI-180 - will be more closely respected. - </p> - <h2>The ID token</h2> - <p> - 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: - </p> - <ul> - <li><emph>webid</emph>, the URI of the user’s webid;</li> - <li><emph>iss</emph>, the URI of the identity provider (issuer);</li> - <li><emph>sub</emph>, the username (the webid-oidc issuer puts the webid again here, but it could be any string);</li> - <li><emph>aud</emph>, the ID of the client application that is intended to receive the ID token;</li> - <li><emph>nonce</emph>, some random data to change the signature;</li> - <li><emph>exp</emph>, an UTC time (in seconds) for when the token expires;</li> - <li><emph>iat</emph>, the time when it was issued.</li> - </ul> - <p> - There are functions to work with ID tokens - in <emph>(webid-oidc oidc-id-token)</emph>. - </p> - <info:deffn type="function" name="id-token?" arguments="object"> - <p> - Check that <info:var>object</info:var> is a decoded ID token. - </p> - </info:deffn> - <p> - The following helper functions convert URIs to the URIs - from <emph>(web uri)</emph> and times - to <emph>(srfi srfi-19)</emph> dates. - </p> - <info:deffn type="function" name="id-token-webid" arguments="token"> - <info:deffnx type="function" name="id-token-iss" arguments="token" /> - <info:deffnx type="function" name="id-token-sub" arguments="token" /> - <info:deffnx type="function" name="id-token-aud" arguments="token" /> - <info:deffnx type="function" name="id-token-nonce" arguments="token" /> - <info:deffnx type="function" name="id-token-exp" arguments="token" /> - <info:deffnx type="function" name="id-token-iat" arguments="token" /> - <p> - Get the suitable field from the payload - of <info:var>token</info:var>. - </p> - </info:deffn> - <p> - ID tokens can be signed and encoded as a string, or decoded. - </p> - <info:deffn type="function" name="id-token-decode" arguments="token [#http-get]"> - <p> - Decode <info:var>token</info:var>, 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 <pre>http-get</pre> optional - keyword argument can set a different implementation - of <pre>http-get</pre> - from <emph>(web client)</emph>. Return <pre>#f</pre> if - it failed, or the decoded token otherwise. - </p> - </info:deffn> - <info:deffn type="function" name="id-token-encode" arguments="token key"> - <p> - Encode <info:var>token</info:var> and sign it with the - issuer’s <info:var>key</info:var>. - </p> - </info:deffn> - <info:deffn type="function" name="issue-id-token" arguments="issuer-key #alg #webid #iss #sub #aud #exp #iat"> - <p> - Create an ID token, and encode it with - <info:var>issuer-key</info:var>. - </p> - </info:deffn> - <h2>The access token</h2> - <p> - 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. - </p> - <p> - The API is defined in - <emph>(webid-oidc access-token)</emph>. - </p> - <info:deffn type="function" name="access-token?" arguments="object"> - <p> - Check that <info:var>object</info:var> is a decoded access token. - </p> - </info:deffn> - <p> - There are field getters for the access token: - </p> - <info:deffn type="function" name="access-token-webid" arguments="token"> - <info:deffnx type="function" name="access-token-iss" arguments="token" /> - <info:deffnx type="function" name="access-token-aud" arguments="token" /> - <info:deffnx type="function" name="access-token-exp" arguments="token" /> - <info:deffnx type="function" name="access-token-iat" arguments="token" /> - <info:deffnx type="function" name="access-token-cnf/jkt" arguments="token" /> - <info:deffnx type="function" name="access-token-client-id" arguments="token" /> - <p> - Get the suitable field from the payload - of <info:var>token</info:var>. - </p> - </info:deffn> - <p> - Access tokens can be signed and encoded as a string, or decoded. - </p> - <info:deffn type="function" name="access-token-decode" arguments="token [#http-get]"> - <p> - Decode <info:var>token</info:var>, 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 - <pre>http-get</pre> optional keyword argument can set a - different implementation of <pre>http-get</pre> from - <emph>(web client)</emph>, for instance to re-use the - what has been obtained by the ID token validation. Return - <pre>#f</pre> if it failed, or the decoded token otherwise. - </p> - </info:deffn> - <info:deffn type="function" name="access-token-encode" arguments="token key"> - <p> - Encode <info:var>token</info:var> and sign it with the - issuer’s <info:var>key</info:var>. - </p> - </info:deffn> - <info:deffn type="function" name="issue-access-token" arguments="issuer-key #alg #webid #iss #exp #iat [#client-key | #cnf/jkt] #client-id "> - <p> - Create an access token, and encode it with - <info:var>issuer-key</info:var>. You can either set the - <pre>#:cnf/jkt</pre> keyword argument with the fingerprint of - the client key, or set <pre>#:client-key</pre> directly, in - which case the fingerprint will be computed for you. - </p> - </info:deffn> - <h2>The DPoP proof</h2> - <p> - 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. - </p> - <info:deffn type="function" name="dpop-proof?" arguments="proof"> - <p> - Check that the <info:var>proof</info:var> is a decoded DPoP - proof. The validity of the proof is not checked by this - function. - </p> - </info:deffn> - <info:deffn type="function" name="dpop-proof-alg" arguments="proof"> - <info:deffnx type="function" name="dpop-proof-jwk" arguments="proof" /> - <info:deffnx type="function" name="dpop-proof-jti" arguments="proof" /> - <info:deffnx type="function" name="dpop-proof-htm" arguments="proof" /> - <info:deffnx type="function" name="dpop-proof-htu" arguments="proof" /> - <info:deffnx type="function" name="dpop-proof-iat" arguments="proof" /> - <p> - Get the corresponding field of the proof. - </p> - </info:deffn> - <info:deffn type="function" name="dpop-proof-decode" arguments="current-time jti-list method uri str cnf/check"> - <p> - Check and decode a DPoP proof encoded - as <info:var>str</info:var>. - </p> - <p> - The <info:var>current-time</info:var> is passed as a date, - time or number (of seconds). - </p> - <p> - In order to prevent replay attacks, each proof has a unique - random string that is remembered - in <info:var>jti-list</info:var> until its expiration date is - reached. See the <pre>make-jti-list</pre> function. - </p> - <p> - The proof is limited to the scope of - one <info:var>uri</info:var> and - one <info:var>method</info:var> - (<pre>'GET</pre>, <pre>'POST</pre> and so on). - </p> - <p> - Finally, the key that is used to sign the proof should be - confirmed by the identity provider. To this end, - the <info:var>cnf/check</info:var> function is called with the - fingerprint of the key. The function should check that the - fingerprint is OK (return a boolean). - </p> - </info:deffn> - <info:deffn type="function" - name="make-jti-list" - arguments=""> - <p> - This function in <emph>(webid-oidc jti-list)</emph> - creates an in-memory, async-safe, thread-safe cache for the - proof IDs. - </p> - </info:deffn> - <info:deffn type="function" name="dpop-proof-encode" arguments="proof key"> - <p> - Encode the proof and sign it with <info:var>key</info:var>. To - generate valid proofs, <info:var>key</info:var> should be the - private key corresponding to the <pre>jwk</pre> field of the - proof. - </p> - </info:deffn> - <info:deffn type="function" name="issue-dpop-proof" arguments="client-key #alg #htm #htu #iat"> - <p> - Create a proof, sign it and encode it with - <info:var>client-key</info:var>. <info:var>client-key</info:var> - 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. - </p> - </info:deffn> - <h2>Generic JWTs</h2> - <p> - You can parse generic JWTs signed with JWS with the following - functions from <emph>(webid-oidc jws)</emph>. - </p> - <info:deffn type="function" name="jws?" arguments="jwt"> - <p> - Check that <info:var>jwt</info:var> is a decoded JWT signed - with JWS. - </p> - </info:deffn> - <info:deffn type="function" name="jws-alg" arguments="jwt"> - <p> - Get the algorithm used to sign <info:var>jwt</info:var>. - </p> - </info:deffn> - <info:deffn type="function" name="jws-decode" arguments="str lookup-keys"> - <p> - Check and decode a JWT signed with JWS and encoded - as <info:var>str</info:var>. - </p> - <p> - Since the decoding and signature verification happen at the - same time (for user friendliness), the - <info:var>lookup-keys</info:var> 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. - </p> - </info:deffn> - <info:deffn type="function" name="jws-encode" arguments="jwt key"> - <p> - Encode the JWT and sign it with <info:var>key</info:var>. - </p> - </info:deffn> - <h1>Caching on server side</h1> - <p> - 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. - </p> - <p> - 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</emph>. 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. - </p> - <p> - The <emph>(webid-oidc cache)</emph> module exports two - functions to deal with the cache. - </p> - <info:deffn type="function" name="clean-cache" arguments="[#percents] [#dir]"> - <p> - Drop <info:var>percents</info:var>% of the cache right now, in - <info:var>dir</info:var> (defaults to some place within - <emph>XDG_CACHE_HOME</emph>). - </p> - </info:deffn> - <info:deffn type="function" name="with-cache" arguments="[#current-time] [#http-get] [#dir]"> - <p> - Return a function acting as <emph>http-get</emph> from - <emph>(web client)</emph> (takes an URI as the first - parameter, and an optional <info:var>#:headers</info:var> set, - and returns 2 values, the response and its body). - </p> - <p> - The cache will be read and written in <info:var>dir</info:var> - (defaults to some place within <emph>XDG_CACHE_HOME</emph>), - and the <info:var>current-time</info:var> number of seconds, - SRFI-19 time or date, or time-returning thunk will be used to - check for the validity of responses. - </p> - <p> - The back-end function, <info:var>http-get</info:var>, defaults - to that of <emph>(web client)</emph>. - </p> - </info:deffn> - <h1>What if something goes wrong?</h1> - <p> - 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 - <emph>(webid-oidc errors)</emph>. - </p> - <info:deffn type="function" name="error->str" arguments="error [#depth]"> - <p> - Return a string explaining the <info:var>error</info:var>. You - can limit the <info:var>depth</info:var> of the explanation as - an integer. - </p> - </info:deffn> - <info:deftp type="exception type" name="&not-base64" arguments="value cause"> - <p> - This exception is raised when the base64 decoding function - failed. <info:var>value</info:var> is the incorrect input, - and <info:var>cause</info:var> is a low-level error. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-json" arguments="value cause"> - <p> - Cannot decode <info:var>value</info:var> to a JSON object. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-turtle" arguments="value cause"> - <p> - Cannot decode <info:var>value</info:var> to a RDF graph. - </p> - </info:deftp> - <info:deftp type="exception type" name="&unsupported-crv" arguments="crv"> - <p> - The identifier <info:var>crv</info:var> does not identify an - elliptic curve. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-a-jwk" arguments="value cause"> - <p> - <info:var>value</info:var> does not identify a JWK. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-a-public-jwk" arguments="value cause"> - <p> - <info:var>value</info:var> does not identify a public JWK. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-a-private-jwk" arguments="value cause"> - <p> - <info:var>value</info:var> does not identify a private JWK. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-a-jwks" arguments="value cause"> - <p> - <info:var>value</info:var> does not identify a set of public keys. - </p> - </info:deftp> - <info:deftp type="exception type" name="&unsupported-alg" arguments="value"> - <p> - <info:var>value</info:var> does not identify a valid hash algorithm. - </p> - </info:deftp> - <info:deftp type="exception type" name="&invalid-signature" arguments="key payload signature"> - <p> - <info:var>key</info:var> has not signed - <info:var>payload</info:var> with - <info:var>signature</info:var>. - </p> - </info:deftp> - <info:deftp type="exception type" name="&missing-alist-key" arguments="value key"> - <p> - <info:var>value</info:var> isn’t an alist, or is missing a - value with <info:var>key</info:var>. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-a-jws-header" arguments="value cause"> - <p> - <info:var>value</info:var> does not identify a decoded JWS header. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-a-jws-payload" arguments="value cause"> - <p> - <info:var>value</info:var> does not identify a decoded JWS payload. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-a-jws" arguments="value cause"> - <p> - <info:var>value</info:var> does not identify a decoded JWS. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-in-3-parts" arguments="string separator"> - <p> - <info:var>string</info:var> cannot be split into 3 parts with - <info:var>separator</info:var>. - </p> - </info:deftp> - <info:deftp type="exception type" name="&no-matching-key" arguments="candidates alg payload signature"> - <p> - No key among <info:var>candidates</info:var> could verify - <info:var>signature</info:var> signed with - <info:var>alg</info:var> for <info:var>payload</info:var>, - because the signature mismatched for all keys. - </p> - </info:deftp> - <info:deftp type="exception type" name="&cannot-decode-jws" arguments="value cause"> - <p> - The <info:var>value</info:var> string is not an encoding of a valid JWS. - </p> - </info:deftp> - <info:deftp type="exception type" name="&cannot-encode-jws" arguments="jws key cause"> - <p> - The <info:var>jws</info:var> cannot be signed. - </p> - </info:deftp> - <info:deftp type="exception type" name="&request-failed-unexpectedly" arguments="response-code response-reason-phrase"> - <p> - We expected the request to succeed, but the server sent a - non-OK <info:var>response-code</info:var>. - </p> - </info:deftp> - <info:deftp type="exception type" name="&unexpected-header-value" arguments="header value"> - <p> - We did not expect the server to respond with - <info:var>header</info:var> set to <info:var>value</info:var>. - </p> - </info:deftp> - <info:deftp type="exception type" name="&unexpected-response" arguments="response cause"> - <p> - The <info:var>response</info:var> (from - <emph>(web response)</emph>) is not appropriate. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-an-oidc-configuration" arguments="value cause"> - <p> - The <info:var>value</info:var> is not an OIDC configuration. - </p> - </info:deftp> - <info:deftp type="exception type" name="&incorrect-webid-field" arguments="value"> - <p> - The <info:var>value</info:var> of the webid field in the JWT - is missing (if <pre>#f</pre>), or not an acceptable value. - </p> - </info:deftp> - <info:deftp type="exception type" name="&incorrect-sub-field" arguments="value"> - <p> - The <info:var>value</info:var> of the sub field is incorrect. - </p> - </info:deftp> - <info:deftp type="exception type" name="&incorrect-iss-field" arguments="value"> - <p> - The <info:var>value</info:var> of the iss field is incorrect. - </p> - </info:deftp> - <info:deftp type="exception type" name="&incorrect-aud-field" arguments="value"> - <p> - The <info:var>value</info:var> of the aud field is incorrect. - </p> - </info:deftp> - <info:deftp type="exception type" name="&incorrect-iat-field" arguments="value"> - <p> - The <info:var>value</info:var> of the iat field is incorrect. - </p> - </info:deftp> - <info:deftp type="exception type" name="&incorrect-exp-field" arguments="value"> - <p> - The <info:var>value</info:var> of the exp field is incorrect. - </p> - </info:deftp> - <info:deftp type="exception type" name="&incorrect-cnf/jkt-field" arguments="value"> - <p> - The <info:var>value</info:var> of the cnf/jkt field is incorrect. - </p> - </info:deftp> - <info:deftp type="exception type" name="&incorrect-client-id-field" arguments="value"> - <p> - The <info:var>value</info:var> of the client-id field is incorrect. - </p> - </info:deftp> - <info:deftp type="exception type" name="&incorrect-redirect-uris-field" arguments="value"> - <p> - The <info:var>value</info:var> of the redirect-uris field of a - client manifest is incorrect. - </p> - </info:deftp> - <info:deftp type="exception type" name="&incorrect-typ-field" arguments="value"> - <p> - The <info:var>value</info:var> of the typ field in the DPoP proof - header is incorrect. - </p> - </info:deftp> - <info:deftp type="exception type" name="&incorrect-jwk-field" arguments="value cause"> - <p> - The <info:var>value</info:var> of the jwk field in the DPoP - proof header is incorrect. - </p> - </info:deftp> - <info:deftp type="exception type" name="&incorrect-jti-field" arguments="value"> - <p> - The <info:var>value</info:var> of the jti field in the DPoP - proof is incorrect. - </p> - </info:deftp> - <info:deftp type="exception type" name="&incorrect-nonce-field" arguments="value"> - <p> - The <info:var>value</info:var> of the nonce field in the DPoP - proof is incorrect. - </p> - </info:deftp> - <info:deftp type="exception type" name="&incorrect-htm-field" arguments="value"> - <p> - The <info:var>value</info:var> of the htm field in the DPoP - proof is incorrect. - </p> - </info:deftp> - <info:deftp type="exception type" name="&incorrect-htu-field" arguments="value"> - <p> - The <info:var>value</info:var> of the htu field in the DPoP - proof is incorrect. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-an-access-token" arguments="value cause"> - <p> - The <info:var>value</info:var> is not an access token. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-an-access-token-header" arguments="value cause"> - <p> - The <info:var>value</info:var> is not an access token header. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-an-access-token-payload" arguments="value cause"> - <p> - The <info:var>value</info:var> is not an access token payload. - </p> - </info:deftp> - <info:deftp type="exception type" name="&cannot-fetch-issuer-configuration" arguments="issuer cause"> - <p> - It is impossible to fetch the configuration of - <info:var>issuer</info:var>. - </p> - </info:deftp> - <info:deftp type="exception type" name="&cannot-fetch-jwks" arguments="issuer uri cause"> - <p> - It is impossible to fetch the keys of - <info:var>issuer</info:var> at <info:var>uri</info:var>. - </p> - </info:deftp> - <info:deftp type="exception type" name="&cannot-decode-access-token" arguments="value cause"> - <p> - The <info:var>value</info:var> string is not an encoding of a - valid access token. - </p> - </info:deftp> - <info:deftp type="exception type" name="&cannot-encode-access-token" arguments="access-token key cause"> - <p> - The <info:var>access-token</info:var> cannot be signed. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-a-dpop-proof" arguments="value cause"> - <p> - The <info:var>value</info:var> is not a DPoP proof. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-a-dpop-proof-header" arguments="value cause"> - <p> - The <info:var>value</info:var> is not a DPoP proof header. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-a-dpop-proof-payload" arguments="value cause"> - <p> - The <info:var>value</info:var> is not a DPoP proof payload. - </p> - </info:deftp> - <info:deftp type="exception type" name="&dpop-method-mismatch" arguments="signed requested"> - <p> - The method value <info:var>signed</info:var> in the DPoP proof - does not match the method that is - <info:var>requested</info:var> on the server. - </p> - </info:deftp> - <info:deftp type="exception type" name="&dpop-uri-mismatch" arguments="signed requested"> - <p> - The URI value <info:var>signed</info:var> in the DPoP proof - does not match the URI that is <info:var>requested</info:var> - on the server. - </p> - </info:deftp> - <info:deftp type="exception type" name="&dpop-signed-in-future" arguments="signed current"> - <p> - The proof is <info:var>signed</info:var> for a date which is - too much ahead of the <info:var>current</info:var> time. - </p> - </info:deftp> - <info:deftp type="exception type" name="&dpop-too-old" arguments="signed current"> - <p> - The proof was <info:var>signed</info:var> at a past date of - <info:var>current</info:var>. - </p> - </info:deftp> - <info:deftp type="exception type" name="&dpop-unconfirmed-key" arguments="key expected cause"> - <p> - The confirmation of <info:var>key</info:var> is not what is - <info:var>expected</info:var>, or (if a function was passed as - <info:var>cnf/check</info:var>) the <info:var>cause</info:var> - exception occurred while confirming. - </p> - </info:deftp> - <info:deftp type="exception type" name="&jti-found" arguments="jti cause"> - <p> - The <info:var>jti</info:var> of the proof has already been - issued in a recent past. - </p> - </info:deftp> - <info:deftp type="exception type" name="&cannot-decode-dpop-proof" arguments="value cause"> - <p> - The <info:var>value</info:var> string is not an encoding of a - valid DPoP proof. - </p> - </info:deftp> - <info:deftp type="exception type" name="&cannot-encode-dpop-proof" arguments="dpop-proof key cause"> - <p> - The <info:var>dpop-proof</info:var> cannot be signed. - </p> - </info:deftp> - <info:deftp type="exception type" name="&cannot-fetch-linked-data" arguments="uri cause"> - <p> - Could not fetch the graph referenced by - <info:var>uri</info:var>. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-a-client-manifest" arguments="value cause"> - <p> - The <info:var>client-manifest</info:var> is incorrect. - </p> - </info:deftp> - <info:deftp type="exception type" name="&unauthorized-redirection-uri" arguments="manifest uri"> - <p> - The authorization <info:var>uri</info:var> is not advertised - in <info:var>manifest</info:var>. - </p> - </info:deftp> - <info:deftp type="exception type" name="&cannot-serve-public-manifest" arguments=""> - <p> - You cannot serve the public client manifest. - </p> - </info:deftp> - <info:deftp type="exception type" name="&no-client-manifest-registration" arguments="id"> - <p> - The <info:var>id</info:var> client manifest does not have a - registration triple in its document. - </p> - </info:deftp> - <info:deftp type="exception type" name="&inconsistent-client-manifest-id" arguments="id advertised-id"> - <p> - The client <info:var>manifest</info:var> is being fetched at - <info:var>id</info:var>, but it is valid for another client - <info:var>advertised-id</info:var>. - </p> - </info:deftp> - <info:deftp type="exception type" name="&cannot-fetch-client-manifest" arguments="id cause"> - <p> - Could not fetch a client manifest at <info:var>id</info:var>. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-an-authorization-code" arguments="value cause"> - <p> - The <info:var>value</info:var> is not an authorization code. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-an-authorization-code-header" arguments="value cause"> - <p> - The <info:var>value</info:var> is not an authorization code header. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-an-authorization-code-payload" arguments="value cause"> - <p> - The <info:var>value</info:var> is not an authorization code payload. - </p> - </info:deftp> - <info:deftp type="exception type" name="&authorization-code-expired" arguments="exp current-time"> - <p> - The authorization code has expired at - <info:var>exp</info:var>, it is now - <info:var>current-time</info:var>. - </p> - </info:deftp> - <info:deftp type="exception type" name="&cannot-decode-authorization-code" arguments="value cause"> - <p> - The <info:var>value</info:var> string is not an encoding of a - valid authorization code. - </p> - </info:deftp> - <info:deftp type="exception type" name="&cannot-encode-authorization-code" arguments="authorization-code key cause"> - <p> - The <info:var>authorization-code</info:var> cannot be signed. - </p> - </info:deftp> - <info:deftp type="exception type" name="&invalid-refresh-token" arguments="refresh-token"> - <p> - The <info:var>refresh-token</info:var> is unknown to the - identity provider. - </p> - </info:deftp> - <info:deftp type="exception type" name="&invalid-key-for-refresh-token" arguments="key jkt"> - <p> - The refresh token was issued for <info:var>jkt</info:var>, but - it is used with <info:var>key</info:var>. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-an-id-token" arguments="value cause"> - <p> - The <info:var>value</info:var> is not an ID token. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-an-id-token-header" arguments="value cause"> - <p> - The <info:var>value</info:var> is not an ID token header. - </p> - </info:deftp> - <info:deftp type="exception type" name="&not-an-id-token-payload" arguments="value cause"> - <p> - The <info:var>value</info:var> is not an ID token payload. - </p> - </info:deftp> - <info:deftp type="exception type" name="&cannot-decode-id-token" arguments="value cause"> - <p> - The <info:var>value</info:var> string is not an encoding of a - valid ID token. - </p> - </info:deftp> - <info:deftp type="exception type" name="&cannot-encode-id-token" arguments="id-token key cause"> - <p> - The <info:var>id-token</info:var> cannot be signed. - </p> - </info:deftp> - <info:deftp type="exception type" name="&unknown-client-locale" arguments="web-locale c-locale"> - <p> - The <info:var>web-locale</info:var> of the client, translated - to C as <info:var>c-locale</info:var>, cannot be set. This - exception is always continuable; if the handler returns, then - the page will be served in the english locale. - </p> - </info:deftp> - <info:deftp type="exception type" name="&unsupported-grant-type" arguments="value"> - <p> - The token request failed to indicate a - <info:var>value</info:var> for the grant type, or indicated an - unsupported grant type. - </p> - </info:deftp> - <info:deftp type="exception type" name="&no-authorization-code" arguments=""> - <p> - The token request forgot to put an authorization code. - </p> - </info:deftp> - <info:deftp type="exception type" name="&no-refresh-token" arguments=""> - <p> - The token request forgot to put a refresh token with the - request. - </p> - </info:deftp> - <info:deftp type="exception type" name="&unconfirmed-provider" arguments="subject provider"> - <p> - <info:var>provider</info:var> is not confirmed by - <info:var>subject</info:var> as an identity provider. - </p> - </info:deftp> - <info:deftp type="exception type" name="&no-provider-candidates" arguments="webid causes"> - <p> - The <info:var>webid</info:var> cannot be certified by any - identity providers. The <info:var>causes</info:var> alist - indicates an error for each candidates. - </p> - </info:deftp> - <info:deftp type="exception type" name="&neither-identity-provider-nor-webid" arguments="uri why-not-identity-provider why-not-webid"> - <p> - The <info:var>uri</info:var> you passed to get an - authorization code is neither an identity provider (because - <info:var>why-not-identity-provider</info:var>) nor a webid - (because <info:var>why-not-webid</info:var>). - </p> - </info:deftp> - <info:deftp type="exception type" name="&token-request-failed" arguments="cause"> - <p> - The token request failed on the server. - </p> - </info:deftp> - <info:deftp type="exception type" name="&profile-not-found" arguments="webid iss dir"> - <p> - The <info:var>webid</info:var>, as certified by - <info:var>iss</info:var>, cannot be refreshed because we don’t - have a refresh token stored in <info:var>dir</info:var>. - </p> - </info:deftp> - - <h1>Running an Identity Provider</h1> - <p> - 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. - </p> - <p> - You can start it by invoking the <pre>webid-oidc-issuer</pre> - program, with the following options: - </p> - <ul> - <li> - <pre>-h</pre>, or <pre>--help</pre> prints a summary of - options and exit. - </li> - <li> - <pre>-v</pre>, or <pre>--version</pre> prints the version of - the program and exits. - </li> - <li> - <pre>-i <info:var>URI</info:var></pre>, or - <pre>--issuer=<info:var>URI</info:var></pre> sets the global - server name of the identity provider. It should have an empty - path. - </li> - <li> - <pre>-k <info:var>FILE.jwk</info:var></pre>, or - <pre>--key-file=<info:var>FILE.jwk</info:var></pre> 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. - </li> - <li> - <pre>-s <info:var>WEBID</info:var></pre>, or - <pre>--subject=<info:var>WEBID</info:var></pre> 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. - </li> - <li> - <pre>-w <info:var>PASSWORD</info:var></pre>, or - <pre>--password=<info:var>PASSWORD</info:var></pre>, sets the - password that the user must enter to authorize an - application. - </li> - <li> - <pre>-j <info:var>URI</info:var></pre>, or - <pre>--jwks-uri=<info:var>URI</info:var></pre> tells the - server that requests to <info:var>URI</info:var> should be - responded with the public key used to sign the tokens. - </li> - <li> - <pre>-a <info:var>URI</info:var></pre>, or - <pre>--authorization-endpoint-uri=<info:var>URI</info:var></pre> - tells the server that requests to <info:var>URI</info:var> - should be treated as authorization requests. - </li> - <li> - <pre>-t <info:var>URI</info:var></pre>, or - <pre>--token-endpoint-uri=<info:var>URI</info:var></pre> tells - the server that requests to <info:var>URI</info:var> should be - treated as token negociation requests. - </li> - <li> - <pre>-p <info:var>PORT</info:var></pre>, or - <pre>--port=<info:var>PORT</info:var></pre>, change the port - number used by the server. By default, it is set to 8080. - </li> - <li> - <pre>-l <info:var>FILE.log</info:var></pre>, or - <pre>--log-file=<info:var>FILE.log</info:var></pre> let the - server dump all its output to - <info:var>FILE.log</info:var>. Since I don’t know how to deal - with syslog, this is the only way to keep logs with a shepherd - service. - </li> - <li> - <pre>-e <info:var>FILE.err</info:var></pre>, or - <pre>--error-file=<info:var>FILE.err</info:var></pre> let the - server dump all its errors to <info:var>FILE.err</info:var>. - </li> - </ul> - <p> - The program is sensitive to the environment variables. The most - important one is <emph>LANG</emph>, 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. - </p> - <p> - The <emph>XDG_DATA_HOME</emph> should point to some place where - the program will store refresh tokens, under the - <pre>webid-oidc</pre> directory. For a system service, you might - want to define that environment to <pre>/var/lib</pre>, for - instance. - </p> - <p> - The <emph>XDG_CACHE_HOME</emph> should point to a directory - where to store the seed of the random number generator (under a - <pre>webid-oidc</pre> 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. - </p> - <h1>Running a Resource Server</h1> - <p> - 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. - </p> - <h2>Running webid-oidc-reverse-proxy</h2> - <p> - The distribution comes with a reverse proxy, aptly named - <pre>webid-oidc-reverse-proxy</pre>, 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. - </p> - <p>The reverse proxy is invoked with the following arguments:</p> - <ul> - <li> - <pre>-p</pre> <info:var>PORT</info:var>, - <pre>--port=</pre><info:var>PORT</info:var>: the port on which - the reverse proxy listens; - </li> - <li> - <pre>-i</pre> <info:var>INBOUND</info:var>, - <pre>--inbound-uri=</pre><info:var>INBOUND</info:var>: the - public name of the server; - </li> - <li> - <pre>-o</pre> <info:var>OUTBOUND</info:var>, - <pre>--outbound-uri=</pre><info:var>OUTBOUND</info:var>: the - address of the backend; - </li> - <li> - <pre>-H</pre> <info:var>HEADER</info:var>, - <pre>--header=</pre><info:var>HEADER</info:var>: replace the - name of the header that will contain the webid of the - user. Defaults to <pre>XXX-Agent</pre>. 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. - </li> - <li> - <pre>-l <info:var>FILE.log</info:var></pre>, or - <pre>--log-file=<info:var>FILE.log</info:var></pre> let the - server dump all its output to - <info:var>FILE.log</info:var>. See the identity provider - comment. - </li> - <li> - <pre>-e <info:var>FILE.err</info:var></pre>, or - <pre>--error-file=<info:var>FILE.err</info:var></pre> let the - server dump all its errors to <info:var>FILE.err</info:var>. - </li> - </ul> - <p> - You can localize the interface by setting the - <info:var>LANG</info:var> environment variable. - </p> - <h2>The authenticator</h2> - <p> - In <emph>(webid-oidc jws)</emph>, the following function - gives a simple API for a web server: - </p> - <info:deffn type="function" - name="make-authenticator" - arguments="jti-list [#server-uri] [#current-time] [#http-get]"> - <p> - Create an authenticator, i.e. a function that takes a request - and request body and returns the webid of the authenticated - user, or <pre>#f</pre> if it is not authenticated. - </p> - <p> - 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. - </p> - <p> - 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. - </p> - <p> - The JTIs are checked within a small time frame. By default, - the system time will be used. Otherwise, you can customize the - <pre>current-time</pre> optional keyword argument, to pass a - thunk returning a time from <emph>(srfi srfi-19)</emph>. - </p> - <p> - You may want to customize the <info:var>http-get</info:var> - optional keyword argument to pass a function to replace - <pre>http-get</pre> from <emph>(http client)</emph>. This - function takes an URI and optional <pre>#:headers</pre> - arguments, makes the request, and return two values: the - response, and the response body. - </p> - <p> - This function, in - <emph>(webid-oidc resource-server)</emph>, 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. - </p> - </info:deffn> - - <h1>Running a client</h1> - <p> - 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. - </p> - <p> - The first operation is performed by the - <emph>(webid-oidc client)</emph> module. - </p> - <info:deffn type="function" name="authorize" arguments="host/webid #client-id #redirect-uri [#state] [#http-get]"> - <p> - The user enters a valid webid or a host name, and then this - function will query it (with the <info:var>http-get</info:var> - parameter, by default the web client from - <emph>(web client)</emph>) 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. - </p> - <p> - Each application should have its own webid, or in that case - <info:var>client-id</info:var>, that can be dereferenced by - the identity provider. - </p> - <p> - Once the user has given authorization, the user’s agent will - be redirected to <info:var>redirect-uri</info:var>, with the - authorization code as a GET parameter. It is possible to pass - a <info:var>state</info:var>, but this is optional. - </p> - </info:deffn> - <p> - Once the client gets the authorization code, it is necessary to - create an access token and ID token. - </p> - <info:deffn type="function" name="token" arguments="host client-key [#authorization-code] [#refresh-token] [#http-get] [#http-post] [#current-time]"> - <p> - Trade an <info:var>authorization-code</info:var>, or a - <info:var>refresh-token</info:var>, for an ID token and an - access token bound to the <info:var>client-key</info:var> - issued by <info:var>host</info:var>, the identity provider. - </p> - <p> - You can override the HTTP client used - (<info:var>http-get</info:var> and - <info:var>http-post</info:var>), and how to compute the time - (<info:var>current-time</info:var>). - </p> - </info:deffn> - <p> - In an application, you would have a list of profiles in - XDG_DATA_HOME, consisting of triples (webid, issuer, refresh - token). - </p> - <info:deffn type="function" name="list-profiles" arguments="[#dir]"> - <p> - Read the list of available profiles. Returns a list of - triples, webid, issuer, reresh token. - </p> - <p> - By default, this function will look for the profiles file in - XDG_DATA_HOME. You can bypass it by providing the - <info:var>#dir</info:var> optional keyword argument. - </p> - </info:deffn> - <info:deffn type="function" name="setup" arguments="get-host/webid choose-provider browse-authorization-uri #client-id #redirect-uri [#dir] [#http-get] [#http-post] [#current-time]"> - <p> - 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. - </p> - <p> - The <info:var>get-host/webid</info:var> thunk should ask the - user’s webid or identity provider, and return - it. <info:var>choose-provider</info:var> 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, - <info:var>browse-authorization-uri</info:var> should ask or - let the user browse an URI as its argument, and return the - authorization code taken from the redirect URI. - </p> - <p> - The refresh token is saved to disk, as a profile, in - XDG_DATA_HOME. Pass the optional <info:var>#dir</info:var> - keyword argument to override the location. - </p> - <p> - You need to set <info:var>client-id</info:var> to the public - webid of the app, and <info:var>redirect-uri</info:var> to one - of the approved redirection URIs for the application ID. - </p> - </info:deffn> - <info:deffn type="function" name="login" arguments="webid issuer refresh-token key [#dir] [#http-get] [#http-post] [#current-time]"> - <p> - 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 - <info:var>#dir</info:var>. Please note that the - <info:var>refresh-token</info:var> is bound to the client - <info:var>key</info:var> on server side, so you must always - use the same <info:var>key</info:var>. - </p> - </info:deffn> - <info:deffn type="function" name="refresh" arguments="id-token key [#dir] [#http-get] [#http-post] [#current-time]"> - <p> - If you have an ID token bound to a known profile, this helper - function will look up the associated refresh token and log in. - </p> - </info:deffn> - <info:deffn type="function" name="make-client" arguments="id-token access-token key [#dir] [#http-get] [#http-post] [#http-request] [#current-time]"> - <p> - Return a replacement of <pre>http-request</pre> from - <emph>(web client)</emph>, that automatically signs - requests and refresh the tokens when needed. - </p> - <p> - <info:var>#http-get</info:var> and - <info:var>#http-post</info:var> are only used to refresh the - tokens, while <info:var>#http-request</info:var> is used as a - back-end for the requests. - </p> - <p> - <info:var>#current-time</info:var> is set to a thunk that - returns the time. It is used to issue DPoP proofs. - </p> - </info:deffn> - <p> - An example application is provided as the - <pre>webid-oidc-example-app</pre> program. It demonstrates how - authentication is done. It should help you understand how - webid-oidc works. - </p> - <p> - The identity provider needs to call the application on the - web. So, your client should have a public endpoint on the web. - </p> - <info:deffn type="function" name="serve-application" arguments="id redirect-uri [#client-name] [#client-uri]"> - <p> - Return a handler for web requests to serve the application - manifest and the redirection to transmit the authorization - code. You should set the <info:var>client-name</info:var> to - your application name and <info:var>client-uri</info:var> to - point to where to a presentation of your application. - </p> - </info:deffn> - <p> - The <pre>webid-oidc-client-service</pre> program can run a - server to serve these resources. It is invoked with the - following options: - </p> - <ul> - <li> - <pre>-h</pre>, or <pre>--help</pre> prints a summary of the - options and exit. - </li> - <li> - <pre>-v</pre>, or <pre>--version</pre> prints the version of - the program and exits. - </li> - <li> - <pre>-i <info:var>URI</info:var></pre>, or - <pre>--client-id=<info:var>URI</info:var></pre> sets the - global identitifier of the application, which is dereferenced - to a semantic resource. - </li> - <li> - <pre>-r <info:var>URI</info:var></pre>, or - <pre>--redirect-uri=<info:var>URI</info:var></pre> sets the - redirection URI. - </li> - <li> - <pre>-n <info:var>NAME</info:var></pre>, or - <pre>--client-name=<info:var>NAME</info:var></pre> 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. - </li> - <li> - <pre>-u <info:var>URI</info:var></pre>, or - <pre>--client-uri=<info:var>URI</info:var></pre>, sets an URI - for the identity provider to learn more about your app. - </li> - <li> - <pre>-p <info:var>PORT</info:var></pre>, or - <pre>--port=<info:var>PORT</info:var></pre>, change the port - number used by the server. By default, it is set to 8080. - </li> - <li> - <pre>-l <info:var>FILE.log</info:var></pre>, or - <pre>--log-file=<info:var>FILE.log</info:var></pre> let the - server dump all its output to <info:var>FILE.log</info:var>. - </li> - <li> - <pre>-e <info:var>FILE.err</info:var></pre>, or - <pre>--error-file=<info:var>FILE.err</info:var></pre> let the - server dump all its errors to <info:var>FILE.err</info:var>. - </li> - </ul> - <p> - The program is sensitive to the environment variable - <emph>LANG</emph>, 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. - </p> - - <h1 type="appendix">GNU Free Documentation License</h1> - <info:gfdl /> - - <h1 type="unnumbered">Index</h1> - <info:printindex /> - </body> -</html> - -<!-- Local Variables: --> -<!-- mode: nxml --> -<!-- End: --> |