summaryrefslogtreecommitdiff
path: root/doc/webid-oidc.texi
blob: 8d2d638e9363e5f9495df3741526d2171c665373 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
\input texinfo @c -*-texinfo-*-
@comment $Id@w{$}
@documentlanguage en
@comment %**start of header
@include version.texi
@settitle Webid-oidc manual
@syncodeindex pg cp
@syncodeindex fn cp
@syncodeindex vr cp
@syncodeindex tp cp
@comment %**end of header

@copying
This is the manual of webid-oidc (version @value{VERSION}, @value{UPDATED}), an implementation of the Solid authentication protocol for guile, client and server.

Copyright @copyright{} 2020, 2021 Vivien Kraus
@quotation
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
Texts. A copy of the license is included in the section entitled ``GNU
Free Documentation License''
@end quotation
@end copying

@dircategory Software libraries

@direntry
* webid-oidc: (webid-oidc)Decentralized Authentication on the Web.
@end direntry

@titlepage
@title Webid-oidc manual
@subtitle for version @value{VERSION}, @value{UPDATED}
@author Vivien Kraus (@email{vivien@@planete-kraus.eu})
@page
@vskip 0pt plus 1fill
@insertcopying
@end titlepage

@contents
@ifnottex
@node Top
@top Webid-oidc
@end ifnottex

@menu
* Decentralized Authentication on the Web::
* The Json Web Token::
* Caching on server side::
* Exceptional conditions::
* GNU Free Documentation License::
* Index::
@end menu

@node Decentralized Authentication on the Web
@chapter Decentralized Authentication on the Web

Authentication on the web is currently handled in the following way:
anyone can install a server that will authenticate users on the
web. The problem is interoperability. If a client (an application)
wants to authenticate a user, it has to be approved by the
authentication server. In other words, if @var{useful-program} wants
to authenticate @var{MegaCorp} users, then @var{useful-program} has to
register to @var{MegaCorp} first, and get approved. This goes against
the principle of permission-less innovation, which is at the heart of
the web.

In the decentralized authentication web, the best attempt so far is
that of ActivityPub. All servers are interoperable with respect to
authentication: if user A emits an activity, it is forwarded by A's
server to its recipients, and A's server is responsible for A's
identity.

The problem with that approach is that the data is tied to the
application. It is not possible to use another application to process
the data differently, or to use multiple data sources, in an
interoperable way (without the ActivityPub server knowing). This means
that on Activitypub, microblogging applications will not present
different activities correctly. This also means that it is difficult
to write a free replacement to a non-free application program, because
it would need to manage the data.

In the Solid ecosystem, there is a clear distinction between servers
and applications. An application is free to read data from all places
at the same time, using a permission-less authentication system. Since
the applications do not need to store data, the cost of having users
is neglectible, so users do not need prior approval before using them
(making captchas and the like a thing of the past). Servers do not
have a say in which applications the user uses.

The authentication used is a slight modification of the
well-established OpenID Connect. It is intended to work in a web
browser, but this package demonstrates that it also works without a
web browser.

@node 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
* Generic JWTs::
@end menu

@node Generic JWTs
@section Generic JWTs

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

@deffn function jws? @var{jwt}
Check that @var{jwt} is a decoded JWT signed with JWS.
@end deffn

@deffn function jws-alg @var{jwt}
Get the algorithm used to sign @var{jwt}.
@end deffn

@deffn function jws-decode @var{str} @var{lookup-keys}
Check and decode a JWT signed with JWS and encoded as @var{str}.

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

@deffn function jws-encode @var{jwt} @var{key}
Encode the JWT and sign it with @var{key}.
@end deffn

@node Caching on server side
@chapter Caching on server side

Both the identity provider and the resource server need to cache
things. The identity provider will cache application webids, and the
resource server will cache the identity provider keys, for instance.

The solution is to use a file-system cache. Every response (except
those that have a cache-control policy of no-store) are stored to a
sub-directory of @emph{XDG_CACHE_HOME}. Each store has a 5% chance of
triggering a cleanup of the cache. When a cleanup occurs, each cached
response has a 5% chance of being dropped, including responses that
are indicated as valid. This way, a malicious cache response that has
a maliciously long validity will not stay too long in the cache. A log
line will indicate which items are dropped.

The @emph{(webid-oidc cache)} module exports two functions to deal
with the cache.

@deffn function clean-cache @var{[#percents]} @var{[#dir]}
Drop @var{percents}% of the cache right now, in @var{dir} (defaults to
some place within @emph{XDG_CACHE_HOME}).
@end deffn

@deffn function with-cache @var{[#current-time]} @var{[#http-get]} @var{[#dir]}
Return a function acting as @emph{http-get} from @emph{(web client)}
(takes an URI as the first parameter, and an optional @var{#:headers}
set, and returns 2 values, the response and its body).

The cache will be read and written in @var{dir} (defaults to some
place within @emph{XDG_CACHE_HOME}), and the @var{current-time} number
of seconds, SRFI-19 time or date, or time-returning thunk will be used
to check for the validity of responses.

The back-end function, @var{http-get}, defaults to that of
@emph{(web client)}.
@end deffn

@node Exceptional conditions
@chapter Exceptional conditions

The library will raise an exception whenever something fishy
occurs. For instance, if a signature is invalid, or the expiration
date has passed. All exception types are defined in
@code{(webid-oidc errors)}.

@deffn function error->str @var{error} @var{[#depth]}
Return a string explaining the @var{error}. You can limit the
@var{depth} of the explanation as an integer.
@end deffn

@menu
* Invalid data format::
* Invalid JWT::
* Cannot fetch data on the web::
@end menu

@node Invalid data format
@section Invalid data format
There are a few JSON objects with required fields. This exceptions
usually occur as the cause of a higher-level exception.

@deftp {exception type} &not-base64 @var{value} @var{cause}
This exception is raised when the base64 decoding function
failed. @var{value} is the incorrect input, and @var{cause} is a
low-level error.
@end deftp

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


@deftp {exception type} &not-json @var{value} @var{cause}
Cannot decode @var{value} to a JSON object.
@end deftp

@deftp {exception type} &unsupported-crv @var{crv}
The identifier @var{crv} does not identify an elliptic curve.
@end deftp

@deftp {exception type} &not-a-jwk @var{value} @var{cause}
@var{value} does not identify a JWK.
@end deftp

@deftp {exception type} &not-a-public-jwk @var{value} @var{cause}
@var{value} does not identify a public JWK.
@end deftp

@deftp {exception type} &not-a-private-jwk @var{value} @var{cause}
@var{value} does not identify a private JWK.
@end deftp

@deftp {exception type} &not-a-jwks @var{value} @var{cause}
@var{value} does not identify a set of public keys.
@end deftp

@deftp {exception type} &unsupported-alg @var{value}
@var{value} does not identify a valid hash algorithm.
@end deftp

@deftp {exception type} &invalid-signature @var{key} @var{payload} @var{signature}
@var{key} has not signed @var{payload} with @var{signature}.
@end deftp

@deftp {exception type} &missing-alist-key @var{value} @var{key}
@var{value} isn’t an alist, or is missing a value with @var{key}.
@end deftp

@deftp {exception type} &not-a-jws-header @var{value} @var{cause}
@var{value} does not identify a decoded JWS header.
@end deftp

@deftp {exception type} &not-a-jws-payload @var{value} @var{cause}
@var{value} does not identify a decoded JWS payload.
@end deftp

@deftp {exception type} &not-a-jws @var{value} @var{cause}
@var{value} does not identify a decoded JWS.
@end deftp

@deftp {exception type} &not-in-3-parts @var{string} @var{separator}
@var{string} cannot be split into 3 parts with @var{separator}.
@end deftp

@deftp {exception type} &no-matching-key @var{candidates} @var{alg} @var{payload} @var{signature}
No key among @var{candidates} could verify @var{signature} signed with
@var{alg} for @var{payload}, because the signature mismatched for all
keys.
@end deftp

@deftp {exception type} &cannot-decode-jws @var{value} @var{cause}
The @var{value} string is not an encoding of a valid JWS.
@end deftp

@deftp {exception type} &cannot-encode-jws @var{jws} @var{key} @var{cause}
The @var{jws} cannot be signed.
@end deftp

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

@deftp {exception type} &request-failed-unexpectedly @var{response-code} @var{response-reason-phrase}
We expected the request to succeed, but the server sent a non-OK
@var{response-code}.
@end deftp

@deftp {exception type} &unexpected-header-value @var{header} @var{value}
We did not expect the server to respond with @var{header} set to
@var{value}.
@end deftp

@deftp {exception type} &unexpected-response @var{response} @var{cause}
The @var{response} (from @emph{(web response)}) is not appropriate.
@end deftp

@deftp {exception type} &not-an-oidc-configuration @var{value} @var{cause}
The @var{value} is not appropriate an OIDC configuration.
@end deftp

@node GNU Free Documentation License
@appendix GNU Free Documentation License

@include fdl.texi

@node Index
@unnumbered Index

@printindex cp

@bye