-
Notifications
You must be signed in to change notification settings - Fork 45
/
oidc-primer.html
1227 lines (1179 loc) · 79.1 KB
/
oidc-primer.html
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
<!doctype html><html lang="en">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<title>Solid-OIDC Primer</title>
<link href="https://www.w3.org/StyleSheets/TR/2021/base.css" rel="stylesheet">
<link href="https://www.w3.org/2008/site/images/favicon.ico" rel="icon">
<meta content="Bikeshed version fb1e763a4, updated Tue Mar 1 13:13:50 2022 -0800" name="generator">
<link href="https://solidproject.org/TR/oidc" rel="canonical">
<link href="https://solidproject.org/TR/solid.svg" rel="icon">
<meta content="4b2c4244d3a9df4680d875d240a8defbf33aaa81" name="document-revision">
<style>/* style-autolinks */
.css.css, .property.property, .descriptor.descriptor {
color: var(--a-normal-text);
font-size: inherit;
font-family: inherit;
}
.css::before, .property::before, .descriptor::before {
content: "‘";
}
.css::after, .property::after, .descriptor::after {
content: "’";
}
.property, .descriptor {
/* Don't wrap property and descriptor names */
white-space: nowrap;
}
.type { /* CSS value <type> */
font-style: italic;
}
pre .property::before, pre .property::after {
content: "";
}
[data-link-type="property"]::before,
[data-link-type="propdesc"]::before,
[data-link-type="descriptor"]::before,
[data-link-type="value"]::before,
[data-link-type="function"]::before,
[data-link-type="at-rule"]::before,
[data-link-type="selector"]::before,
[data-link-type="maybe"]::before {
content: "‘";
}
[data-link-type="property"]::after,
[data-link-type="propdesc"]::after,
[data-link-type="descriptor"]::after,
[data-link-type="value"]::after,
[data-link-type="function"]::after,
[data-link-type="at-rule"]::after,
[data-link-type="selector"]::after,
[data-link-type="maybe"]::after {
content: "’";
}
[data-link-type].production::before,
[data-link-type].production::after,
.prod [data-link-type]::before,
.prod [data-link-type]::after {
content: "";
}
[data-link-type=element],
[data-link-type=element-attr] {
font-family: Menlo, Consolas, "DejaVu Sans Mono", monospace;
font-size: .9em;
}
[data-link-type=element]::before { content: "<" }
[data-link-type=element]::after { content: ">" }
[data-link-type=biblio] {
white-space: pre;
}</style>
<style>/* style-colors */
/* Any --*-text not paired with a --*-bg is assumed to have a transparent bg */
:root {
color-scheme: light dark;
--text: black;
--bg: white;
--unofficial-watermark: url(https://www.w3.org/StyleSheets/TR/2016/logos/UD-watermark);
--logo-bg: #1a5e9a;
--logo-active-bg: #c00;
--logo-text: white;
--tocnav-normal-text: #707070;
--tocnav-normal-bg: var(--bg);
--tocnav-hover-text: var(--tocnav-normal-text);
--tocnav-hover-bg: #f8f8f8;
--tocnav-active-text: #c00;
--tocnav-active-bg: var(--tocnav-normal-bg);
--tocsidebar-text: var(--text);
--tocsidebar-bg: #f7f8f9;
--tocsidebar-shadow: rgba(0,0,0,.1);
--tocsidebar-heading-text: hsla(203,20%,40%,.7);
--toclink-text: var(--text);
--toclink-underline: #3980b5;
--toclink-visited-text: var(--toclink-text);
--toclink-visited-underline: #054572;
--heading-text: #005a9c;
--hr-text: var(--text);
--algo-border: #def;
--del-text: red;
--del-bg: transparent;
--ins-text: #080;
--ins-bg: transparent;
--a-normal-text: #034575;
--a-normal-underline: #bbb;
--a-visited-text: var(--a-normal-text);
--a-visited-underline: #707070;
--a-hover-bg: rgba(75%, 75%, 75%, .25);
--a-active-text: #c00;
--a-active-underline: #c00;
--blockquote-border: silver;
--blockquote-bg: transparent;
--blockquote-text: currentcolor;
--issue-border: #e05252;
--issue-bg: #fbe9e9;
--issue-text: var(--text);
--issueheading-text: #831616;
--example-border: #e0cb52;
--example-bg: #fcfaee;
--example-text: var(--text);
--exampleheading-text: #574b0f;
--note-border: #52e052;
--note-bg: #e9fbe9;
--note-text: var(--text);
--noteheading-text: hsl(120, 70%, 30%);
--notesummary-underline: silver;
--assertion-border: #aaa;
--assertion-bg: #eee;
--assertion-text: black;
--advisement-border: orange;
--advisement-bg: #fec;
--advisement-text: var(--text);
--advisementheading-text: #b35f00;
--warning-border: red;
--warning-bg: hsla(40,100%,50%,0.95);
--warning-text: var(--text);
--amendment-border: #330099;
--amendment-bg: #F5F0FF;
--amendment-text: var(--text);
--amendmentheading-text: #220066;
--def-border: #8ccbf2;
--def-bg: #def;
--def-text: var(--text);
--defrow-border: #bbd7e9;
--datacell-border: silver;
--indexinfo-text: #707070;
--indextable-hover-text: black;
--indextable-hover-bg: #f7f8f9;
--outdatedspec-bg: rgba(0, 0, 0, .5);
--outdatedspec-text: black;
--outdated-bg: maroon;
--outdated-text: white;
--outdated-shadow: red;
--editedrec-bg: darkorange;
}</style>
<style>/* style-counters */
body {
counter-reset: example figure issue;
}
.issue {
counter-increment: issue;
}
.issue:not(.no-marker)::before {
content: "Issue " counter(issue);
}
.example {
counter-increment: example;
}
.example:not(.no-marker)::before {
content: "Example " counter(example);
}
.invalid.example:not(.no-marker)::before,
.illegal.example:not(.no-marker)::before {
content: "Invalid Example" counter(example);
}
figcaption {
counter-increment: figure;
}
figcaption:not(.no-marker)::before {
content: "Figure " counter(figure) " ";
}</style>
<style>/* style-issues */
a[href].issue-return {
float: right;
float: inline-end;
color: var(--issueheading-text);
font-weight: bold;
text-decoration: none;
}
</style>
<style>/* style-md-lists */
/* This is a weird hack for me not yet following the commonmark spec
regarding paragraph and lists. */
[data-md] > :first-child {
margin-top: 0;
}
[data-md] > :last-child {
margin-bottom: 0;
}</style>
<style>/* style-selflinks */
:root {
--selflink-text: white;
--selflink-bg: gray;
--selflink-hover-text: black;
}
.heading, .issue, .note, .example, li, dt {
position: relative;
}
a.self-link {
position: absolute;
top: 0;
left: calc(-1 * (3.5rem - 26px));
width: calc(3.5rem - 26px);
height: 2em;
text-align: center;
border: none;
transition: opacity .2s;
opacity: .5;
}
a.self-link:hover {
opacity: 1;
}
.heading > a.self-link {
font-size: 83%;
}
li > a.self-link {
left: calc(-1 * (3.5rem - 26px) - 2em);
}
dfn > a.self-link {
top: auto;
left: auto;
opacity: 0;
width: 1.5em;
height: 1.5em;
background: var(--selflink-bg);
color: var(--selflink-text);
font-style: normal;
transition: opacity .2s, background-color .2s, color .2s;
}
dfn:hover > a.self-link {
opacity: 1;
}
dfn > a.self-link:hover {
color: var(--selflink-hover-text);
}
a.self-link::before { content: "¶"; }
.heading > a.self-link::before { content: "§"; }
dfn > a.self-link::before { content: "#"; }
</style>
<style>/* style-syntax-highlighting */
code.highlight { padding: .1em; border-radius: .3em; }
pre.highlight, pre > code.highlight { display: block; padding: 1em; margin: .5em 0; overflow: auto; border-radius: 0; }
.highlight:not(.idl) { background: rgba(0, 0, 0, .03); }
c-[a] { color: #990055 } /* Keyword.Declaration */
c-[b] { color: #990055 } /* Keyword.Type */
c-[c] { color: #708090 } /* Comment */
c-[d] { color: #708090 } /* Comment.Multiline */
c-[e] { color: #0077aa } /* Name.Attribute */
c-[f] { color: #669900 } /* Name.Tag */
c-[g] { color: #222222 } /* Name.Variable */
c-[k] { color: #990055 } /* Keyword */
c-[l] { color: #000000 } /* Literal */
c-[m] { color: #000000 } /* Literal.Number */
c-[n] { color: #0077aa } /* Name */
c-[o] { color: #999999 } /* Operator */
c-[p] { color: #999999 } /* Punctuation */
c-[s] { color: #a67f59 } /* Literal.String */
c-[t] { color: #a67f59 } /* Literal.String.Single */
c-[u] { color: #a67f59 } /* Literal.String.Double */
c-[cp] { color: #708090 } /* Comment.Preproc */
c-[c1] { color: #708090 } /* Comment.Single */
c-[cs] { color: #708090 } /* Comment.Special */
c-[kc] { color: #990055 } /* Keyword.Constant */
c-[kn] { color: #990055 } /* Keyword.Namespace */
c-[kp] { color: #990055 } /* Keyword.Pseudo */
c-[kr] { color: #990055 } /* Keyword.Reserved */
c-[ld] { color: #000000 } /* Literal.Date */
c-[nc] { color: #0077aa } /* Name.Class */
c-[no] { color: #0077aa } /* Name.Constant */
c-[nd] { color: #0077aa } /* Name.Decorator */
c-[ni] { color: #0077aa } /* Name.Entity */
c-[ne] { color: #0077aa } /* Name.Exception */
c-[nf] { color: #0077aa } /* Name.Function */
c-[nl] { color: #0077aa } /* Name.Label */
c-[nn] { color: #0077aa } /* Name.Namespace */
c-[py] { color: #0077aa } /* Name.Property */
c-[ow] { color: #999999 } /* Operator.Word */
c-[mb] { color: #000000 } /* Literal.Number.Bin */
c-[mf] { color: #000000 } /* Literal.Number.Float */
c-[mh] { color: #000000 } /* Literal.Number.Hex */
c-[mi] { color: #000000 } /* Literal.Number.Integer */
c-[mo] { color: #000000 } /* Literal.Number.Oct */
c-[sb] { color: #a67f59 } /* Literal.String.Backtick */
c-[sc] { color: #a67f59 } /* Literal.String.Char */
c-[sd] { color: #a67f59 } /* Literal.String.Doc */
c-[se] { color: #a67f59 } /* Literal.String.Escape */
c-[sh] { color: #a67f59 } /* Literal.String.Heredoc */
c-[si] { color: #a67f59 } /* Literal.String.Interpol */
c-[sx] { color: #a67f59 } /* Literal.String.Other */
c-[sr] { color: #a67f59 } /* Literal.String.Regex */
c-[ss] { color: #a67f59 } /* Literal.String.Symbol */
c-[vc] { color: #0077aa } /* Name.Variable.Class */
c-[vg] { color: #0077aa } /* Name.Variable.Global */
c-[vi] { color: #0077aa } /* Name.Variable.Instance */
c-[il] { color: #000000 } /* Literal.Number.Integer.Long */
</style>
<body class="h-entry">
<div class="head">
<p data-fill-with="logo"><a class="logo" href="https://solidproject.org/TR/"> <img alt="Solid" src="https://solidproject.org/TR/solid.svg" width="72"> </a> </p>
<h1 class="p-name no-ref" id="title">Solid-OIDC Primer</h1>
<h2>Version 0.1.0, 2022-03-28</h2>
<details open>
<summary>More details about this document</summary>
<div data-fill-with="spec-metadata">
<dl>
<dt>This version:
<dd><a class="u-url" href="https://solidproject.org/TR/2022/oidc-primer-20220328">https://solidproject.org/TR/2022/oidc-primer-20220328</a>
<dt>Latest published version:
<dd><a class="u-url" href="https://solidproject.org/TR/oidc-primer">https://solidproject.org/TR/oidc-primer</a>
<dt>Editor's Draft:
<dd><a class="u-url" href="https://solid.github.io/solid-oidc/primer/">https://solid.github.io/solid-oidc/primer/</a>
<dt>Issue Tracking:
<dd><a href="https://github.com/solid/solid-oidc/issues/">GitHub</a>
<dt class="editor">Editors:
<dd class="editor p-author h-card vcard"><span class="p-name fn"><a href="https://github.com/jaxoncreed">Jackson Morgan</a></span>
<dd class="editor p-author h-card vcard"><span class="p-name fn"><a href="https://github.com/acoburn">Aaron Coburn</a></span>
<dd class="editor p-author h-card vcard"><span class="p-name fn"><a href="https://github.com/matthieubosquet">Matthieu Bosquet</a></span>
</dl>
</div>
</details>
<div data-fill-with="warning"></div>
<p class="copyright" data-fill-with="copyright">MIT License. Copyright © 2019–2022 <a href="http://www.w3.org/community/solid/">W3C Solid Community Group</a>.</p>
<hr title="Separator for header">
</div>
<div class="p-summary" data-fill-with="abstract">
<h2 class="no-num no-toc no-ref heading settled" id="abstract"><span class="content">Abstract</span></h2>
<p>The Solid OpenID Connect (Solid OIDC) specification defines how resource servers
verify the identity of relying parties and end users based on the authentication
performed by an OpenID provider. Solid OIDC builds on top of OpenID Connect 1.0.
This primer is designed to provide the reader with the basic knowledge required
to understand Solid OpenID Connect authentication flows. It introduces the basic
concepts of authentication in the Solid ecosystem.</p>
</div>
<h2 class="no-num no-toc no-ref heading settled" id="sotd"><span class="content">Status of this document</span></h2>
<div data-fill-with="status">
<p>This section describes the status of this document at the time of its publication.</p>
<p>This document was published by the <a href="https://www.w3.org/community/solid/">Solid Community Group</a> as <em>Version 0.1.0</em>. The sections that have been incorporated have been reviewed following the <a href="https://github.com/solid/process">Solid process</a>. However, the information in this document is still subject to change. You are invited to <a href="https://github.com/solid/solid-oidc/issues">contribute</a> any feedback, comments, or questions you might have.</p>
<p>Publication as <em>Version 0.1.0</em> does not imply endorsement by the <abbr title="World Wide Web Consortium">W3C</abbr> Membership. This document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.</p>
<p>This document was produced by a group operating under the <a href="https://www.w3.org/community/about/agreements/cla/">W3C Community Contributor License Agreement (CLA)</a>. A human-readable <a href="https://www.w3.org/community/about/agreements/cla-deed/">summary</a> is available.</p>
</div>
<div data-fill-with="at-risk"></div>
<nav data-fill-with="table-of-contents" id="toc">
<h2 class="no-num no-toc no-ref" id="contents">Table of Contents</h2>
<ol class="toc" role="directory">
<li><a href="#intro"><span class="secno">1</span> <span class="content">Introduction</span></a>
<li><a href="#definitions"><span class="secno">2</span> <span class="content">Definitions</span></a>
<li><a href="#actors"><span class="secno">3</span> <span class="content">Actors</span></a>
<li>
<a href="#solid-oidc-flow"><span class="secno">4</span> <span class="content">Solid OIDC Flow</span></a>
<ol class="toc">
<li><a href="#authorization-code-pkce-flow"><span class="secno">4.1</span> <span class="content">Authorization Code Grant with PKCE Authorization Flow</span></a>
<li><a href="#request-flow"><span class="secno">4.2</span> <span class="content">Request Flow</span></a>
</ol>
<li>
<a href="#w3c-conformance"><span class="secno"></span> <span class="content">Conformance</span></a>
<ol class="toc">
<li><a href="#w3c-conventions"><span class="secno"></span> <span class="content">Document conventions</span></a>
<li><a href="#w3c-conformant-algorithms"><span class="secno"></span> <span class="content">Conformant Algorithms</span></a>
</ol>
<li>
<a href="#references"><span class="secno"></span> <span class="content">References</span></a>
<ol class="toc">
<li><a href="#normative"><span class="secno"></span> <span class="content">Normative References</span></a>
</ol>
</ol>
</nav>
<main>
<h2 class="heading settled" data-level="1" id="intro"><span class="secno">1. </span><span class="content">Introduction</span><a class="self-link" href="#intro"></a></h2>
<p>This document outlines in details how Alice (end-user) asserts her identity
(logs in) when using Decent Photos (relying party) to access data in hers and
Bob’s Solid Storage (resource servers).</p>
<h2 class="heading settled" data-level="2" id="definitions"><span class="secno">2. </span><span class="content">Definitions</span><a class="self-link" href="#definitions"></a></h2>
<dl>
<dt id="openid-provider"><a class="self-link" href="#openid-provider"></a>OpenID Provider (OP)
<dd>An OAuth 2.0 authorization server implementing OpenID Connect as <a href="https://openid.net/specs/openid-connect-core-1_0.html">defined in the OpenID Connect Core 1.0 specification</a>. <a data-link-type="biblio" href="#biblio-oidccore">[OIDC.Core]</a>
<dt id="relying-party"><a class="self-link" href="#relying-party"></a>Relying Party (RP)
<dd>A client application using OpenID Connect to make resource requests on behalf of the resource owner. Client is one of the four roles <a href="https://tools.ietf.org/html/rfc6749#section-1.1">defined in the OAuth 2.0 specification</a>. <a data-link-type="biblio" href="#biblio-rfc6749">[RFC6749]</a>
<dt id="resource"><a class="self-link" href="#resource"></a>Resource
<dd>Something denoted by an IRI or a literal as <a href="https://www.w3.org/TR/rdf11-concepts/#resources-and-statements">defined in RDF 1.1</a>. <a data-link-type="biblio" href="#biblio-rdf11-concepts">[rdf11-concepts]</a>
<dt id="resource-owner"><a class="self-link" href="#resource-owner"></a>Resource Owner
<dd>An entity capable of granting access to a protected resource. When the resource owner is a person, it is referred to as an end-user. Resource Owner is one of the four roles <a href="https://tools.ietf.org/html/rfc6749#section-1.1">defined in the OAuth 2.0 specification</a>. <a data-link-type="biblio" href="#biblio-rfc6749">[RFC6749]</a>
<dt id="resource-server"><a class="self-link" href="#resource-server"></a>Resource Server (RS)
<dd>A server hosting resources, capable of accepting and responding to protected resource requests using access tokens. RS is one of the four roles <a href="https://tools.ietf.org/html/rfc6749#section-1.1">defined in the OAuth 2.0 specification</a>. <a data-link-type="biblio" href="#biblio-rfc6749">[RFC6749]</a>
<dt id="solid-data-pod"><a class="self-link" href="#solid-data-pod"></a>Solid Storage
<dd>A Solid compliant Resource Server as <a href="https://solidproject.org/TR/protocol#data-pod">defined in the Solid Protocol</a>. <a data-link-type="biblio" href="#biblio-solidprotocol">[Solid.Protocol]</a>
<dt id="solid-oidc"><a class="self-link" href="#solid-oidc"></a>Solid OpenID Connect (Solid OIDC)
<dd>The specification defining authentication in the Solid ecosystem. <a data-link-type="biblio" href="#biblio-solidoidc">[Solid.OIDC]</a>
<dt id="webid"><a class="self-link" href="#webid"></a>WebID
<dd>A WebID is an HTTP URI which refers to an Agent (Person, Organization, Group, Device, etc.) as <a href="https://dvcs.w3.org/hg/WebID/raw-file/tip/spec/identity-respec.html#introduction">defined in the WebID 1.0 specification</a>. <a data-link-type="biblio" href="#biblio-webid">[WebID]</a>
</dl>
<h2 class="heading settled" data-level="3" id="actors"><span class="secno">3. </span><span class="content">Actors</span><a class="self-link" href="#actors"></a></h2>
<p>Several actors are at play in our example Solid OIDC authentication flows:</p>
<dl>
<dt id="alice"><a class="self-link" href="#alice"></a>Alice
<dd>Alice will be providing consent for Decent Photos to make resource requests
on her behalf. Let’s assume that Alice is using a standard web browser.
<dt id="alice-op"><a class="self-link" href="#alice-op"></a>Alice’s OP
<dd>Alice’s OpenID Provider, also known as an Identity Provider (IdP), is the
service responsible for authorizing our third-party web app (Decent Photos) by
providing it with the tokens necessary to gain access to any resource Alice has
access to (in any Storage, for example Alice’s and Bob’s). In our example, Alice’s
OP is hosted at <code>https://secureauth.example</code>.
<dt id="alice-webid"><a class="self-link" href="#alice-webid"></a>Alice’s WebID
<dd>Alice’s WebID is <code>https://alice.coolpod.example/profile/card#me</code>. The WebID
profile document denoted by URI <code>https://alice.coolpod.example/profile/card</code> is
hosted on Alice’s Solid Storage and contains the URI of her OP. Alice’s WebID <code>https://alice.coolpod.example/profile/card#me</code> serves as her unique identifier
in the Solid Ecosystem.
<dt id="rp"><a class="self-link" href="#rp"></a>RP
<dd>The Decent Photos application is a third party photo viewing web application.
It is OIDC compliant and will therefore be referred to as the Relying Party. The
Decent Photos web app allows its users to view photos they have access to. For
example, Alice’s and her friend Bob’s photos. In our example, Decent Photos is
hosted at <code>https://decentphotos.example</code>.
<dt id="decentphotos-rp-webid"><a class="self-link" href="#decentphotos-rp-webid"></a>RP’s Client ID Document
<dd>Decent Photos is a Solid compliant application and has a URI of its own,
which resolves to a Client ID Document. An RP’s Client ID Document
contains information identifying them as a registered OAuth 2.0 client
application. Decent Photo’s URI is <code>https://decentphotos.example/webid#this</code>
<dt id="bob-pod"><a class="self-link" href="#bob-pod"></a>Bob’s Storage
<dd>We will be trying to access photos stored in Bob’s Storage. In our example, Bob
is a friend of Alice and has previously indicated via access control that Alice
may access some of his photos using any web app. Bob’s Solid Storage is hosted at <code>https://bob.otherpod.example</code>.
<dt id="bob-auth-server"><a class="self-link" href="#bob-auth-server"></a>Authorization Server for Bob’s Storage
<dd>The Authorization Server (AS) connected to Bob’s Storage will issue access tokens that
can be used with Bob’s Storage. In these examples, this is hosted at <code>https://auth.otherpod.example</code>.
</dl>
<h2 class="heading settled" data-level="4" id="solid-oidc-flow"><span class="secno">4. </span><span class="content">Solid OIDC Flow</span><a class="self-link" href="#solid-oidc-flow"></a></h2>
<h3 class="heading settled" data-level="4.1" id="authorization-code-pkce-flow"><span class="secno">4.1. </span><span class="content">Authorization Code Grant with PKCE Authorization Flow</span><a class="self-link" href="#authorization-code-pkce-flow"></a></h3>
<p>The Authorization Code grant with PKCE is the primary OAuth 2.0 authorization flow
recommended by the Solid OIDC. It is defined by the PKCE RFC <a data-link-type="biblio" href="#biblio-rfc7636">[RFC7636]</a> and
described here in the Solid OIDC context.</p>
<p><img src="oidc-primer-login.svg" width="980"></p>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-1"><span class="content">1. Alice uses the Decent Photos web app</span><a class="self-link" href="#authorization-code-pkce-flow-step-1"></a></h4>
<p>Alice has heard of a great new site that allows her to view her friend’s photos and tag faces.
She navigates to <code>https://decentphotos.example</code> via her web browser which returns an HTML page.
This page contains JavaScript that will help with the authentication process.</p>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-2"><span class="content">2. Alice selects her OP or WebID</span><a class="self-link" href="#authorization-code-pkce-flow-step-2"></a></h4>
<p>Before decentphotos can start displaying images, Alice needs to start the
process of providing consent. To do so, she must either provide her WebID
(<code>https://alice.coolpod.example/profile/card#me</code>) or the URL of her OP
(<code>https://secureauth.example</code>).</p>
<p>Although it is not the case for Alice, a user’s Storage and OP can be hosted under the
same domain. For example, Bob could have a Storage at <code>https://bob.solidpod.example.com</code>,
an OP at <code>https://solidpod.example.com/</code>, and a WebID of <code>https://bob.solidpod.example.com/profile/card#me</code>.</p>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-2.1"><span class="content">2.1 Retrieve Profile (only needed if a WebID is provided)</span><a class="self-link" href="#authorization-code-pkce-flow-step-2.1"></a></h4>
<p>If Alice enters her WebID’s URL rather than her OP’s URL, a request should be
made to determine her OP. To do so, a request should be made to Alice’s WebId:</p>
<pre class="language-http highlight">GET https://alice.coolpod.example/profile/card#me
</pre>
<p>It will return a body similar to this:</p>
<pre class="language-ttl highlight">@<c- g>prefix</c-> : <c- o><</c->#<c- o>></c->.
@<c- g>prefix</c-> <c- g>solid</c->: <c- o><</c-><c- g>http</c->:<c- o>//</c-><c- g>www</c->.<c- g>w3</c->.<c- g>org</c-><c- o>/</c-><c- g>ns</c-><c- o>/</c-><c- g>solid</c-><c- o>/</c-><c- g>terms</c->#<c- o>></c->.
@<c- g>prefix</c-> <c- g>foaf</c->: <c- o><</c-><c- g>http</c->:<c- o>//</c-><c- g>xmlns</c->.<c- g>com</c-><c- o>/</c-><c- g>foaf</c-><c- o>/</c-><c- mi>0</c->.<c- mi>1</c-><c- o>/></c->.
@<c- g>prefix</c-> <c- g>schema</c->: <c- o><</c-><c- g>http</c->:<c- o>//</c-><c- g>schema</c->.<c- g>org</c-><c- o>/></c->.
<c- o><></c->
<c- g>a</c-> <c- g>foaf</c->:<c- g>PersonalProfileDocument</c-> <c- c1>;</c->
<c- g>foaf</c->:<c- g>maker</c-> <c- o><</c-><c- g>https</c->:<c- o>//</c-><c- g>localhost</c->:<c- mi>8443</c-><c- o>/</c-><c- g>profile</c-><c- o>/</c-><c- g>card</c->#<c- g>me</c-><c- o>></c-> <c- c1>;</c->
<c- g>foaf</c->:<c- g>primaryTopic</c-> <c- o><</c-><c- g>https</c->:<c- o>//</c-><c- g>localhost</c->:<c- mi>8443</c-><c- o>/</c-><c- g>profile</c-><c- o>/</c-><c- g>card</c->#<c- g>me</c-><c- o>></c-> .
<c- nl>:me</c-> <c- g>a</c-> <c- g>foaf</c->:<c- g>Person</c-> <c- c1>;</c->
<c- g>a</c-> <c- g>schema</c->:<c- g>Person</c-> <c- c1>;</c->
<c- g>foaf</c->:<c- g>name</c-> <c- u>"</c-><c- s>Alice</c-><c- u>"</c-> <c- c1>;</c->
<c- g>solid</c->:<c- g>oidcIssuer</c-> <c- o><</c-><c- g>https</c->:<c- o>//</c-><c- g>secureauth</c->.<c- g>example</c-><c- o>></c-> <c- c1>;</c->
</pre>
<p>The OP URL is located at <code>:me -> solid:oidcIssuer</code></p>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-3"><span class="content">3. Retrieves OP Configuration</span><a class="self-link" href="#authorization-code-pkce-flow-step-3"></a></h4>
<p>Now that we have Alice’s OP’s URL, the RP must make a request to retrieve the
OP’s configuration. This is always located at the OP’s issuer URL followed by <code>/.well-known/openid-configuration</code>.</p>
<pre class="language-http highlight">GET https://secureauth.example/.well-known/openid-configuration
</pre>
<p>The <a href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata">openid-configuration</a> describes everything the client will need to know to authorize with Alice’s specific OP.</p>
<p>Response Body:</p>
<pre class="language-json highlight"><c- p>{</c->
<c- f>"issuer"</c-><c- p>:</c-> <c- u>"https://secureauth.example"</c-><c- p>,</c->
<c- f>"authorization_endpoint"</c-><c- p>:</c-> <c- u>"https://secureauth.example/authorize"</c-><c- p>,</c->
<c- f>"token_endpoint"</c-><c- p>:</c-> <c- u>"https://secureauth.example/token"</c-><c- p>,</c->
<c- f>"userinfo_endpoint"</c-><c- p>:</c-> <c- u>"https://secureauth.example/userinfo"</c-><c- p>,</c->
<c- f>"registration_endpoint"</c-><c- p>:</c-> <c- u>"https://secureauth.example/register"</c-><c- p>,</c->
<c- f>"end_session_endpoint"</c-><c- p>:</c-> <c- u>"https://secureauth.example/endsession"</c-><c- p>,</c->
<c- f>"jwks_uri"</c-><c- p>:</c-> <c- u>"https://secureauth.example/jwks"</c-><c- p>,</c->
<c- f>"response_types_supported"</c-><c- p>:</c-> <c- p>[</c->
<c- u>"code"</c->
<c- p>],</c->
<c- f>"grant_types_supported"</c-><c- p>:</c-> <c- p>[</c->
<c- u>"authorization_code"</c-><c- p>,</c->
<c- u>"refresh_token"</c->
<c- p>],</c->
<c- f>"subject_types_supported"</c-><c- p>:</c-> <c- p>[</c->
<c- u>"public"</c->
<c- p>],</c->
<c- f>"claims_supported"</c-><c- p>:</c-> <c- p>[</c->
<c- u>"sub"</c-><c- p>,</c->
<c- u>"webid"</c->
<c- p>],</c->
<c- f>"scopes_supported"</c-><c- p>:</c-> <c- p>[</c->
<c- u>"openid"</c-><c- p>,</c->
<c- u>"webid"</c-><c- p>,</c->
<c- u>"profile"</c-><c- p>,</c->
<c- u>"email"</c-><c- p>,</c->
<c- u>"offline_access"</c->
<c- p>],</c->
<c- f>"token_endpoint_auth_methods_supported"</c-><c- p>:</c-> <c- p>[</c->
<c- u>"client_secret_basic"</c->
<c- p>],</c->
<c- f>"token_endpoint_auth_signing_alg_values_supported"</c-><c- p>:</c-> <c- p>[</c->
<c- u>"ES256"</c->
<c- p>],</c->
<c- f>"request_object_signing_alg_values_supported"</c-><c- p>:</c-> <c- p>[</c->
<c- u>"ES256"</c->
<c- p>],</c->
<c- f>"id_token_signing_alg_values_supported"</c-><c- p>:</c-> <c- p>[</c->
<c- u>"ES256"</c->
<c- p>],</c->
<c- f>"code_challenge_methods_supported"</c-><c- p>:</c-> <c- p>[</c->
<c- u>"plain"</c-><c- p>,</c->
<c- u>"S256"</c->
<c- p>],</c->
<c- f>"claims_parameter_supported"</c-><c- p>:</c-> <c- kc>false</c-><c- p>,</c->
<c- f>"request_parameter_supported"</c-><c- p>:</c-> <c- kc>true</c-><c- p>,</c->
<c- f>"request_uri_parameter_supported"</c-><c- p>:</c-> <c- kc>false</c-><c- p>,</c->
<c- f>"require_request_uri_registration"</c-><c- p>:</c-> <c- kc>false</c->
<c- p>}</c->
</pre>
<p>The thing we care about here is the <code>authorization_endpoint</code> field. This will
be the url we use when we’re ready to send an authorization request to the OP.</p>
<p>Note that we only support the <code>code</code> response type. The OIDC discovery
specification states that Dynamic OpenID Providers MUST also support <code>id_token</code> and <code>token id_token</code>. However, <a href="https://oauth.net/2/grant-types/implicit/">implicit flows should not be used for
security reasons</a>. Therefore, we
don’t recommend enabling them. For the same reason, we do not support the <code>implicit</code> grant type.</p>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-4"><span class="content">4. Generates PKCE code challenge and code verifier</span><a class="self-link" href="#authorization-code-pkce-flow-step-4"></a></h4>
<p>To follow the <a href="https://oauth.net/2/pkce/">PKCE code flow</a> we need to generate a
code challenge and code verifier as instructed in the <a href="https://tools.ietf.org/html/rfc7636#section-4.1">Proof Key for Code
Exchange spec</a>.</p>
<p>We start by generating a code verifier. The can be done by creating a
cryptographically random string. Let’s say ours looks like this: <code>"JXPOuToEB7"</code>.</p>
<p>Now using the code verifier, we construct a code challenge. This can be made by transforming
the code verifier with Sha 256: <code>BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))</code>. Here’s our
code challenge: <code>"HSi9dwlvRpNHCDm-L8GOdM16qcb0tLHPZqQSvaWXTI0"</code></p>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-5"><span class="content">5. Saved code verifier to session storage</span><a class="self-link" href="#authorization-code-pkce-flow-step-5"></a></h4>
<p>Now, we save the code verifier (<code>"JXPOuToEB7"</code>) to session storage. We’ll need it later to
confirm to the OP that this is the app that initiated the request. Do not save the code
challenge anywhere.</p>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-6"><span class="content">6. Authorization request</span><a class="self-link" href="#authorization-code-pkce-flow-step-6"></a></h4>
<p>Now that the web app is registered, we can finally make an auth request to authorize the web
application.</p>
<pre class="language-http highlight">GET https://secureauth.example/authorize?response_type=code&
redirect_uri=https%3A%2F%2Fdecentphotos.example%2Fcallback&
scope=openid%20webid%20offline_access&
client_id=https%3A%2F%2Fdecentphotos.example%2Fwebid%23this&
code_challenge_method=S256&
code_challenge=HSi9dwlvRpNHCDm-L8GOdM16qcb0tLHPZqQSvaWXTI0
</pre>
<p>That URL might look a little complex, but it’s essentially a request to <code>https://secureauth.example/authorize</code> with the following URL parameters:</p>
<ul>
<li data-md>
<p><code>response_type=code</code> indicates the desired response data.</p>
<li data-md>
<p><code>redirect_uri=https%3A%2F%2Fdecentphotos.example%2Fcallback</code>:
The url that the OP will redirect to once the user has logged in (<code>https://decentphotos.example/callback</code>).</p>
<li data-md>
<p><code>scope=openid%20webid%20offline_access</code>: a list of <a href="https://auth0.com/docs/scopes/current/oidc-scopes">OIDC scopes</a> (attributes of the RS to which this token should have access) separated by spaces (%20).</p>
<ul>
<li data-md>
<p><code>openid</code> is a scope that is needed to verify Alice’s identity.</p>
<li data-md>
<p><code>webid</code> is required by the Solid OIDC specification to denote a WebID login.</p>
<li data-md>
<p><code>offline_access</code>: Is required to get a refresh token.</p>
</ul>
<li data-md>
<p><code>client_id=https%3A%2F%2Fdecentphotos.example%2Fwebid%23this</code>: Usually the client id of a Solid
application is the app’s URI (in our case <code>https://decentphotos.example/webid#this</code>) as seen here.</p>
<li data-md>
<p><code>code_challenge_method=S256</code>: Tells the OP that our code challenge was transformed using SHA-256.</p>
<li data-md>
<p><code>code_challenge=HSi9dwlvRpNHCDm-L8GOdM16qcb0tLHPZqQSvaWXTI0</code>: The code challenge we generated before.</p>
</ul>
<p class="note" role="note"><span>Note:</span> If the app doesn’t have a URI, you can either register an app using static registration
via some UI on the OP or use <a href="https://openid.net/specs/openid-connect-registration-1_0.html">dynamic registration</a>.</p>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-7"><span class="content">7. Fetch RP Client ID Document</span><a class="self-link" href="#authorization-code-pkce-flow-step-7"></a></h4>
<p>If an app URI is provided as the client id (see note above to see other options), we must
fetch that app URI to confirm its validity.</p>
<p>For the URI <code>https://decentphotos.example/webid#this</code>, request the Client ID Document with:</p>
<pre class="language-http highlight">GET https://decentphotos.example/webid
</pre>
<p>Response:</p>
<pre class="language-jsonld highlight"><c- p>{</c->
<c- nd>"@context"</c-><c- p>:</c-> <c- p>[</c-> <c- u>"https://www.w3.org/ns/solid/oidc-context.jsonld"</c-> <c- p>],</c->
<c- f>"client_id"</c-><c- p>:</c-> <c- u>"https://decentphtos.example/webid#this"</c-><c- p>,</c->
<c- f>"client_name"</c-><c- p>:</c-> <c- u>"DecentPhotos"</c-><c- p>,</c->
<c- f>"redirect_uris"</c-><c- p>:</c-> <c- p>[</c-> <c- u>"https://decentphotos.example/callback"</c-> <c- p>],</c->
<c- f>"post_logout_redirect_uris"</c-><c- p>:</c-> <c- p>[</c-> <c- u>"https://decentphotos.example/logout"</c-> <c- p>],</c->
<c- f>"client_uri"</c-><c- p>:</c-> <c- u>"https://decentphotos.example/"</c-><c- p>,</c->
<c- f>"logo_uri"</c-><c- p>:</c-> <c- u>"https://decentphotos.example/logo.png"</c-><c- p>,</c->
<c- f>"tos_uri"</c-><c- p>:</c-> <c- u>"https://decentphotos.example/tos.html"</c-><c- p>,</c->
<c- f>"scope"</c-><c- p>:</c-> <c- u>"openid webid offline_access"</c-><c- p>,</c->
<c- f>"grant_types"</c-><c- p>:</c-> <c- p>[</c-> <c- u>"refresh_token"</c-><c- p>,</c-> <c- u>"authorization_code"</c-> <c- p>],</c->
<c- f>"response_types"</c-><c- p>:</c-> <c- p>[</c-> <c- u>"code"</c-> <c- p>],</c->
<c- f>"default_max_age"</c-><c- p>:</c-> <c- mi>3600</c-><c- p>,</c->
<c- f>"require_auth_time"</c-><c- p>:</c-> <c- kc>true</c->
<c- p>}</c->
</pre>
<p>Notice that the application Client ID Document contains a JSON-LD representation of an <a href="https://tools.ietf.org/html/rfc7591#section-2">OIDC Client Registration</a>.
It also must use the specific <code>"@context": ["https://www.w3.org/ns/solid/oidc-context.jsonld"]</code>.</p>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-8"><span class="content">8. Validate redirect url with Client ID Document</span><a class="self-link" href="#authorization-code-pkce-flow-step-8"></a></h4>
<p>Check to be sure that the <code>redirect_uri</code> value provided in the auth request
(<code>https://decentphotos.example/callback</code>) is listed in the <code>redirect_uris</code> array in the
Client ID Document. If it is not, the OP must reject the request. In our case, the <code>redirect_uri</code> is valid, so we may continue.</p>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-9"><span class="content">9. Alice Logs In</span><a class="self-link" href="#authorization-code-pkce-flow-step-9"></a></h4>
<p>The OP should redirect to its login screen. The actual implementation of this is completely up
to the OP. A user can log in with her password, a TLS certificate, or any other proven method
of authentication. The important thing is that, thanks to the redirect, the control is now out
of the hands of the RP and is in complete control of the OP.</p>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-10"><span class="content">10. Generate a code</span><a class="self-link" href="#authorization-code-pkce-flow-step-10"></a></h4>
<p>Generate a cryptographically random string that will be used as a code (Let’s say ours is <code>m-OrTPHdRsm8W_e9P0J2Bt</code>). Store that string in a persistant keystore as the key for the client
id, the code challenge, the user’s webid, their desired response types, and their scopes:</p>
<pre class="language-json highlight"><c- p>{</c->
<c- f>"m-OrTPHdRsm8W_e9P0J2Bt"</c-><c- p>:</c-> <c- p>{</c->
<c- f>"client_id"</c-><c- p>:</c-> <c- u>"https://decentphotos.example/webid#this"</c-><c- p>,</c->
<c- f>"code_challenge"</c-><c- p>:</c-> <c- u>"HSi9dwlvRpNHCDm-L8GOdM16qcb0tLHPZqQSvaWXTI0"</c-><c- p>,</c->
<c- f>"webid"</c-><c- p>:</c-> <c- u>"https://alice.coolpod.example/profile/card#me"</c-><c- p>,</c->
<c- f>"response_types"</c-><c- p>:</c-> <c- p>[</c-> <c- u>"code"</c-> <c- p>],</c->
<c- f>"scope"</c-><c- p>:</c-> <c- p>[</c-> <c- u>"openid"</c-><c- p>,</c-> <c- u>"webid"</c-><c- p>,</c-> <c- u>"offline_access"</c-> <c- p>]</c->
<c- p>}</c->
<c- p>}</c->
</pre>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-11"><span class="content">11. Send code to redirect url</span><a class="self-link" href="#authorization-code-pkce-flow-step-11"></a></h4>
<p>Once Alice successfully logs in, the OP redirects back to the application via the provided
redirect uri, including useful information with it:</p>
<pre class="language-http highlight">302 redirect to: https://decentphotos.example/callback?code=m-OrTPHdRsm8W_e9P0J2Bt
</pre>
<p>This redirect gives decentphotos the code that it will exchange for an access token.</p>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-12"><span class="content">12. Generates a DPoP Client Key Pair</span><a class="self-link" href="#authorization-code-pkce-flow-step-12"></a></h4>
<p>Solid-OIDC depends on <a href="https://tools.ietf.org/html/draft-ietf-oauth-dpop">Demonstration of Proof-of-Possession (DPoP) tokens</a>.
DPoP tokens ensure that third-party web applications can send requests to any number of
Storage servers while ensuring that malicious actors can’t steal and replay a user’s token.</p>
<p>The first step to generating a DPoP token is generating a public and private key pair on the
third-party RP. In our example, the private key is generated using elliptic curves and looks
like:</p>
<pre class="language-json highlight"><c- p>{</c->
<c- f>"kty"</c-><c- p>:</c-> <c- u>"EC"</c-><c- p>,</c->
<c- f>"kid"</c-><c- p>:</c-> <c- u>"2i00gHnREsMhD5WqsABPSaqEjLC5MS-E98ykd-qtF1I"</c-><c- p>,</c->
<c- f>"use"</c-><c- p>:</c-> <c- u>"sig"</c-><c- p>,</c->
<c- f>"alg"</c-><c- p>:</c-> <c- u>"EC"</c-><c- p>,</c->
<c- f>"crv"</c-><c- p>:</c-> <c- u>"P-256"</c-><c- p>,</c->
<c- f>"x"</c-><c- p>:</c-> <c- u>"N6VsICiPA1ciAA82Jhv7ykkPL9B0ippUjmla8Snr4HY"</c-><c- p>,</c->
<c- f>"y"</c-><c- p>:</c-> <c- u>"ay9qDOrFGdGe_3hAivW5HnqHYdnYUkXJJevHOBU4z5s"</c-><c- p>,</c->
<c- f>"d"</c-><c- p>:</c-> <c- u>"RrM4Ou_7PzjP24B4k06B9ZML16HbfzNPKFN11Z8c9_s"</c->
<c- p>}</c->
</pre>
<p>From now on we will refer to this as <code>RP_PRIVATE_KEY</code>.</p>
<p>The public key looks like:</p>
<pre class="language-json highlight"><c- p>{</c->
<c- f>"kty"</c-><c- p>:</c-> <c- u>"EC"</c-><c- p>,</c->
<c- f>"kid"</c-><c- p>:</c-> <c- u>"2i00gHnREsMhD5WqsABPSaqEjLC5MS-E98ykd-qtF1I"</c-><c- p>,</c->
<c- f>"use"</c-><c- p>:</c-> <c- u>"sig"</c-><c- p>,</c->
<c- f>"alg"</c-><c- p>:</c-> <c- u>"EC"</c-><c- p>,</c->
<c- f>"crv"</c-><c- p>:</c-> <c- u>"P-256"</c-><c- p>,</c->
<c- f>"x"</c-><c- p>:</c-> <c- u>"N6VsICiPA1ciAA82Jhv7ykkPL9B0ippUjmla8Snr4HY"</c-><c- p>,</c->
<c- f>"y"</c-><c- p>:</c-> <c- u>"ay9qDOrFGdGe_3hAivW5HnqHYdnYUkXJJevHOBU4z5s"</c->
<c- p>}</c->
</pre>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-13"><span class="content">13. Generates a DPoP Header</span><a class="self-link" href="#authorization-code-pkce-flow-step-13"></a></h4>
<p>Now that we generated a private key for the client, we need to generate the DPoP header. To do
so, we create a <a href="https://jwt.io/introduction/">JSON Web Token</a> and sign it using the key we
generated.</p>
<p>Our token could look like the following (you can decode the token using https://jwt.io):</p>
<pre class="language-text highlight">eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Arand0IiwiandrIjp7Imt0eSI6IkVDIiwia2lkIjoiZkJ1STExTkdGbTQ4Vlp6RzNGMjVDOVJmMXYtaGdEakVnV2pEQ1BrdV9pVSIsInVzZSI6InNpZyIsImFsZyI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiOWxlT2gxeF9IWkhzVkNScDcyQzVpR01jek1nUnpDUFBjNjBoWldfSFlLMCIsInkiOiJqOVVYcnRjUzRLVzBIYmVteW1vRWlMXzZ1cko0TFFHZXJQZXVNaFNEaV80In19.eyJodHUiOiJodHRwczovL3NlY3VyZWF1dGguZXhhbXBsZS90b2tlbiIsImh0bSI6InBvc3QiLCJqdGkiOiI0YmEzZTllZi1lOThkLTQ2NDQtOTg3OC03MTYwZmE3ZDNlYjgiLCJpYXQiOjE2MDMzMDYxMjgsImV4cCI6MTYwMzMwOTcyOH0.2lbgLoRCkj0MsDc9BpquoaYuq0-XwRf_URdXru2JKrVzaWUqQfyKRK76_sQ0aJyVwavM3pPswLlHq2r9032O7Q
</pre>
<p>Token Header:</p>
<pre class="language-json highlight"><c- p>{</c->
<c- f>"alg"</c-><c- p>:</c-> <c- u>"ES256"</c-><c- p>,</c->
<c- f>"typ"</c-><c- p>:</c-> <c- u>"dpop+jwt"</c-><c- p>,</c->
<c- f>"jwk"</c-><c- p>:</c-> <c- p>{</c->
<c- f>"kty"</c-><c- p>:</c-> <c- u>"EC"</c-><c- p>,</c->
<c- f>"kid"</c-><c- p>:</c-> <c- u>"2i00gHnREsMhD5WqsABPSaqEjLC5MS-E98ykd-qtF1I"</c-><c- p>,</c->
<c- f>"use"</c-><c- p>:</c-> <c- u>"sig"</c-><c- p>,</c->
<c- f>"alg"</c-><c- p>:</c-> <c- u>"EC"</c-><c- p>,</c->
<c- f>"crv"</c-><c- p>:</c-> <c- u>"P-256"</c-><c- p>,</c->
<c- f>"x"</c-><c- p>:</c-> <c- u>"N6VsICiPA1ciAA82Jhv7ykkPL9B0ippUjmla8Snr4HY"</c-><c- p>,</c->
<c- f>"y"</c-><c- p>:</c-> <c- u>"ay9qDOrFGdGe_3hAivW5HnqHYdnYUkXJJevHOBU4z5s"</c->
<c- p>}</c->
<c- p>}</c->
</pre>
<ul>
<li data-md>
<p><code>"alg": "ES256"</code>: The signing algorithm used in this token. In this case, <code>ES256</code> because we generated the keys using eliptic curves.</p>
<li data-md>
<p><code>"typ": "dpop+jwt"</code>: The type of token. All DPoP Tokens should have a type of "dpop+jwt"</p>
<li data-md>
<p><code>"jwk": { "kty": "EC" ... }</code>: The client’s public key.</p>
</ul>
<p>Token Body:</p>
<pre class="language-json highlight"><c- p>{</c->
<c- f>"htu"</c-><c- p>:</c-> <c- u>"https://secureauth.example/token"</c-><c- p>,</c->
<c- f>"htm"</c-><c- p>:</c-> <c- u>"POST"</c-><c- p>,</c->
<c- f>"jti"</c-><c- p>:</c-> <c- u>"4ba3e9ef-e98d-4644-9878-7160fa7d3eb8"</c-><c- p>,</c->
<c- f>"iat"</c-><c- p>:</c-> <c- mi>1603306128</c->
<c- p>}</c->
</pre>
<ul>
<li data-md>
<p><code>"htu": "https://secureauth.example/token"</code>: htu limits the token for use only on the given url.</p>
<li data-md>
<p><code>"htm": "POST"</code>: htm limits the token for use only on a specific http method, in this case <code>POST</code>.</p>
<li data-md>
<p><code>"jti": "4ba3e9ef-e98d-4644-9878-7160fa7d3eb8"</code>: jti is a unique identifier for the DPoP token
that can optionally be used by the server to defend against replay attacks</p>
<li data-md>
<p><code>"iat": 1603306128</code>: The date the token was issued, in this case October 21, 2020 15:52:33 GMT.</p>
</ul>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-14"><span class="content">14. Token request with code and code verifier</span><a class="self-link" href="#authorization-code-pkce-flow-step-14"></a></h4>
<p>Now, we have everything we need to make an auth request. No need to redirect the web browser
for this one. We only need to make an AJAX request to the <code>token</code> endpoint as defined in the
OP’s <code>openid-configuration</code> file, in our case <code>https://secureauth.example/token</code></p>
<pre class="language-http highlight">POST https://secureauth.example/token
Headers: {
"DPoP": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Arand0IiwiandrIjp7Imt0eSI6IkVDIiwia2lkIjoiZkJ1STExTkdGbTQ4Vlp6RzNGMjVDOVJmMXYtaGdEakVnV2pEQ1BrdV9pVSIsInVzZSI6InNpZyIsImFsZyI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiOWxlT2gxeF9IWkhzVkNScDcyQzVpR01jek1nUnpDUFBjNjBoWldfSFlLMCIsInkiOiJqOVVYcnRjUzRLVzBIYmVteW1vRWlMXzZ1cko0TFFHZXJQZXVNaFNEaV80In19.eyJodHUiOiJodHRwczovL3NlY3VyZWF1dGguZXhhbXBsZS90b2tlbiIsImh0bSI6InBvc3QiLCJqdGkiOiI0YmEzZTllZi1lOThkLTQ2NDQtOTg3OC03MTYwZmE3ZDNlYjgiLCJpYXQiOjE2MDMzMDYxMjgsImV4cCI6MTYwMzMwOTcyOH0.2lbgLoRCkj0MsDc9BpquoaYuq0-XwRf_URdXru2JKrVzaWUqQfyKRK76_sQ0aJyVwavM3pPswLlHq2r9032O7Q",
"content-type": "application/x-www-form-urlencoded"
}
Body:
grant_type=authorization_code&
code_verifier=JXPOuToEB7&
code=m-OrTPHdRsm8W_e9P0J2Bt&
redirect_uri=https%3A%2F%2Fdecentphotos.example%2Fcallback&
client_id=https%3A%2F%2Fdecentphotos.example%2Fwebid%23this
</pre>
<ul>
<li data-md>
<p><code>headers.DPoP: "eyJhbGciOiJFUz..."</code>: The DPoP token we generated before. This will tell the
OP what the client’s public key is.</p>
<li data-md>
<p><code>headers.content-type: "application/x-www-form-urlencoded"</code>: Sets to body’s content type to <code>application/x-www-form-urlencoded</code>. Some OPs will accept other content types like <code>application/json</code> but they all must access urlencoded content types, so it’s safest to use
that.</p>
<li data-md>
<p><code>body.grant_type=authorization_code</code>: Tells the OP that we are doing the authorization code
flow.</p>
<li data-md>
<p><code>body.code_verifier=JXPOuToEB7</code>: Our code verifier that we stored in session storage</p>
<li data-md>
<p><code>body.code=m-OrTPHdRsm8W_e9P0J2Bt</code>: The code that we received from the OP upon redirect</p>
<li data-md>
<p><code>body.redirect_uri</code>: The app’s redirect url. In this case, this isn’t needed because we’re
doing an AJAX request.</p>
<li data-md>
<p><code>body.client_id=https%3A%2F%2Fdecentphotos.example%2Fwebid%23this</code>: The app’s client id.</p>
</ul>
<p>Once this request is completed decentphotos can remove the code verifier from session storage.</p>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-15"><span class="content">15. Validate code verifier</span><a class="self-link" href="#authorization-code-pkce-flow-step-15"></a></h4>
<p>The OP looks up the code that was saved earlier in a keystore. It checks to see that the client
id in the keystore corresponds to the client id from the request. If it does not, it must
reject the request with a 400 HTTP status and <code>invalid_grant</code> error code.</p>
<p>The OP then verifies that the code verifier [corresponds with the code challenge]
(https://tools.ietf.org/html/rfc7636#section-4.6) stored in the keystore.</p>
<pre class="language-bash highlight">BASE64URL-ENCODE<c- o>(</c->SHA256<c- o>(</c->ASCII<c- o>(</c->code_verifier<c- o>)))</c-> <c- o>==</c-> code_challenge
</pre>
<p>If they do not correspond the OP must reject the request with a 400 HTTP status and <code>invalid_grant</code> error code.</p>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-16"><span class="content">16. Validates DPoP Token Signature</span><a class="self-link" href="#authorization-code-pkce-flow-step-16"></a></h4>
<p>The OP extracts the client’s public key from the DPoP header (at header.jwk). It confirms that
the DPoP token has a valid signature. If not, the OP must reject the request with a 400 HTTP
status and <code>invalid_dpop_proof</code> error code.</p>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-17"><span class="content">17. Converts the DPoP public key to a JWK thumbprint</span><a class="self-link" href="#authorization-code-pkce-flow-step-17"></a></h4>
<p>Currently the DPoP token contains a JWK public key, but before we place it inside the access
token, it needs to be converted into a <a href="https://tools.ietf.org/html/rfc7638">JWK thumbprint</a>.
Our JWK thumbprint looks more like:</p>
<pre class="language-text highlight">9XmwK8mQ3H5-PnzAt3lFHzWBW_v5QhYynezbbit4kC8
</pre>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-19"><span class="content">19. Generates the id_token</span><a class="self-link" href="#authorization-code-pkce-flow-step-19"></a></h4>
<p>Since <code>openid</code> was listed as a scope during the authorization request, the OP generates an id
token. The id token will be pushed as claim to any Solid Authorization Server needed, which clients want to get Access Token from.
This token is a <a href="https://jwt.io/introduction/">JSON Web Token</a>. It would look like
the following (you can decrypt the token with <a href="https://jwt.io/#debugger-io?token=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJodHRwczovL2FsaWNlLmNvb2xwb2QuZXhhbXBsZS9wcm9maWxlL2NhcmQjbWUiLCJhdWQiOiJodHRwczovL2RlY2VudHBob3Rvcy5leGFtcGxlL3dlYmlkI3RoaXMiLCJ3ZWJpZCI6Imh0dHBzOi8vYWxpY2UuY29vbHBvZC5leGFtcGxlL3Byb2ZpbGUvY2FyZCNtZSIsImlzcyI6Imh0dHBzOi8vc2VjdXJlYXV0aC5leGFtcGxlIiwianRpIjoiODQ0YTA5NWMtOWNkYi00N2U1LTk1MTAtMWRiYTk4N2MwYTVmIiwiaWF0IjoxNjAzMzg2NDQ4LCJleHAiOjE2MDMzODcwNDh9.T306vT8dmn9gQIMEdG92AM4WRnrhqWZTfDpovwqZ6Zn0mK9yxj0iOVGqXD4CW8-tzDTitNwEGorAo85atL0Oeg">https://jwt.io</a>):</p>
<p class="issue" id="issue-81973254"><a class="self-link" href="#issue-81973254"></a> encoded token needs to be updated</p>
<pre class="language-text highlight">eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJodHRwczovL2FsaWNlLmNvb2xwb2QuZXhhbXBsZS9wcm9maWxlL2NhcmQjbWUiLCJhdWQiOiJodHRwczovL2RlY2VudHBob3Rvcy5leGFtcGxlL3dlYmlkI3RoaXMiLCJ3ZWJpZCI6Imh0dHBzOi8vYWxpY2UuY29vbHBvZC5leGFtcGxlL3Byb2ZpbGUvY2FyZCNtZSIsImlzcyI6Imh0dHBzOi8vc2VjdXJlYXV0aC5leGFtcGxlIiwianRpIjoiODQ0YTA5NWMtOWNkYi00N2U1LTk1MTAtMWRiYTk4N2MwYTVmIiwiaWF0IjoxNjAzMzg2NDQ4LCJleHAiOjE2MDMzODcwNDh9.T306vT8dmn9gQIMEdG92AM4WRnrhqWZTfDpovwqZ6Zn0mK9yxj0iOVGqXD4CW8-tzDTitNwEGorAo85atL0Oeg
</pre>
<p>Token Header:</p>
<pre class="language-json highlight"><c- p>{</c->
<c- f>"alg"</c-><c- p>:</c-> <c- u>"ES256"</c-><c- p>,</c->
<c- f>"typ"</c-><c- p>:</c-> <c- u>"JWT"</c->
<c- p>}</c->
</pre>
<ul>
<li data-md>
<p><code>"alg": "ES256"</code>: indicates the token was signed using eliptic curve</p>
<li data-md>
<p><code>"typ": "JWT"</code>: indicates that this is a JSON web token</p>
</ul>
<p>Token Body:</p>
<pre class="language-json highlight"><c- p>{</c->
<c- f>"sub"</c-><c- p>:</c-> <c- u>"https://alice.coolpod.example/profile/card#me"</c-><c- p>,</c->
<c- f>"aud"</c-><c- p>:</c-> <c- p>[</c-> <c- u>"solid"</c-><c- p>,</c-> <c- u>"https://decentphotos.example/webid#this"</c-><c- p>],</c->
<c- f>"azp"</c-><c- p>:</c-> <c- f>"https://decentphotos.example/webid#this"</c->
<c- f>"webid"</c-><c- p>:</c-> <c- u>"https://alice.coolpod.example/profile/card#me"</c-><c- p>,</c->
<c- f>"iss"</c-><c- p>:</c-> <c- u>"https://secureauth.example"</c-><c- p>,</c->
<c- f>"jti"</c-><c- p>:</c-> <c- u>"844a095c-9cdb-47e5-9510-1dba987c0a5f"</c-><c- p>,</c->
<c- f>"iat"</c-><c- p>:</c-> <c- mi>1603370641</c-><c- p>,</c->
<c- f>"exp"</c-><c- p>:</c-> <c- mi>1603371241</c-><c- p>,</c->
<c- f>"cnf"</c-><c- p>:</c-> <c- p>{</c->
<c- f>"jkt"</c-><c- p>:</c-> <c- u>"9XmwK8mQ3H5-PnzAt3lFHzWBW_v5QhYynezbbit4kC8"</c->
<c- p>},</c->
<c- p>}</c->
</pre>
<ul>
<li data-md>
<p><code>"sub": "https://alice.coolpod.example/profile/card#me"</code>: The subject claim. It must
be the same as the authenticated user’s WebID.</p>
<li data-md>
<p><code>"aud": "https://decentphotos.example/webid#this"</code>: The token’s audience. Because an
id_token is intended for the client and any Solid Authorization Server,
its audience is the client id and the string "solid".</p>
<li data-md>
<p><code>"azp": "https://decentphotos.example/webid#this"</code>: The token’s authorized party. Because an
id_token is intended to be used by the client, its authorized party is the client id.</p>
<li data-md>
<p><code>"webid": "https://alice.coolpod.example/profile/card#me"</code>: The WebID of the user that
logged in</p>
<li data-md>
<p><code>"iss": "https://secureauth.example"</code>: The OP that was used to generate this token</p>
<li data-md>
<p><code>"jti": "844a095c-9cdb-47e5-9510-1dba987c0a5f"</code>: The jti is an optional unique identifier
for this token that can be used to prevent replay attacks.</p>
<li data-md>
<p><code>"iat": 1603370641</code>: The date the token was issued, in this case October 22, 2020 8:44:01</p>
<li data-md>
<p><code>"exp": 1603371241</code>: The token’s expiration date, in this case October 22, 2020 8:54:01</p>
<li data-md>
<p><code>"cnf": { "jkt": "9XmwK8mQ3H5-PnzAt3lFHzWBW_v5QhYynezbbit4kC8" }</code>: The jwk thrumbprint must
be embedded in an object on the field "jkt"</p>
</ul>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-20"><span class="content">20. Generates refresh token</span><a class="self-link" href="#authorization-code-pkce-flow-step-20"></a></h4>
<p>If <code>offline_access</code> was provided as a scope, the OP creates an opaque token as
a refresh token. It could be like the one below. Notice the one below is a JWT, but a refresh
token does not need to be a JWT.</p>
<pre class="language-text highlight">eyJhbGciOiJub25lIn0.eyJqdGkiOiJhNzhiNDllZi03MWM1LTQ5ODUtYTUwYy01ZWYzYWVmMGZkOGYifQ.
</pre>
<p>The example token would decrypt as:</p>
<p>Token Header:</p>
<pre class="language-json highlight"><c- p>{</c->
<c- f>"alg"</c-><c- p>:</c-> <c- u>"none"</c->
<c- p>}</c->
</pre>
<p>Token Body:</p>
<pre class="language-json highlight"><c- p>{</c->
<c- f>"jti"</c-><c- p>:</c-> <c- u>"a78b49ef-71c5-4985-a50c-5ef3aef0fd8f"</c->
<c- p>}</c->
</pre>
<p>Save the refresh token to a persistant store.</p>
<h4 class="no-num heading settled" id="authorization-code-pkce-flow-step-21"><span class="content">21. Sends tokens</span><a class="self-link" href="#authorization-code-pkce-flow-step-21"></a></h4>
<p>Once the OP has confirmed that everything checks out and all the tokens are generated, it sends
a response with the tokens in the body:</p>
<p>Response (content-type: application/json)</p>
<pre class="language-json highlight"><c- p>{</c->
<c- f>"access_token"</c-><c- p>:</c-> <c- u>"531683bf-fea8-4bca-8976-2de50e5c9a50"</c-><c- p>,</c->
<c- f>"id_token"</c-><c- p>:</c-> <c- u>"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJodHRwczovL2FsaWNlLmNvb2xwb2QuZXhhbXBsZS9wcm9maWxlL2NhcmQjbWUiLCJhdWQiOiJodHRwczovL2RlY2VudHBob3Rvcy5leGFtcGxlL3dlYmlkI3RoaXMiLCJ3ZWJpZCI6Imh0dHBzOi8vYWxpY2UuY29vbHBvZC5leGFtcGxlL3Byb2ZpbGUvY2FyZCNtZSIsImlzcyI6Imh0dHBzOi8vc2VjdXJlYXV0aC5leGFtcGxlIiwianRpIjoiODQ0YTA5NWMtOWNkYi00N2U1LTk1MTAtMWRiYTk4N2MwYTVmIiwiaWF0IjoxNjAzMzg2NDQ4LCJleHAiOjE2MDMzODcwNDh9.T306vT8dmn9gQIMEdG92AM4WRnrhqWZTfDpovwqZ6Zn0mK9yxj0iOVGqXD4CW8-tzDTitNwEGorAo85atL0Oeg"</c-><c- p>,</c->
<c- f>"refresh_token"</c-><c- p>:</c-> <c- u>"eyJhbGciOiJub25lIn0.eyJqdGkiOiJhNzhiNDllZi03MWM1LTQ5ODUtYTUwYy01ZWYzYWVmMGZkOGYifQ."</c->
<c- p>}</c->
</pre>
<ul>
<li data-md>
<p><code>"access_token": "531683bf-feea8..."</code>: The OAuth 2.0 access token, which may be used at the
OP’s <code>userinfo_endpoint</code>.</p>
<li data-md>
<p><code>"id_token": "eyJhbGciOiJFU..."</code>: The ID token we generated. The client will use this to
extract information such as the user’s WebId. A client will also exchange the ID token for
an access token at an authorization server.</p>
<li data-md>
<p><code>"refresh_token": "eyJhbGciOiJ..."</code>: The refresh token. The client will use this to fetch a
new ID token when the current token expires.</p>
</ul>
<h3 class="heading settled" data-level="4.2" id="request-flow"><span class="secno">4.2. </span><span class="content">Request Flow</span><a class="self-link" href="#request-flow"></a></h3>
<p><img src="oidc-primer-request.svg" width="980"></p>
<p>In this example, Alice has logged into <code>https://decentphotos.example</code> and has completed the
authentication steps above. She wants to make a request to Bob’s Storage to get a photo album
information at <code>https://bob.otherpod.example/private/photo_album.ttl</code>. Bob has previously
granted access to Alice but has not granted access to anyone else.</p>
<h4 class="no-num heading settled" id="request-flow-step-1"><span class="content">1. Discover Authorization Server</span><a class="self-link" href="#request-flow-step-1"></a></h4>
<p>Client can discover Authorization Server by making request to the resource</p>
<p>Request:</p>
<pre class="language-http highlight">GET https://bob.otherpod.example/private/photo_album.ttl
</pre>
<p>Response:</p>
<pre class="language-http highlight"><c- kr>HTTP</c-><c- o>/</c-><c- m>1.1</c-> <c- m>401</c-> <c- ne>Unauthorized</c->
<c- e>WWW-Authenticate</c-><c- o>:</c-> <c- l>UMA realm="example",</c->
<c- l>as_uri="https://auth.otherpod.example",</c->
<c- l>ticket="016f84e8-f9b9-11e0-bd6f-0021cc6004de"</c->
</pre>
<h4 class="no-num heading settled" id="request-flow-step-2"><span class="content">2. Request AS configuration</span><a class="self-link" href="#request-flow-step-2"></a></h4>
<p>Knowing Authorization Server now client can discover its Token Endpoint (<a data-link-type="biblio" href="#biblio-rfc8414">[RFC8414]</a>)</p>
<p>Request:</p>
<pre class="language-http highlight">GET https://auth.otherpod.example/.well-known/uma2-configuration
</pre>
<p>Response:</p>
<pre class="language-json highlight"><c- p>{</c->
<c- f>"issuer"</c-><c- p>:</c-> <c- u>"https://auth.otherpod.example"</c-><c- p>,</c->
<c- f>"token_endpoint"</c-><c- p>:</c-> <c- u>"https://auth.otherpod.example/token"</c-><c- p>,</c->
<c- f>"grant_types_supported"</c-><c- p>:</c-> <c- p>[</c->
<c- u>"urn:ietf:params:oauth:grant-type:uma-ticket"</c->
<c- p>]</c->
...
<c- p>}</c->
</pre>
<h4 class="no-num heading settled" id="request-flow-step-3"><span class="content">3. Creates a DPoP header token</span><a class="self-link" href="#request-flow-step-3"></a></h4>
<p>Before we request access token, we need to generate a DPoP header token. A new DPoP token must be
generated every time a request is made.</p>
<p>Generating a DPoP token is done the same way we did it in the authentication section. It must be signed by the same keypair that we generated in the authentication section. Our token
could look like the following (you can decode the token using https://jwt.io):</p>
<pre class="language-text highlight">eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Arand0IiwiandrIjp7Imt0eSI6IkVDIiwia2lkIjoiQ21HVE9Dd3ZKWXhrb0dGOGNxcFpBNTdab2xVdThBcFJQb3MwVlduWk1TNCIsInVzZSI6InNpZyIsImFsZyI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiU0FZcmF5VUh4Z1FPQ29YSC1MbHdyOW1iSWJpUHBsLXRQRUpLeE1WWFltcyIsInkiOiJ6eGJQODdPQ3JpeEZpMk9vZjU1QkhsTC1ySHhvWHVuUmttNFBkV3duUzJnIn19.eyJodHUiOiJodHRwczovL2JvYi5vdGhlcnBvZC5leGFtcGxlL3ByaXZhdGUvcGhvdG9fYWxidW0udHRsIiwiaHRtIjoiZ2V0IiwianRpIjoiZmIxMjY0ZGQtZmZmMS00NTA5LWE3YjEtMGZlNThkMDhkM2UxIiwiaWF0IjoxNjAzMzg5NjE2LCJleHAiOjE2MDMzOTMyMTZ9.G8JktoMOadenCYtO4Z_ZI7ACnjKJvT59OyKlQ6WpB1Qq2GoCK6v1ocrpsfELDOKIL5nt5fwWccfvCAA2bMrkjA
</pre>
<p>Token Header:</p>
<pre class="language-json highlight"><c- p>{</c->
<c- f>"alg"</c-><c- p>:</c-> <c- u>"ES256"</c-><c- p>,</c->
<c- f>"typ"</c-><c- p>:</c-> <c- u>"dpop+jwt"</c-><c- p>,</c->
<c- f>"jwk"</c-><c- p>:</c-> <c- p>{</c->
<c- f>"kty"</c-><c- p>:</c-> <c- u>"EC"</c-><c- p>,</c->
<c- f>"kid"</c-><c- p>:</c-> <c- u>"2i00gHnREsMhD5WqsABPSaqEjLC5MS-E98ykd-qtF1I"</c-><c- p>,</c->
<c- f>"use"</c-><c- p>:</c-> <c- u>"sig"</c-><c- p>,</c->
<c- f>"alg"</c-><c- p>:</c-> <c- u>"EC"</c-><c- p>,</c->
<c- f>"crv"</c-><c- p>:</c-> <c- u>"P-256"</c-><c- p>,</c->
<c- f>"x"</c-><c- p>:</c-> <c- u>"N6VsICiPA1ciAA82Jhv7ykkPL9B0ippUjmla8Snr4HY"</c-><c- p>,</c->
<c- f>"y"</c-><c- p>:</c-> <c- u>"ay9qDOrFGdGe_3hAivW5HnqHYdnYUkXJJevHOBU4z5s"</c-><c- p>,</c->
<c- p>}</c->
<c- p>}</c->
</pre>
<ul>
<li data-md>
<p><code>"alg": "ES256"</code>: The signing algorithm used in this token. In this case, <code>ES256</code> because we
generated the keys using eliptic curves.</p>
<li data-md>
<p><code>"typ": "dpop+jwt"</code>: The type of token. All DPoP Tokens should have a type of "dpop+jwt"</p>
<li data-md>
<p><code>"jwk": { "kty": "EC" ... }</code>: The client’s public key.</p>
</ul>
<p>Token Body:</p>
<pre class="language-json highlight"><c- p>{</c->
<c- f>"htu"</c-><c- p>:</c-> <c- u>"https://auth.otherpod.example/token"</c-><c- p>,</c->
<c- f>"htm"</c-><c- p>:</c-> <c- u>"POST"</c-><c- p>,</c->
<c- f>"jti"</c-><c- p>:</c-> <c- u>"fb1264dd-fff1-4509-a7b1-0fe58d08d3e1"</c-><c- p>,</c->
<c- f>"iat"</c-><c- p>:</c-> <c- mi>1603389616</c->
<c- p>}</c->
</pre>
<ul>
<li data-md>
<p><code>"htu": "https://auth.otherpod.example/token"</code>: htu limits the token for
use only on the given url.</p>
<li data-md>
<p><code>"htm": "POST"</code>: htm limits the token for use only on a specific http method, in this case <code>POST</code>.</p>
<li data-md>
<p><code>"jti": "fb1264dd-fff1-4509-a7b1-0fe58d08d3e1"</code>: jti is a unique identifier for the DPoP