summaryrefslogtreecommitdiff
path: root/doc/disfluid.texi
blob: d18c8e906080e0e81506053d5822d6834ca4688b (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
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
\input texinfo @c -*-texinfo-*-
@comment $Id@w{$}
@documentlanguage en
@comment %**start of header
@include version.texi
@settitle Demanding Interoperability to Strengthen the Free (Libre) Web: Introducing Disfluid
@syncodeindex pg cp
@syncodeindex fn cp
@syncodeindex vr cp
@syncodeindex tp cp
@comment %**end of header

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

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

@dircategory Software libraries

@direntry
* disfluid: (disfluid)Interoperability on the web
@end direntry

@titlepage
@title Demanding Interoperability to Strengthen the Free (Libre) Web: Introducing Disfluid
@subtitle for version @value{VERSION}, @value{UPDATED}
@author Vivien Kraus (@email{vivien@@planete-kraus.eu})
@page
@vskip 0pt plus 1fill
@insertcopying
@end titlepage

@contents
@ifnottex
@node Top
@top Disfluid
@end ifnottex

@menu
* Decentralized Authentication on the Web::
* Invoking disfluid::
* The Json Web Token::
* Caching on server side::
* Content negociation::
* Running an Identity Provider::
* Running a Resource Server::
* Running a client::
* Exceptional conditions::
* GNU Free Documentation License::
* Index::
@end menu

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

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

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

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

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

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

@node Invoking disfluid
@chapter Invoking disfluid

The @samp{disfluid} program provides different modes of operations:

@table @samp
@item reverse-proxy
Run an authenticating reverse proxy. With this command, you specify a
backend server. When an authenticated user makes a request, you
receive an additional header containing the user’s identity.
@item identity-provider
Run the identity provider only.
@item client-service
The client applications must serve some resources: namely, the client
manifest and the redirect URI.
@item server
Run both an identity provider and a resource server.
@end table

The server is configured with command-line arguments, and environment
variables.

@menu
* General options::
* General server configuration::
* Configuration for the resource server::
* Configuration for the identity provider::
* Configuration for the client service::
@end menu

@node General options
@section General options
The server will respond to @samp{-h} and @samp{-v} commands, to get
the help output and the version information.

The server output (command-line, logs) are localized for the system
administrator. You can control it with the @samp{LANG} environment
variable. So if your locale is not English, you can have the same
commands as in this manual by running with @code{LANG=C}.

The programs respect the @samp{XDG_DATA_HOME} and
@samp{XDG_CACHE_HOME} to store persistent data and disposable
data. The cache directory can be deleted at any time. If one of these
variables is not set, its value is computed from the @samp{HOME}
environment variable.

@node General server configuration
@section General server configuration
All servers are published under the Affero GPL, which means that the
service provider needs to publish all changes made to the program to
users over the network. The @samp{disfluid} command provides a
@samp{--complete-corresponding-source} option so that the system
administrator can specify a means to download the source.

The servers will add a @samp{Source:} header in each response,
containing the value of this configuration option.

The servers can be configured to redirect output and errors to a log
file and an error file, with the @samp{--log-file} and
@samp{--error-file} options.

The server will listen to port 8080 by default, but this may be
configured with @samp{--port}. Since the servers do not support TLS,
and they only support HTTP/1.1, they are intended to run behind a
reverse proxy (even for the authenticating reverse proxy).

Finally, the servers are required to know their public name. This is
configured with the @samp{--server-name} option.

The server will make requests on the world-wide web, for instance to
download client manifests. The requests can be redirected with XML
Catalog, by setting the @samp{XML_CATALOG_FILES} to a space-separated
list of URIs (can be @code{file:} URIs). The requests cannot be
directed to the file system.

@node Configuration for the resource server
@section Configuration for the resource server
The reverse proxy sets an identity header to authenticated
requests. By default, it is @samp{XXX-Agent}, but it can be configured
with @samp{--header}.

The reverse proxy is configured to contact a backend URI with
@samp{--backend-uri}. This backend URI should not be directly exposed,
because a malicious user could set the identity header.

@node Configuration for the identity provider
@section Configuration for the identity provider
The identity provider can only handle one user. If you want to handle
multiple users, it is highly advised to use a different host name for
each user, in case the server is accessed from a web browser. You can
set the identity of the user with @samp{--subject}, and write the
user’s password in a file. Pass the file name with
@samp{--encrypted-password-file}. You can pass the encrypted password
directly with @samp{--encrypted-password}, but the encrypted password
will be public.

The encrypted password format is defined by the crypt function in the
C library. For glibc, it looks like this:
@code{$@var{N}$@var{salt}$@var{hash}}, where @var{N} is the algorithm
identifier, @var{salt} is the password salt annd @var{hash} is its
hash.

The server uses a key, which is not the same thing as the TLS
certificate of the server (remember, the servers don’t support
TLS). It is in the JWK format. You set its file name with
@samp{--key-file}. If the key file does not exist, it will be
generated.

Finally, the public openid configuration requires you to set the JWKS
URI (@samp{--jwks-uri}), authorization endpoint URI
(@samp{--authorization-endpoint-uri}) and token endpoint URI
(@samp{--token-endpoint-uri}). The identity provider will publish the
full URIs, but will respond to their path, regardless of the host.

@node Configuration for the client service
@section Configuration for the client service
The client will serve a stupid page for the redirect URI that will
only display the authorization code. The redirect URI is set with
@samp{--redirect-uri}.

The client ID is set with @samp{--client-id}. This is the URI under
which the client registrationn is served.

Finally, you can set some cosmetic options, but since it can confuse
the user, they are hidden by default by the identity provider.

@table @samp
@item --client-name
set the name of the application.
@item --client-uri
set an URI where to find more information about the client.
@end table

@node The Json Web Token
@chapter The Json Web Token

The Json Web Token, or @dfn{JWT}, is a terse representation of a pair
of JSON objects: the @dfn{header}, and the @dfn{payload}. The JWT can
be @dfn{encoded} as a Json Web Signature (@dfn{JWS}), in which case
the header is encoded to base64 with the URL alphabet, and without
padding characters, the payload is also encoded to base64, and the
concatenation of the encoding of the header, a dot, and the encoding
of the payload is signed with some cryptography algorithm. In the
following, we will only be interested by public-key cryptography. The
concatenation of header, dot, payload, dot and signature in base64 is
the encoding of the JWT.

Decoded JWT are represented as a pair. The car of the pair is the
header, and the cdr is the payload. Both the header and the payload
use the JSON representation from srfi-180: objects are alists of
@strong{symbols} to values, arrays are vectors. It is unfortunate that
guile-json has a slightly different representation, where alist keys
are @emph{strings}, but we hope that in the future SRFI-180 will be
more closely respected.

@menu
* The ID token::
* The access token::
* The DPoP proof::
* Generic JWTs::
@end menu

@node The ID token
@section The ID token

The ID token is a special JWT that the application keeps for
itself. It is signed by the identity provider, and contains the
following claims:

@table @emph
@item webid
the URI of the user’s webid;
@item iss
the URI of the identity provider (issuer);
@item sub
the username (the webid-oidc issuer puts the webid again here, but it
could be any string);
@item aud
the ID of the client application that is intended to receive the ID
token;
@item nonce
some random data to change the signature;
@item exp
an UTC time (in seconds) for when the token expires;
@item iat
the time when it was issued.
@end table

There are functions to work with ID tokens in
@emph{(webid-oidc oidc-id-token)}.

@deffn function id-token? @var{object}
Check that @var{object} is a decoded ID token.
@end deffn

The following helper functions convert URIs to the URIs from
@emph{(web uri)} and times to @emph{(srfi srfi-19)} dates.

@deffn function id-token-webid @var{token}
@deffnx function id-token-iss @var{token}
@deffnx function id-token-sub @var{token}
@deffnx function id-token-aud @var{token}
@deffnx function id-token-nonce @var{token}
@deffnx function id-token-exp @var{token}
@deffnx function id-token-iat @var{token}
Get the suitable field from the payload of @var{token}.
@end deffn

ID tokens can be signed and encoded as a string, or decoded.

@deffn function id-token-decode @var{token} @var{[#http-get]}
Decode @var{token}, as a string, into a decoded token. The signature
verification will need to fetch the oidc configuration of the claimed
issuer, and check the signature against the published keys. The
@code{http-get} optional keyword argument can set a different
implementation of @code{http-get} from @emph{(web client)}. Return
@code{#f} if it failed, or the decoded token otherwise.
@end deffn

@deffn function id-token-encode @var{token} @var{key}
Encode @var{token} and sign it with the issuer’s @var{key}.
@end deffn

@deffn function issue-id-token @var{issuer-key} @var{#:alg} @var{#:webid} @var{#:iss} @var{#:sub} @var{#:aud} @var{#:validity}
Create an ID token that is valid for @var{#:validity} seconds, and
encode it with @var{issuer-key}.
@end deffn

@node The access token
@section The access token

The access token is obtained by the client through a token request,
and is presented to the server on each authenticated request. It is
signed by the identity provider, and it contains enough information so
that the server knows who the user is and who the agent is, and most
importantly the fingerprint of the key that the client should use in a
DPoP proof.

The API is defined in @emph{(webid-oidc access-token)}.

@deffn function access-token? @var{object}
Check that @var{object} is a decoded access token.
@end deffn

There are field getters for the access token:

@deffn function access-token-webid @var{token}
@deffnx function access-token-iss @var{token}
@deffnx function access-token-aud @var{token}
@deffnx function access-token-exp @var{token}
@deffnx function access-token-iat @var{token}
@deffnx function access-token-cnf/jkt @var{token}
@deffnx function access-token-client-id @var{token}
Get the suitable field from the payload of @var{token}.
@end deffn

Access tokens can be signed and encoded as a string, or decoded.

@deffn function access-token-decode @var{token} @var{[#http-get]}
Decode @var{token}, as a string, into a decoded token. As with the ID
token, the signature verification will need to fetch the oidc
configuration of the claimed issuer, and check the signature against
the published keys. The @code{http-get} optional keyword argument can
set a different implementation of @code{http-get} from
@emph{(web client)}, for instance to re-use the what has been obtained
by the ID token validation. Return @code{#f} if it failed, or the
decoded token otherwise.
@end deffn

@deffn function access-token-encode @var{token} @var{key}
Encode @var{token} and sign it with the issuer’s @var{key}.
@end deffn

@deffn function issue-access-token @var{issuer-key} @var{#alg} @var{#webid} @var{#iss} @var{#:validity} @var{[#client-key} @var{|} @var{#cnf/jkt]} @var{#client-id}
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 in @var{jti-list} until its expiration date
is reached. See the @code{make-jti-list} function.

The proof is limited to the scope of one @var{uri} and one
@var{method} (@code{'GET}, @code{'POST} and so on).

The key that is used to sign the proof should be confirmed by the
identity provider. To this end, the @var{cnf/check} function is called
with the fingerprint of the key. The function should check that the
fingerprint is OK (return a boolean).

Finally, when the DPoP proof is tied to an access token (so, for all
uses except requesting an access token or a refresh token), it must be
bound to an @var{access-token}.
@end deffn

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{#alg} @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 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]}
Drop @var{percents}% of the cache right now.
@end deffn

@deffn function with-cache @var{[#http-get]}
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 the @samp{web-cache}
subdirectory of the cache home. To check the time window validity, the
@var{current-date} parameter is used.

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

@deffn parameter cache-home
This parameters sets the cache directory. By default, it is
@emph{XDG_CACHE_HOME}.
@end deffn

@node Content negociation
@chapter Content negociation
There are a number of different available syntaxes for RDF, some being
simple and human readable like @emph{turtle}, and others more adapted
to the JavaScript ecosystem like @emph{json-ld}. To help clients both
from and outside of the JS ecosystem, the server needs to perform
@dfn{content negociation}, i.e. convert from one content-type to
another.

@deffn {function from @code{(webid-oidc serve)}} convert @var{client-accepts} @var{server-name} @var{path} @var{content-type} @var{content}
Convert the resource representation under @var{path} on
@var{server-name}, which has a given @var{content-type} and
@var{content}, to a content-type that the @var{client accepts}.

Return 2 values:
@enumerate
@item
the accepted content-type;
@item
the content in the given content-type.
@end enumerate

Currently, the only conversions are from and to @emph{Turtle} and
@emph{N-Quads}.
@end deffn

@node Running an Identity Provider
@chapter Running an Identity Provider

This project is packaged with a barebones identity provider. It has an
authorization endpoint and a token endpoint (and it serves its public
keys), but it is only intended for one specific person.

You can start it by invoking the @code{webid-oidc} program with the
@code{issuer} command, with the following options:

@table @asis
@item @code{-h}, or @code{--help}
prints a summary of options and exit.
@item @code{-v}, or @code{--version}
prints the version of the program and exits.
@item @code{-n @var{URI}}, or @code{--server-name=@var{URI}}
sets the global server name of the identity provider. It should have
an empty path.
@item @code{-k @var{FILE.jwk}}, or @code{--key-file=@var{FILE.jwk}}
sets the file name where to read or generate a key for the identity
provider. This file should be JSON, containing the representation of a
JWK key pair.
@item @code{-s @var{WEBID}}, or @code{--subject=@var{WEBID}}
sets the webid of the only user of the identity provider. This is an
URI, pointing to a RDF node corresponding to the user’s profile.
@item @code{-w @var{PASSWORD}}, or @code{--password=@var{PASSWORD}}
sets the password that the user must enter to authorize an
application.
@item @code{-j @var{URI}}, or @code{--jwks-uri=@var{URI}}
tells the server that requests to @var{URI} should be responded with
the public key used to sign the tokens.
@item @code{-a @var{URI}}, or @code{--authorization-endpoint-uri=@var{URI}}
tells the server that requests to @var{URI} should be treated as
authorization requests.
@item @code{-t @var{URI}}, or @code{--token-endpoint-uri=@var{URI}}
tells the server that requests to @var{URI} should be treated as token
negociation requests.
@item @code{-p @var{PORT}}, or @code{--port=@var{PORT}}
change the port number used by the server. By default, it is set to
8080.
@item @code{-l @var{FILE.log}}, or @code{--log-file=@var{FILE.log}}
let the server dump all its output to @var{FILE.log}. Since I don’t
know how to deal with syslog, this is the only way to keep logs with a
shepherd service.
@item @code{-e @var{FILE.err}}, or @code{--error-file=@var{FILE.err}}
let the server dump all its errors to @var{FILE.err}.
@end table

The program is sensitive to the environment variables. The most
important one is @emph{LANG}, which influences how the program is
internationalized to the server administrator (the pages served to the
user use the user agent’s locale). This changes the long form of the
options, and the language in the log files.

The @emph{XDG_DATA_HOME} should point to some place where the program
will store refresh tokens, under the @code{webid-oidc} directory. For
a system service, you might want to define that environment to
@code{/var/lib}, for instance.

The @emph{XDG_CACHE_HOME} should point to a directory where to store
the seed of the random number generator (under a @code{webid-oidc}
directory, again). Changing the seed only happens when a program
starts to require the random number generator. You can safely delete
this directory, but you need to restart the program to actually change
the seed.

@node Running a Resource Server
@chapter Running a Resource Server

@menu
* The authenticator::
* The full server::
* Resources stored on the server::
@end menu

A Solid server is the server that manages your data. It needs to check
that the proofs of possession are correct, and the possessed key is
signed by the identity provider.

@node The authenticator
@section The authenticator

In @emph{(webid-oidc resource-server)}, the following function gives a
simple API for a web server:

@deffn function make-authenticator @var{jti-list} @var{[#server-uri]} @var{[#current-time]} @var{[#http-get]}
Create an authenticator, i.e. a function that takes a request and
request body and returns the webid of the authenticated user, or
@code{#f} if it is not authenticated.

To prevent replay attacks, each request is signed by the client with a
different unique padding value. If such a value has already been seen,
then the request must fail.

The authenticator expects the client to demonstrate the possession of
a key that the identity provider knows. So the client creates a DPoP
proof, targetted to a specific URI. In order to check that the URI is
correct, the authenticator needs the public URI of the service.

The JTIs are checked within a small time frame. By default, the system
time will be used. Otherwise, you can customize the
@code{current-time} optional keyword argument, to pass a thunk
returning a time from @emph{(srfi srfi-19)}.

You may want to customize the @var{http-get} optional keyword argument
to pass a function to replace @code{http-get} from @emph{(http
client)}. This function takes an URI and optional @code{#:headers}
arguments, makes the request, and return two values: the response, and
the response body.

This function, in @emph{(webid-oidc resource-server)}, returns a web
request handler, taking the request and request body, and returning
the subject of the access token. If an error happens, it is thrown;
the function always returns a valid URI.
@end deffn

@node The full server
@section The full server

@deffn {function from @emph{(webid-oidc resource-server)}} make-server @var{[#:server-uri]} @var{[#:owner]} @var{[#:authenticator]} @var{[#:current-time]} @var{[#:http-get]}
Return a server handler, a function taking 2 values, a request and a
request body, and returning 2 values, the response and response body.

The optional @var{[#:authenticator]} argument defaults to the
webid-oidc authenticator, @var{[#:current-time]} defaults to a thunk
returning the system time and @var{[#:http-get]} to the web client
from @emph{(web client)}.
@end deffn

@node Resources stored on the server
@section Resources stored on the server

To store and serve resources, the server has two distinct
mechanisms. A @dfn{content} is a read-only possible value for a
resource, indexed by etags, and a @dfn{path} is a mutable value that
indicates the etag of the resource, and of the auxiliary resources
(description and ACL). With this separation, it is possible to
atomically delete a resource and all associated auxiliary resources,
by unlinking the corresponding @emph{path}. It is also possible to
mutate separately the ACL and the resource itself without writing a
copy for both.

The @emph{content} API is contained in the
@code{(webid-oidc server resource content)} module.

@deffn function with-session @var{f}
Call @var{f} with 5 arguments:
@itemize
@item
a function to get the content-type of a given etag;
@item
a function to list the paths contained within the resource;
@item
a function to load the content of a given etag;
@item
a function to create a new content;
@item
a function to remove a content from the file system. It is still
possible to query it with the first 3 functions, but new sessions will
not see it.
@end itemize

Since the contents are read-only, it is possible to cache the value of
the content in memory. This is why @var{f} should run within a session
with memoization.

Resources only store @emph{static} content, because the membership
triples for containers is considered dynamic and not included in the
representation.

The first 3 functions as well as the last one are called with an etag,
and the function to create a content is called with the content-type,
list of contained paths, and (static) content.

The contents are searched in the @emph{server/content} subdirectory of
@var{data-home}.
@end deffn

@deffn parameter data-home
Defines the directory where to store persistent data. Defaults to
@emph{XDG_DATA_HOME}.
@end deffn

The @emph{path} API is defined in
@code{(webid-oidc server resource path)}.

@deffn function read-path @var{path}
Read the resource at @var{path}, and return 2 values:
@enumerate
@item
the ETag of the main resource;
@item
an alist where keys are auxiliary resource type URIs (the type is from
@code{(web uri)}), and the values are ETags of the corresponding
resource.
@end enumerate

If the resource is not found, raise an exception with type
@code{&path-not-found}, and maybe @code{&uri-slash-semantics-error} if
a resource with a different ending-in-slash exists.

This function is safe to call when the path is being modified, either
by another thread, process or else, as the returned values will always
be consistent. However, once the function returns, an updating process
may have deleted the returned ETags. If this is the case, then you
must call this function again to read the updated path.
@end deffn

@deffn function update-path @var{path} @var{f} @var{content-type} @var{contained} @var{static-content} @var{create} @var{delete} [@var{#:create-intermediate-containers?}=@code{#f}]
Read @var{path}, call @var{f} with two values: the ETag and the
auxiliary ETags (as returned by @var{read-path}), and update the path
accordingly. If @var{path} does not exist, then the first argument is
@code{#f} and the second one is the empty list.

If @var{f} returns @code{#f}, then the resource is deleted.

If @var{f} returns an ETag as the first returned value and an alist of
auxiliary resource ETags as the second value, then the resource is
updated.

The last functions are from the content API. Since creating or
deleting children requires updating the parent, we need them.

Some operations should create the intermediate containers for a given
path, this is the case for the @code{PUT} HTTP verb. For @code{POST},
the parent should exist. The @var{#:create-intermediate-containers?}
switch lets you change the behavior. In any case, it is an error to
delete a non-empty container.

The update is atomic, meaning that at any point in time the file is
fully written out. Concurrent access to the same resource is performed
by locking the lock file named @var{X}/.lock, where @var{X} is the
first character of the base64-url sha-256 hash of the
path. @strong{The lock file is not meant to be removed} when the
resource is unlocked. It should be locked with @code{flock}
instead. @strong{Like other forms of lock-based synchronization, this
function is not composable}. This means that you cannot call this
function within @var{f}, otherwise a deadlock may ensue.

If the resource is created or deleted, then the parent resource is
updated as well. To avoid deadlocks with other processes, please
follow the following rules: lock the path, then lock the parent path,
then update the parent, then unlock the parent, and finally unlock the
child path.
@end deffn

The Web Access Control specification defines an RDF vocabulary to
check whether a given user is allowed to perform some operations. The
@code{(webid-oidc server resource wac)} helps you do that.

@deffn function wac-get-modes @var{server-name} @var{path} @var{user} @var{[#:http-get]}
Return the list of modes that are allowed for @var{user} accessing
@var{path}. The @var{server-name} URI is required to find the relevant
triples in the ACL. If @var{user} is unauthenticated, pass @code{#f}.

Please note that in any case, the data owner should have all rights
whatsoever, bypassing WAC. Otherwise, it is possible to steal control
away from the data owner.
@end deffn

@deffn function check-acl-can-read @var{server-name} @var{path} @var{owner} @var{user} @var{[#:http-get]}
@deffnx function check-acl-can-write @var{server-name} @var{path} @var{owner} @var{user} @var{[#:http-get]}
@deffnx function check-acl-can-append @var{server-name} @var{path} @var{owner} @var{user} @var{[#:http-get]}
@deffnx function check-acl-can-control @var{server-name} @var{path} @var{owner} @var{user} @var{[#:http-get]}
Assert that the resource at @var{path} on @var{server-name} is owned
by @var{owner}, and check that @var{user} has the proper
authorization. Otherwise, raise an exception of type
@code{&forbidden}.
@end deffn

@node Running a client
@chapter Running a client

To run a client, you need to proceed in two steps. First, acquire an
OIDC ID token and an access token from the identity provider, and then
present the access token and a proof of possession of the linked key
in each request, in a DPoP HTTP header.

The first operation is performed by the @emph{(webid-oidc client)}
module.

@deffn function authorize @var{host/webid} @var{#client-id} @var{#redirect-uri} @var{[#state]} @var{[#http-get]}
The user enters a valid webid or a host name, and then this function
will query it (with the @var{http-get} parameter, by default the web
client from @emph{(web client)}) to determine the authorization
endpoint. The function will return an alist of authorization URIs,
indexed by approved identity provider URIs, that the user should
browse with a traditional web browser.

Each application should have its own webid, or in that case
@var{client-id}, that can be dereferenced by the identity provider.

Once the user has given authorization, the user’s agent will be
redirected to @var{redirect-uri}, with the authorization code as a GET
parameter. It is possible to pass a @var{state}, but this is optional.
@end deffn

Once the client gets the authorization code, it is necessary to create
an access token and ID token.

@deffn function token @var{host} @var{client-key} @var{[#authorization-code]} @var{[#refresh-token]} @var{[#http-get]} @var{[#http-post]} @var{[#current-time]}
Trade an @var{authorization-code}, or a @var{refresh-token}, for an ID
token and an access token bound to the @var{client-key} issued by
@var{host}, the identity provider.

You can override the HTTP client used (@var{http-get} and
@var{http-post}), and how to compute the time (@var{current-time}).
@end deffn

In an application, you would have a list of profiles in XDG_DATA_HOME,
consisting of triples (webid, issuer, refresh token).

@deffn function list-profiles @var{[#dir]}
Read the list of available profiles. Returns a list of triples, webid,
issuer, reresh token.

By default, this function will look for the profiles file in
@var{XDG_DATA_HOME}. You can bypass it by providing the @var{#dir}
optional keyword argument.
@end deffn

@deffn function setup @var{get-host/webid} @var{choose-provider} @var{browse-authorization-uri} @var{#client-id} @var{#redirect-uri} @var{[#dir]} @var{[#http-get]} @var{[#http-post]} @var{[#current-time]}
Negociate a refresh token, and save it. The function returns 3 values:
the decoded ID token pyload, the encoded access token and the key
pair.

The @var{get-host/webid} thunk should ask the user’s webid or identity
provider, and return it. @var{choose-provider} is called with a list
of possible identity providers as host names (strings), and the user
should choose one. The chosen one is returned. Finally,
@var{browse-authorization-uri} should ask or let the user browse an
URI as its argument, and return the authorization code taken from the
redirect URI.

The refresh token is saved to disk, as a profile, in
XDG_DATA_HOME. Pass the optional @var{#dir} keyword argument to
override the location.

You need to set @var{client-id} to the public webid of the app, and
@var{redirect-uri} to one of the approved redirection URIs for the
application ID.
@end deffn

@deffn function login @var{webid} @var{issuer} @var{refresh-token} @var{key} @var{[#dir]} @var{[#http-get]} @var{[#http-post]} @var{[#current-time]}
If you have already a known profile, you can use it to automatically
log in. This function might update the refresh token if it changed, so
you can again set @var{#dir}. Please note that the @var{refresh-token}
is bound to the client @var{key} on server side, so you must always
use the same @var{key}.
@end deffn

@deffn function refresh @var{id-token} @var{key} @var{[#dir]} @var{[#http-get]} @var{[#http-post]} @var{[#current-time]}
If you have an ID token bound to a known profile, this helper function
will look up the associated refresh token and log in.
@end deffn

@deffn function make-client @var{id-token} @var{access-token} @var{key} @var{[#dir]} @var{[#http-get]} @var{[#http-post]} @var{[#http-request]} @var{[#current-time]}
Return a replacement of @code{http-request} from @emph{(web client)},
that automatically signs requests and refresh the tokens when needed.

@var{#http-get} and @var{#http-post} are only used to refresh the
tokens, while @var{#http-request} is used as a back-end for the
requests.

@var{#current-time} is set to a thunk that returns the time. It is
used to issue DPoP proofs.
@end deffn

An example application is provided as the
@code{disfluid-example-app} program. It demonstrates how
authentication is done. It should help you understand how Solid-OIDC
works.

The identity provider needs to call the application on the web. So,
your client should have a public endpoint on the web.

@deffn function serve-application @var{id} @var{redirect-uri} @var{[#client-name]} @var{[#client-uri]}
Return a handler for web requests to serve the application manifest
and the redirection to transmit the authorization code. You should set
the @var{client-name} to your application name and @var{client-uri} to
point to where to a presentation of your application.
@end deffn

@node Exceptional conditions
@chapter Exceptional conditions

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

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

@menu
* Invalid data format::
* Invalid JWT::
* Cannot fetch data on the web::
* Other errors in the protocol or from a reasonable implementation::
* Server-side errors::
@end menu

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

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

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

@deftp {exception type} &not-turtle @var{value} @var{cause}
Cannot decode @var{value} to a RDF graph.
@end deftp

@deftp {exception type} &incorrect-webid-field @var{value}
The @var{value} of the webid field in the JWT is missing (if
@code{#f}), or not an acceptable value.
@end deftp

@deftp {exception type} &incorrect-iss-field @var{value}
The @var{value} of the iss field is incorrect.
@end deftp

@deftp {exception type} &incorrect-aud-field @var{value}
The @var{value} of the aud field is incorrect.
@end deftp

@deftp {exception type} &incorrect-iat-field @var{value}
The @var{value} of the iat field is incorrect.
@end deftp

@deftp {exception type} &incorrect-exp-field @var{value}
The @var{value} of the exp field is incorrect.
@end deftp

@deftp {exception type} &incorrect-cnf/jkt-field @var{value}
The @var{value} of the cnf/jkt field is incorrect.
@end deftp

@deftp {exception type} &incorrect-client-id-field @var{value}
The @var{value} of the client-id field is incorrect.
@end deftp

@deftp {exception type} &incorrect-typ-field @var{value}
The @var{value} of the typ field in the DPoP proof header is
incorrect.
@end deftp

@deftp {exception type} &incorrect-jwk-field @var{value} @var{cause}
The @var{value} of the jwk field in the DPoP proof header is
incorrect.
@end deftp

@deftp {exception type} &incorrect-jti-field @var{value}
The @var{value} of the jti field in the DPoP proof is incorrect.
@end deftp

@deftp {exception type} &incorrect-htm-field @var{value}
The @var{value} of the htm field in the DPoP proof is incorrect.
@end deftp

@deftp {exception type} &incorrect-htu-field @var{value}
The @var{value} of the htu field in the DPoP proof is incorrect.
@end deftp

@deftp {exception type} &incorrect-ath-field @var{value}
The @var{value} of the ath field is not the hash of the access token.
@end deftp

@deftp {exception type} &incorrect-redirect-uris-field @var{value}
The @var{value} of the redirect-uris field of a client manifest is
incorrect.
@end deftp

@deftp {exception type} &incorrect-typ-field @var{value}
The @var{value} of the typ field in the DPoP proof header is
incorrect.
@end deftp

@deftp {exception type} &incorrect-sub-field @var{value}
The @var{value} of the sub field is incorrect.
@end deftp

@deftp {exception type} &incorrect-iss-field @var{value}
The @var{value} of the iss field is incorrect.
@end deftp

@deftp {exception type} &incorrect-nonce-field @var{value}
The @var{value} of the nonce field in the DPoP proof is incorrect.
@end deftp

@deftp {exception type} &incorrect-htm-field @var{value}
The @var{value} of the htm field in the DPoP proof is incorrect.
@end deftp

@deftp {exception type} &not-a-client-manifest @var{value} @var{cause}
The @var{client-manifest} is incorrect.
@end deftp

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@deftp {exception type} &not-an-access-token @var{value} @var{cause}
The @var{value} is not an access token.
@end deftp

@deftp {exception type} &not-an-access-token-header @var{value} @var{cause}
The @var{value} is not an access token header.
@end deftp

@deftp {exception type} &not-an-access-token-payload @var{value} @var{cause}
The @var{value} is not an access token payload.
@end deftp

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

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

@deftp {exception type} &not-a-dpop-proof @var{value} @var{cause}
The @var{value} is not a DPoP proof.
@end deftp

@deftp {exception type} &not-a-dpop-proof-header @var{value} @var{cause}
The @var{value} is not a DPoP proof header.
@end deftp

@deftp {exception type} &not-a-dpop-proof-payload @var{value} @var{cause}
The @var{value} is not a DPoP proof payload.
@end deftp

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

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

@deftp {exception type} &not-an-authorization-code @var{value} @var{cause}
The @var{value} is not an authorization code.
@end deftp

@deftp {exception type} &not-an-authorization-code-header @var{value} @var{cause}
The @var{value} is not an authorization code header.
@end deftp

@deftp {exception type} &not-an-authorization-code-payload @var{value} @var{cause}
The @var{value} is not an authorization code payload.
@end deftp

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

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

@deftp {exception type} &not-an-id-token @var{value} @var{cause}
The @var{value} is not an ID token.
@end deftp

@deftp {exception type} &not-an-id-token-header @var{value} @var{cause}
The @var{value} is not an ID token header.
@end deftp

@deftp {exception type} &not-an-id-token-payload @var{value} @var{cause}
The @var{value} is not an ID token payload.
@end deftp

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

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

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

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

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

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

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

@deftp {exception type} &cannot-fetch-issuer-configuration @var{issuer} @var{cause}
It is impossible to fetch the configuration of @var{issuer}.
@end deftp

@deftp {exception type} &cannot-fetch-jwks @var{issuer} @var{uri} @var{cause}
It is impossible to fetch the keys of @var{issuer} at @var{uri}.
@end deftp

@deftp {exception type} &cannot-fetch-linked-data @var{uri} @var{cause}
Could not fetch the graph referenced by @var{uri}.
@end deftp

@deftp {exception type} &cannot-fetch-client-manifest @var{id} @var{cause}
Could not fetch a client manifest at @var{id}.
@end deftp

@node Other errors in the protocol or from a reasonable implementation
@section Other errors in the protocol or from a reasonable implementation
The protocol does not rely solely on JWT validation, so these errors
may happen too.

@deftp {exception type} &dpop-method-mismatch @var{signed} @var{requested}
The method value @var{signed} in the DPoP proof does not match the
method that is @var{requested} on the server.
@end deftp

@deftp {exception type} &dpop-uri-mismatch @var{signed} @var{requested}
The URI value @var{signed} in the DPoP proof does not match the URI
that is @var{requested} on the server.
@end deftp

@deftp {exception type} &dpop-signed-in-future @var{signed} @var{current}
The proof is @var{signed} for a date which is too much ahead of the
@var{current} time.
@end deftp

@deftp {exception type} &dpop-too-old @var{signed} @var{current}
The proof was @var{signed} at a past date of @var{current}.
@end deftp

@deftp {exception type} &dpop-unconfirmed-key @var{key} @var{expected} @var{cause}
The confirmation of @var{key} is not what is @var{expected}, or (if a
function was passed as @var{cnf/check}) the @var{cause} exception
occurred while confirming.
@end deftp

@deftp {exception type} &dpop-invalid-access-token-hash @var{hash} @var{access-token}
The @var{access-token} passed to the resource server does not match
the @var{hash} provided in the DPoP proof.
@end deftp

@deftp {exception type} &jti-found @var{jti} @var{cause}
The @var{jti} of the proof has already been issued in a recent past.
@end deftp

@deftp {exception type} &unauthorized-redirection-uri @var{manifest} @var{uri}
The authorization @var{uri} is not advertised in @var{manifest}.
@end deftp

@deftp {exception type} &cannot-serve-public-manifest 
You cannot serve the public client manifest.
@end deftp

@deftp {exception type} &no-client-manifest-registration @var{id}
The @var{id} client manifest does not have a registration triple in
its document.
@end deftp

@deftp {exception type} &inconsistent-client-manifest-id @var{id} @var{advertised-id}
The client @var{manifest} is being fetched at @var{id}, but it is
valid for another client @var{advertised-id}.
@end deftp

@deftp {exception type} &authorization-code-expired @var{exp} @var{current-time}
The authorization code has expired at @var{exp}, it is now
@var{current-time}.
@end deftp

@deftp {exception type} &invalid-refresh-token @var{refresh-token}
The @var{refresh-token} is unknown to the identity provider.
@end deftp

@deftp {exception type} &invalid-key-for-refresh-token @var{key} @var{jkt}
The refresh token was issued for @var{jkt}, but it is used with
@var{key}.
@end deftp

@deftp {exception type} &unknown-client-locale @var{web-locale} @var{c-locale}
The @var{web-locale} of the client, translated to C as @var{c-locale},
cannot be set. This exception is always continuable; if the handler
returns, then the page will be served in the english locale.
@end deftp

@deftp {exception type} &unsupported-grant-type @var{value}
The token request failed to indicate a @var{value} for the grant type,
or indicated an unsupported grant type.
@end deftp

@deftp {exception type} &no-authorization-code 
The token request forgot to put an authorization code.
@end deftp

@deftp {exception type} &no-refresh-token 
The token request forgot to put a refresh token with the request.
@end deftp

@deftp {exception type} &unconfirmed-provider @var{subject} @var{provider}
@var{provider} is not confirmed by @var{subject} as an identity
provider.
@end deftp

@deftp {exception type} &no-provider-candidates @var{webid} @var{causes}
The @var{webid} cannot be certified by any identity providers. The
@var{causes} alist indicates an error for each candidates.
@end deftp

@deftp {exception type} &neither-identity-provider-nor-webid @var{uri} @var{why-not-identity-provider} @var{why-not-webid}
The @var{uri} you passed to get an authorization code is neither an
identity provider (because @var{why-not-identity-provider}) nor a
webid (because @var{why-not-webid}).
@end deftp

@deftp {exception type} &token-request-failed @var{cause}
The token request failed on the server.
@end deftp

@deftp {exception type} &profile-not-found @var{webid} @var{iss} @var{dir}
The @var{webid}, as certified by @var{iss}, cannot be refreshed
because we don’t have a refresh token stored in @var{dir}.
@end deftp

@node Server-side errors
@section Server-side errors
The resource server implementation may encounter some more exceptional
conditions.

@deftp {exception type} &path-not-found @var{path}
There is no registered resource at @var{path}.
@end deftp

@deftp {exception type} &auxiliary-resource-absent @var{path} @var{kind}
The auxiliary resource of given @var{kind} is not instanciated on the
server for the base resource @var{path}.
@end deftp

@deftp {exception type} &uri-slash-semantics-error @var{path} @var{expected-path}
While the resource at @var{path} does not exist, the resource at
@var{expected-path} does, and @var{path} and @var{expected-path}
differ only by a trailing slash. This exception may be raised along
with @code{&path-not-found}.

Beware that even if it is true at the time when the exception is
created, maybe the resource has been created by the time it is
handled.
@end deftp

@deftp {exception type} &cannot-delete-root
There was a request to delete the root storage, which is an error.
@end deftp

@deftp {exception type} &container-not-empty @var{path}
There was a request to delete a non-empty container.
@end deftp

@deftp {exception type} &cannot-fetch-group @var{group-uri} @var{cause}
The access control could not fetch the group @var{group-uri} (with a
known @var{cause}). This warning is continuable every time it is
raised. If the handler returns, then the group will be considered
empty.
@end deftp

@deftp {exception type} &incorrect-containment-triples @var{path}
The client wanted to create or update a resource, and by that it tried
to change the containment triples at @var{path}.
@end deftp

@deftp {exception type} &unsupported-media-type @var{content-type}
The client wanted to create a resource with the given
@var{content-type}, but it is not accepted, because @var{content-type}
is not recognized as an RDF content type.
@end deftp

@deftp {exception type} &path-is-auxiliary @var{path}
The client wanted to create a resource that targets an auxiliary
resource, at @var{path}.
@end deftp

@deftp {exception type} &forbidden @var{path} @var{user} @var{owner} @var{mode}
The @var{user} wanted to do something under @var{path} requiring
@var{mode}, but it is not the @var{owner} and it is forbidden by WAC.
@end deftp

@deftp {exception type} &precondition-failed @var{path} @var{if-match} @var{if-none-match} @var{real-etag}
The resource under @var{path} has a @var{real-etag} that does not
match the request headers @var{if-match} and @var{if-none-match}.

If the resource does not exist, @var{real-etag} is set to
@code{#f}. In this case, an exception of type @code{&path-not-found}
is also thrown.
@end deftp

@deftp {exception type} &not-acceptable @var{client-accepts} @var{path} @var{content-type}
The client wanted a response with a specific set of
@var{client-accept}ed content-types, but the real @var{content-type}
of the resource under @var{path} cannot be converted to one of them.
@end deftp
@node GNU Free Documentation License
@appendix GNU Free Documentation License

@include fdl.texi

@node Index
@unnumbered Index

@printindex cp

@bye