From e910b3ba2ded990a5193f7ea0cfad525332e4171 Mon Sep 17 00:00:00 2001 From: Vivien Kraus Date: Mon, 20 Sep 2021 11:25:29 +0200 Subject: JWS: use GOOPS --- doc/disfluid.texi | 965 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 707 insertions(+), 258 deletions(-) (limited to 'doc') diff --git a/doc/disfluid.texi b/doc/disfluid.texi index 2cfe60c..0047379 100644 --- a/doc/disfluid.texi +++ b/doc/disfluid.texi @@ -60,6 +60,7 @@ is tracked in the Guix channel * Decentralized Authentication on the Web:: * Invoking disfluid:: * Running disfluid with GNU Guix:: +* Managing keys:: * The Json Web Token:: * Caching on server side:: * Content negociation:: @@ -280,264 +281,8 @@ This record configures a server to serve public application pages. The configuration for the full server. @end deftp -@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:: -* Public-key cryptography:: -@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{#:webid} @var{#:iss} @var{#:sub} @var{#:aud} @var{#:validity} -Create an ID token that is valid for @var{#:validity} seconds, and -sign 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 the decoded access token, or raise -an exception. -@end deffn - -@deffn function issue-access-token @var{issuer-key} #:@var{webid} #:@var{iss} #:@var{client-id} #:@var{validity} [#:@var{[client-key} | #:@var{cnf/jkt}] -Create an access token for @var{#:validity} seconds, 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{method} @var{uri} @var{str} @var{cnf/check} @var{[#:access-token]} -Check and decode a DPoP proof encoded as @var{str}. - -In order to prevent replay attacks, each proof has a unique random -string that is remembered globally until its expiration date is -reached. - -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 - -The DPoP proof algorithm is sensitive to the current time, because the -proofs have a limited time validity. By default, the time is the -system time when the proof is decoded. - -@deffn parameter current-date -This parameter overrides the current time. - -It is a thunk returning a date, so you need to put two parenthesis to -get the time. However, you can set it to a date, a time, a number of -seconds, or a thunk returning any of these. - -@example - (use-module ((webid-oidc parameters) #:prefix p:)) - ;; This is the current date: - ((p:current-date)) - ;; You can override it with a thunk, or a fixed date: - (parameterize ((p:current-date 0)) - ;; Jan 1st 1970 - ((p:current-date))) -@end example -@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{htm} #:@var{htu} {[#:@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. - -The @samp{iat} field of the DPoP proof is read from the -@var{current-date} parameter. -@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 Public-key cryptography -@section Public-key cryptography +@node Managing keys +@chapter Managing keys Some functions require a key, or a key pair, to operate. The @emph{(webid-oidc jwk)} module provides you with everything required @@ -713,6 +458,710 @@ If the JWKS cannot be downloaded, or is incorrect, this exception is raised. @end deftp +@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. + +@menu +* Tokens:: +* Tokens issued by an OIDC provider:: +* Date verification for tokens:: +* Single-use tokens:: +* ID tokens:: +* Access tokens:: +* DPoP proofs:: +* Authorization codes:: +@end menu + +@node Tokens +@section Tokens + +The @emph{(webid-oidc jws)} implements some functionality for tokens. + +@deftp {Class} () @var{alg} +The base class for all tokens. It only knows the signature +@var{alg}orithm. You can construct one in different ways: +@itemize +@item +the @code{#:@var{alg}} construct keyword supports a string or a +keyword as a value, containing a valid JWA identifier, such as +@code{RS256}; +@item +the @code{#:@var{signing-key}} keyword defines the key that will serve +to sign the token. The signature algorithm is set to the default of +@var{signing-key}; +@item +the @code{#:@var{jwt-header}} and @code{#:@var{jwt-payload}} keywords +let you pass two alists, following the JSON representation from +srfi-180: objects are alists of @strong{symbols} to values, arrays are +vectors. +@end itemize +@end deftp + +@deftp {Exception type} &invalid-jws +This exception is raised when a JWT cannot be parsed or constructed as +a JWS. +@end deftp + +@deffn {function} make-invalid-jws +Construct an exception of type @code{&invalid-jws}. +@end deffn + +@deffn {function} invalid-jws? @var{exception} +Check whether @var{exception} was raised because of an invalid JWS. +@end deffn + +There are multiple things you can do with a token. + +@deffn {Generic} alg @var{token} +Return the signature algorithm used for @var{token}, as a symbol. +@end deffn + +@deffn {Generic} token->jwt @var{token} +Return two alists, following the JSON representation from srfi-180: +one for the header, and then one for the payload. +@end deffn + +@deffn {Generic} lookup-keys @var{token} @var{args} +Return the set of keys that could be used to sign @var{token}, as a +public key, a list of keys, or a JWKS. @var{args} is a list of keyword +arguments for specific implementations. +@end deffn + +@deffn {Generic} verify @var{token} @var{args} +Suppose that the @var{token} signature has been checked, perform some +additional verifications. This function should raise exceptions to +signal an invalid token. +@end deffn + +@deffn {function} decode @var{expected-token-class} @var{encoded} . @var{args} +Parse @var{encoded} as a token from the @var{expected-token-class}, +check its signature against the key obtained by @code{(lookup-keys +@var{token} @var{args})} where @var{token} is the parsed token, and +perform additional verifications with @code{(verify @var{token} +@var{args})}. +@end deffn + +@deffn {function} encode @var{token} @var{key} +Encode and sign @var{token} with @var{key}, returning a string. +@end deffn + +@deffn {function} issue @var{token-class} @var{issuer-key} . @var{args} +Construct a token of @var{token-class} and @var{args} and sign it with +@var{issuer-key}. Since we know the key to sign it, it is not +necessary to pass either @code{#:signing-key} nor @code{#:alg} to the +constructor. +@end deffn + +@node Tokens issued by an OIDC provider +@section Tokens issued by an OIDC provider +OIDC tokens are those signed by an OIDC identity provider. This kind +of token knows its issuer, and getting the keys to check the token +signature is done by OIDC discovery. + +@deftp {Class} () @var{iss} +The base class for tokens which are issued by an identity provider. It +knows the issuer (@var{iss}, an uri from @emph{(web uri)}), and can +query it to check the token signature. + +Similarly to the base token type, you can construct one by specifying +its arguments, or create one from a pair of alists. +@itemize +@item +@code{#:@var{alg}} or @code{#:@var{signing-key}} is required to +construct the base token; +@item +@code{#:@var{iss}} specifies the issuer. +@end itemize + +The main point of this class is to provide a method for the +@code{lookup-keys} generic. This method accepts one keyword argument, +@code{#:@var{http-request}}, a function that behaves like the web +client in @emph{(web client)}. You can set this value as a keyword +argument in the @code{decode} function. +@end deftp + +@deffn {Generic} iss @var{token} +Return the issuer of @var{token}, as an URI. +@end deffn + +@deftp {Exception type} &cannot-query-identity-provider @var{identity-provider} +This exception is raised when the OIDC discovery +fails. @var{identity-provider} is an URI. +@end deftp + +@deffn {function} make-cannot-query-identity-provider @var{identity-provider} +Construct an exception of type @code{&cannot-query-identity-provider}. +@end deffn + +@deffn {function} cannot-query-identity-provider? @var{exception} +Check whether @var{exception} was raised because an identity provider +could not be queried. +@end deffn + +@deffn {function} cannot-query-identity-provider-value @var{exception} +Return the faulty identity provider for @var{exception}. +@end deffn + +@node Date verification for tokens +@section Date verification for tokens +Different kinds of tokens have a requirement for a limited time window +for which the signature should be valid. + +@deftp {Class} () @var{iat} @var{exp} +The base class for tokens which are issued for a limited time +window. It knows the issuance date (@var{iat}, a date from +@emph{(srfi srfi-19)}), and the expiration date (@var{iat}, a date +from @emph{(srfi srfi-19)}). + +Similarly to the base token type, you can construct one by specifying +its arguments, or create one from a pair of alists. +@itemize +@item +@code{#:@var{alg}} or @code{#:@var{signing-key}} is required to +construct the base token; +@item +@code{#:@var{iat}} specifies the issuance date. It defaults to the +current date; +@item +@code{#:@var{exp}} specifies the expiration date. If it is not set, +the value will be computed from @var{iat} and @var{validity}; +@item +@code{#:@var{validity}} is used when the expiration date is not known +in advance. It is a number of seconds. For a DPoP proof, the value +should be around 30 seconds. For an access token, a good value is in +the ballpark of 3600 seconds (an hour). Defaults to 3600 seconds, but +be aware that for single-use tokens, this value will be ignored and +replaced with a much shorter time. +@end itemize + +The main point of this class is to provide a stricter token validation +function. You can customize the current date by passing +@code{#:@var{current-date} ...} as keyword arguments to +@code{decode}. @code{...} would be replaced with a time or date. +@end deftp + +@deffn {Generic} default-validity @var{token} +Return the default validity as a number of seconds to construct +@var{token}, or @code{#f} if an explicit @code{#:validity} is +required. +@end deffn + +@deffn {Generic} has-explicit-exp? @var{token} +Check whether we should trust the JWT exp field when constructing +@var{token}. DPoP proofs should not be able to fill our cache with +infinitely-valid proofs, so it is disabled for DPoP proofs. +@end deffn + +@deffn {Generic} iat @var{token} +Return the signature date of @var{token}, as a srfi-19 date. +@end deffn + +@deffn {Generic} exp @var{token} +Return the expiration date of @var{token}, as a srfi-19 date. +@end deffn + +@deftp {Exception type} &signed-in-future @var{signature-date} @var{current-date} +@deftpx {Exception type} &expired @var{expiration-date} @var{current-date} +An exception of type @code{&signed-in-future} is raised when the +current date is before the alleged signature date. Since the signing +entity and the verifier entity may not be on the same system, the +clocks may be slightly out of synchronization, so a margin of 5 +seconds is usually accepted. + +An exception of type @code{&expired} indicates that the signature is +no longer valid. +@end deftp + +@deffn {function} make-signed-in-future @var{signature-date} @var{current-date} +@deffnx {function} make-expired @var{expiration-date} @var{current-date} +Constructors for the @code{&signed-in-future} and @code{&expired} +exception types. +@end deffn + +@deffn {function} signed-in-future? @var{exception} +@deffnx {function} expired? @var{exception} +Check whether @var{exception} was raised because of a date mismatch. +@end deffn + +@deffn {function} error-signature-date @var{exception} +@deffnx {function} error-expiration-date @var{exception} +@deffnx {function} error-current-date @var{exception} +If @var{exception} was raised because of a date mismatch, return the +signature, expiration or current date. +@end deffn + +@node Single-use tokens +@section Single-use tokens +To prevent replay attacks, you might want to assign an unique +identifier to each token of some kind. If you have an expiration date, +you could remember that this identifier has been seen, and forget +about it as soon as the token expires. For this to work, you would +need an expiration date for your single-use token: this is why we only +support it for time-bound tokens, and the validity is reduced down to +2 minutes. + +@deftp {Class} () @var{nonce} +The base class for tokens which are intended to be decoded only +once. The unique identifier string @var{nonce} will be remembered as +long as the program is running and the token is not expired. + +Similarly to the base token type, you can construct one by specifying +its arguments, or create one from a pair of alists. +@itemize +@item +@code{#:@var{alg}} or @code{#:@var{signing-key}} is required to +construct the base token; +@item +@code{#:@var{iat}} and @code{#:@var{exp}} or @code{#:@var{validity}} +is required to construct the time-bound token; +@item +@code{#:@var{nonce}} specifies the unique identifier. It defaults to a +random string of base64 data encoding 96 bits of entropy. +@item +@end itemize + +The main point of this class is to provide an even stricter token +validation function, that can only be run once for a given token (with +reasonable limits: if the program is killed, it won’t remember the +tokens from before). You can customize the current date by passing +@code{#:@var{current-date} ...} as keyword arguments to @code{decode}, +just as you do for regular time-bound tokens. @code{...} would be +replaced with a time or date. +@end deftp + +@deffn {Generic} nonce-field-name @var{token} +When constructing @var{token} from an existing JWT, this method gives +the field name in the JWT payload that represents the nonce. DPoP +proofs use @code{'jti}, so they override this value. +@end deffn + +@deffn {Generic} nonce @var{token} +Return the unique identifier of @var{token}, as a string. +@end deffn + +@deftp {Exception type} &nonce-found @var{nonce} +If a token with the same nonce has already been decoded during its +life time, this exception is raised with the duplicated @var{nonce}. +@end deftp + +@deffn {function} make-nonce-found @var{nonce} +Construct an exception of type @code{&nonce-found}. +@end deffn + +@deffn {function} nonce-found? @var{exception} +Check whether @var{exception} was raised because a single-use token +was already parsed. +@end deffn + +@deffn {function} nonce-found-nonce @var{exception} +Return the faulty nonce in @var{exception}. +@end deffn + +@node ID tokens +@section ID tokens + +The @emph{(webid-oidc oidc-id-token)} module contains a definition for +the OIDC ID token. + +@deftp {Class} ( ) @var{webid} @var{sub} @var{aud} +The ID token is issued by an identity provider, and is intended to be +used by the client only. It gives information about the user +identified by a @var{webid}, an URI from @emph{(web uri)}, and the +client ID, @var{aud}, an URI too. Since the client should not +communicate this token, it is reasonable to think that the client will +deccode the token as soon as it gets it, and then forget the now +useless signature. This is why this token is considered +single-use. The @var{sub} field should store a username as a string, +but if it is missing, the webid (as a string) will be used. + +To construct an ID token, you would either need +@code{#:@var{jwt-header}} and @code{#:@var{jwt-payload}}, as for any +token, or a combination of parameters: + +@itemize +@item +@code{#:@var{alg}} or @code{#:@var{signing-key}}, to initialize a JWT; +@item +@code{#:@var{iat}} and @code{#:@var{exp}} or @code{#:@var{validity}}, +because it is issued for a limited time window (around an hour); +@item +@code{#:@var{nonce}} to define its identifier (defaults to a random +one); +@item +@code{#:@var{iss}}, the issuer URI, because it is an OIDC token; +@item +@code{#:@var{webid}}, an URI identifying the user; +@item +@code{#:@var{sub}}, a string that defaults to the webid; +@item +@code{#:@var{aud}}, an URI identifying the application. +@end itemize +@end deftp + +@deffn {Generic} webid @var{token} +Return the user identifier in @var{token}, as an URI. +@end deffn + +@deffn {Generic} sub @var{token} +Return the username in @var{token}, as a string. +@end deffn + +@deffn {Generic} aud @var{token} +Return the client identifier in @var{token}, as an URI. +@end deffn + +@deftp {Exception type} &invalid-id-token +This exception is raised when the ID token is invalid. +@end deftp + +@deffn {function} make-invalid-id-token +Construct an exception of type @code{&invalid-id-token}. +@end deffn + +@deffn {function} invalid-id-token? @var{exception} +Check whether @var{exception} was raised because of an invalid ID +token. +@end deffn + +@node Access tokens +@section Access tokens + +The @emph{(webid-oidc access-token)} module contains a definition for +the OIDC access token. + +@deftp {Class} ( ) @var{webid} @var{aud} @var{client-id} @var{cnf/jkt} +The access token is issued by an identity provider for a client, and +is intended to be used by the resource servers. It indicates that the +agent possessing a key hashed to @var{cnf/jkt} (a string) is +identified by @var{client-id} (an URI) and is authorized to act on +behalf of the user identified by @var{webid} (an URI). For +compatibility, @var{aud} should be set to the literal string +@code{"solid"}. The agent demonstrates that it owns this key by +issuing a DPoP proof alongside the access token. + +To construct an access token, you would either need +@code{#:@var{jwt-header}} and @code{#:@var{jwt-payload}}, as for any +token, or a combination of parameters: + +@itemize +@item +@code{#:@var{alg}} or @code{#:@var{signing-key}}, to initialize a JWT; +@item +@code{#:@var{iat}} and @code{#:@var{exp}} or @code{#:@var{validity}}, +because it is issued for a limited time window (around an hour); +@item +@code{#:@var{iss}}, the issuer URI, because it is an OIDC token; +@item +@code{#:@var{webid}}, an URI identifying the user; +@item +@code{#:@var{client-id}}, an URI identifying the client; +@item +@code{#:@var{cnf/jkt}}, the hash of a public key whose private key is +owned by the client, or @code{#:@var{client-key}}, the client key +itself; +@item +@code{#:@var{aud}}, literally @code{"solid"}, +optional, defaults to the correct value. +@end itemize + +Since the same access token is presented on each request, it is not +single-use. +@end deftp + +@deffn {Generic} webid @var{token} +Return the user identifier in @var{token}, as an URI. +@end deffn + +@deffn {Generic} client-id @var{token} +Return the client identifier in @var{token}, as an URI. +@end deffn + +@deffn {Generic} cnf/jkt @var{token} +Return the hash of the client key, as a string. +@end deffn + +@deffn {Generic} aud @var{token} +Return @code{"solid"}. +@end deffn + +@deftp {Exception type} &invalid-access-token +This exception is raised when the access token is invalid. +@end deftp + +@deffn {function} make-invalid-access-token +Construct an exception of type @code{&invalid-access-token}. +@end deffn + +@deffn {function} invalid-access-token? @var{exception} +Check whether @var{exception} was raised because of an invalid access +token. +@end deffn + +@node DPoP proofs +@section DPoP proofs + +The @emph{(webid-oidc dpop-proof)} module contains a definition for +the DPoP proof token. + +@deftp {Class} () @var{typ} @var{jwk} @var{htm} @var{htu} @var{ath} +The DPoP proof is a token that is issued by the client, and presented +to the resource server along with an access token. It is only valid +for one request, and for one use. So, it should have a very short +validity frame, for instance 30 seconds, and should only be valid for +a specific request method @var{htm} and a specific request URI +@var{htu}, down to the path, but ignoring the query and fragment. + +The DPoP proof is the proof of possession of @var{jwk}, a public +key. It should always have a @var{typ} field set to @code{"dpop+jwt"}. + +To construct a DPoP proof, you would either need +@code{#:@var{jwt-header}} and @code{#:@var{jwt-payload}}, as for any +token, or a combination of parameters: + +@itemize +@item +@code{#:@var{alg}} or @code{#:@var{signing-key}}, to initialize a JWT; +@item +@code{#:@var{iat}} and @code{#:@var{exp}} or @code{#:@var{validity}}, +because it is issued for a limited time window (around 30 seconds); +@item +@code{#:@var{nonce}}, because it is single-use; +@item +@code{#:@var{jwk}}, the public key whose possession we demonstrate by +signing the proof; +@item +@code{#:@var{htm}}, the HTTP method used (as a symbol); +@item +@code{#:@var{htu}}, the HTTP URI used (as an URI); +@item +@code{#:@var{ath}}, the hash of the access token that goes with this +proof, or @code{#:@var{access-token}}, the encoded access token +itself, if the proof goes with an access token. Otherwise, pass +@code{#f}. Defaults to @code{#f}; +@item +@code{#:@var{typ}}, literally @code{"dpop+jwt"}, +optional, defaults to the correct value. +@end itemize + +This token class makes a stricter verification function. It requires +you to set as a keyword argument in @code{decode} the following +parameters: + +@table @code +@item #:@var{access-token} +set the access token that should go with the proof, defaults to +@code{#f} (no access token); +@item #:@var{method} +set the method used for the proof; +@item #:@var{uri} +set the URI used for the proof; +@item #:@var{cnf/check} +set the expected hash of the key used by the DPoP proof, or a function +taking a public key hash. If this is a function, it should raise an +exception if the hash is invalid, because its return value is ignored. +@end table +@end deftp + +@deffn {Generic} jwk @var{proof} +Return the public key whose possession @var{proof} demonstrates. +@end deffn + +@deffn {Generic} htm @var{proof} +Return the HTTP method in @var{proof}, as a symbol. +@end deffn + +@deffn {Generic} htu @var{proof} +Return the HTTP URI in @var{proof}, as an URI. +@end deffn + +@deffn {Generic} ath @var{proof} +Return the hash of the access token that should go with @var{proof}, +or @code{#f} if @var{proof} is not used with an access token. +@end deffn + +@deffn {Generic} typ @var{proof} +Return @code{"dpop+jwt"}. +@end deffn + +@deftp {Exception type} &invalid-dpop-proof +This exception is raised when the DPoP proof is invalid. +@end deftp + +@deffn {function} make-invalid-dpop-proof +Construct an exception of type @code{&invalid-dpop-proof}. +@end deffn + +@deffn {function} invalid-dpop-proof? @var{exception} +Check whether @var{exception} was raised because of an invalid DPoP +proof. +@end deffn + +@deftp {Exception type} &dpop-method-mismatch @var{advertised} @var{actual} +This exception is raised when the @var{advertised} method is not what +is @var{actual}ly used in the request (both symbols). +@end deftp + +@deffn {function} make-dpop-method-mismatch @var{advertised} @var{actual} +Construct an exception of type @code{&dpop-method-mismatch}. +@end deffn + +@deffn {function} dpop-method-mismatch? @var{exception} +Check whether @var{exception} was raised because of a difference +between the advertised and actual HTTP methods used. +@end deffn + +@deffn {function} dpop-method-mismatch-advertised @var{exception} +In case of a DPoP method mismatch causing @var{exception}, return the +method used in the proof signature. +@end deffn + +@deffn {function} dpop-method-mismatch-actual @var{exception} +In case of a DPoP method mismatch causing @var{exception}, return the +method that the server received. +@end deffn + +@deftp {Exception type} &dpop-uri-mismatch @var{advertised} @var{actual} +This exception is raised when the @var{advertised} URI is not what is +@var{actual}ly used in the request (both URIs). +@end deftp + +@deffn {function} make-dpop-uri-mismatch @var{advertised} @var{actual} +Construct an exception of type @code{&dpop-uri-mismatch}. +@end deffn + +@deffn {function} dpop-uri-mismatch? @var{exception} +Check whether @var{exception} was raised because of a difference +between the advertised and actual HTTP URIs used. +@end deffn + +@deffn {function} dpop-uri-mismatch-advertised @var{exception} +In case of a DPoP URI mismatch causing @var{exception}, return the +URI used in the proof signature. +@end deffn + +@deffn {function} dpop-uri-mismatch-actual @var{exception} +In case of a DPoP URI mismatch causing @var{exception}, return the URI +that the server received. +@end deffn + +@deftp {Exception type} &dpop-invalid-ath @var{hash} @var{access-token} +This exception is raised when the DPoP proof is intended for use along +with an access token identified by @var{hash}, but is actually used +along with @var{access-token}. +@end deftp + +@deffn {function} make-dpop-invalid-ath @var{hash} @var{access-token} +Construct an exception of type @code{&dpop-invalid-ath}. +@end deffn + +@deffn {function} dpop-invalid-ath? @var{exception} +Check whether @var{exception} was raised because the DPoP proof was +not used with the correct access token. +@end deffn + +@deffn {function} dpop-invalid-ath-hash @var{exception} +In case of a DPoP presented with the wrong access token, causing +@var{exception}, return the hash of the intended access token. +@end deffn + +@deffn {function} dpop-invalid-ath-access-token @var{exception} +In case of a DPoP presented with the wrong access token, causing +@var{exception}, return the actual access token. +@end deffn + +@deftp {Exception type} &dpop-unconfirmed-key +This exception is raised when the DPoP proof does not demonstrate the +possession of the correct key. +@end deftp + +@deffn {function} make-dpop-unconfirmed-key +Construct an exception of type @code{&dpop-unconfirmed-key}. +@end deffn + +@deffn {function} dpop-unconfirmed-key? @var{exception} +Check whether @var{exception} was raised because the DPoP proof +demonstrated the possession of an incorrect key. +@end deffn + +@node Authorization codes +@section Authorization codes +@emph{(webid-oidc authorization-code)} defines an authorization code +type. + +@deftp {Class} () @var{webid} @var{client-id} +While it is not necessary that an authorization code is a JWT, it is +easier to implement that way. It is an authorization for +@var{client-id}, an URI identifying a client, to access the data of +the user identified by @var{webid}, an URI too. It should only be +valid for a limited amount of time, and used once only. + + +The DPoP proof is a token that is issued by the client, and presented +to the resource server along with an access token. It is only valid +for one request, and for one use. So, it should have a very short +validity frame, for instance 30 seconds, and should only be valid for +a specific request method @var{htm} and a specific request URI +@var{htu}, down to the path, but ignoring the query and fragment. + +The DPoP proof is the proof of possession of @var{jwk}, a public +key. It should always have a @var{typ} field set to @code{"dpop+jwt"}. + +To construct an authorization code, you would either need +@code{#:@var{jwt-header}} and @code{#:@var{jwt-payload}}, as for any +token, or a combination of parameters: + +@itemize +@item +@code{#:@var{alg}} or @code{#:@var{signing-key}}, to initialize a JWT; +@item +@code{#:@var{iat}} and @code{#:@var{exp}} or @code{#:@var{validity}}, +because it is issued for a limited time window (around 30 seconds); +@item +@code{#:@var{nonce}}, because it is single-use; +@item +@code{#:@var{webid}}, the user identifier; +@item +@code{#:@var{client-id}}, the client identifier. +@end itemize + +The authorization code is signed and verified by the same entity. So, +the key lookup function is tuned to always return the issuer key. You +need to set it as the @code{#:@var{issuer-key}} keyword argument of +the @code{decode} function. +@end deftp + +@deffn {Generic} webid @var{token} +Return the user identifier in @var{token}, as an URI. +@end deffn + +@deffn {Generic} client-id @var{token} +Return the client identifier in @var{token}, as an URI. +@end deffn + +@deftp {Exception type} &invalid-authorization-code +This exception is raised when the authorization ccode is invalid. +@end deftp + +@deffn {function} make-invalid-authorization-code +Construct an exception of type @code{&invalid-authorization-code}. +@end deffn + +@deffn {function} invalid-authorization-code? @var{exception} +Check whether @var{exception} was raised because of an invalid +authorization code. +@end deffn + @node Caching on server side @chapter Caching on server side -- cgit v1.2.3