summaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
authorVivien Kraus <vivien@planete-kraus.eu>2021-08-07 22:45:06 +0200
committerVivien Kraus <vivien@planete-kraus.eu>2021-08-13 01:06:38 +0200
commitdb55d55e5c36c940986f437d26da1ff3c601c3b4 (patch)
tree0ecec5b2bd0b0bc6a02981a7c3b9ccafbb891c3b /doc
parent0b5d0622e11c1f919ce660893067d3121e2583a0 (diff)
Make a better client API
Diffstat (limited to 'doc')
-rw-r--r--doc/disfluid.texi212
1 files changed, 137 insertions, 75 deletions
diff --git a/doc/disfluid.texi b/doc/disfluid.texi
index 93128c1..2841052 100644
--- a/doc/disfluid.texi
+++ b/doc/disfluid.texi
@@ -443,8 +443,8 @@ Get the corresponding field of the proof.
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 in @var{jti-list} until its expiration date
-is reached. See the @code{make-jti-list} function.
+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).
@@ -873,103 +873,165 @@ 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.
+The list of accounts is stored on the file system. You can manipulate
+the accounts with the @emph{(webid-oidc client accounts)} 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.
+@deftp {Record type} <account> @var{subject} @var{issuer} @var{id-token} @var{access-token} @var{refresh-token} @var{keypair}
+Store information about an account. @var{subject} and @var{issuer} are
+required, they must bue URIs. If the access token was not invalidated,
+then @var{id-token} contains a (decrypted) identity token, and
+@var{access-token} an encrypted access token. If you got a
+@var{refresh-token} for this account, it is also stored, along with
+the @var{keypair} that is server-side bound to it.
-Each application should have its own webid, or in that case
-@var{client-id}, that can be dereferenced by the identity provider.
+The optional parameters are @code{#f} when we don’t have them.
+@end deftp
-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.
+@deffn function make-account @var{subject} @var{issuer} @var{id-token} @var{access-token} @var{refresh-token} @var{keypair}
+Create an account.
@end deffn
-Once the client gets the authorization code, it is necessary to create
-an access token and ID token.
+@deffn function account? @var{object}
+Check whether @var{object} is an account.
+@end deffn
-@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.
+@deffn function account-subject @var{account}
+@deffnx function account-issuer @var{account}
+@deffnx function account-id-token @var{account}
+@deffnx function account-access-token @var{account}
+@deffnx function account-refresh-token @var{account}
+@deffnx function account-keypair @var{account}
+Access the account.
+@end deffn
-You can override the HTTP client used (@var{http-get} and
-@var{http-post}), and how to compute the time (@var{current-time}).
+You should always manage the accounts with the users database.
+
+@deffn function read-accounts
+Read the list of all accounts. This function is safe to call at any
+time during concurrent updates of the database. If the update was
+finished, the new list is returned, otherwise the old list is returned
+without blocking.
@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 save-account @var{account}
+Find an account in the database with the same subject and issuer, and
+replace its contents with @var{account}. Return @var{account}.
+@end deffn
-@deffn function list-profiles @var{[#dir]}
-Read the list of available profiles. Returns a list of triples, webid,
-issuer, reresh token.
+@deffn function delete-account @var{account}
+Remove all accounts from the database that have the same subject and
+issuer as @var{account}.
+@end deffn
+
+To log a user in, you must know at least per issuer. More precisely,
+if the user is already known (because, for instance, the user presents
+a cookie for your web application), then you should know the user’s
+webid and the webid issuer. If you don’t know the user, and the user
+is eligible to your service, then you will only know the identity
+provider (issuer), because that’s what the user typed in.
-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.
+@deffn function login @var{subject} @var{issuer} [#:@var{http-get}=@code{http-get}] [#:@var{http-post}=@code{http-post}] [#:@var{state}=@code{#f}] #:@var{client-id} #:@var{client-key} #:@var{redirect-uri}
+Return a new account with an ID token and an access
+token. @var{subject} is optional.
+
+When you receive an account record from this function, make sure to
+save it to the accounts database with @code{save-account}.
@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.
+@deftp {Exception type} &authorization-code-required @var{uri}
+If the login process requires the user to send an authorization code,
+an exception of this type will be raised, with an implicit invitation
+for the user to browse @var{uri} and follow the instructions.
+
+The instructions will be handled by the @var{redirect-uri} in the
+@code{login} function. If your client is a traditional web
+application, the user will be redirected to this URI with an
+authorization code. If your client is a native application, then maybe
+that redirection URI should display the authorization code and invite
+the user to paste it in the appropriate place in the application.
+
+When an exception of this type is raised during the @code{login}
+function, it is continuable, meaning that the login function will
+resume. You need to create an exception handler for an exception of
+this type, look up the @var{uri}, direct the user to browse it, get
+the authorization code back, and @emph{return} the authorization code
+@emph{from the exception handler}.
+@end deftp
+
+@deffn function make-authorization-code-required @var{uri}
+@deffnx function authorization-code-required? @var{error}
+@deffnx function authorization-code-required-uri @var{error}
+Constructor, predicate, and accessor for the
+@code{&authorization-code-required} exception type.
@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}.
+@deftp {Exception type} &refresh-token-expired
+The refresh token can be used to still perform requests on behalf of
+the user when perse is offline. However, if the refresh token expires
+while the user is offline, it is not possible to log in again, because
+it requires a new authorization code. So, it is not possible to
+recover from this error, and the refresh token is immediately
+discarded.
+@end deftp
+
+@deffn function make-refresh-token-expired
+@deffnx function refresh-token-expired? @var{error}
+Constructor and predicate for the @code{&refresh-token-expired}
+exception type.
@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.
+@deffn function invalidate-access-token @var{account}
+Discard the access token for @var{account}. It is not saved in the
+user database yet. This is roughly equivalent to log out.
@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.
+@deffn function invalidate-refresh-token @var{account}
+Discard the refresh token for @var{account}. You still need to save
+the @var{account}.
+@end deffn
-@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.
+@deftp {Exception type} &token-request-failed @var{response} @var{response-body}
+If the token endpoint is unable to deliver an identity token and an
+access token, this exception is raised with the identity provider
+@var{response} and @var{response body}. This exception cannot be
+continued.
+@end deftp
-@var{#current-time} is set to a thunk that returns the time. It is
-used to issue DPoP proofs.
+@deffn function make-token-request-failed @var{response response-body}
+@deffnx function token-request-failed? @var{error}
+@deffnx function token-request-response @var{error}
+@deffnx function token-request-response-body @var{error}
+Constructor, predicate, and accessors for the
+@code{&token-request-failed} exception type.
@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 @emph{(webid-oidc client)} module provides support for complete
+clients.
-The identity provider needs to call the application on the web. So,
-your client should have a public endpoint on the web.
+@deftp {Record type} <client> @var{id} @var{key} @var{redirect-uri}
+The @var{id} of a client is an URI without fragment that can be
+dereferenced in the world-wide web to metadata about the client. It
+should allow @var{redirect-uri} to access the authorization code.
+
+It is useful if an application rotates its @var{key}. So, while a key
+will still be used as long as the associated refresh token for a given
+user is valid, you can equip new users with a new key pair.
+@end deftp
+
+@deffn function make-client @var{id} @var{key} @var{redirect-uri}
+@deffnx function client? @var{object}
+@deffnx client-id @var{object}
+@deffnx client-key @var{object}
+@deffnx client-redirect-uri @var{object}
+Constructor, predicate and accessors for the @code{<client>} record
+type.
+@end deffn
+
+@deffn function request @var{client} @var{subject} @var{issuer} [#:@var{http-request}=@code{http-request}]
+Log in with @var{subject} (optional, may be @code{#f}) and
+@var{issuer}, and return a function that takes a request and request
+body and transfers it, signed, to the @var{http-request} back-end.
+@end deffn
@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