Repository navigation
Expand file tree
/
Copy pathcallback.js
More file actions
129 lines (114 loc) · 3.75 KB
/
Copy pathcallback.js
File metadata and controls
129 lines (114 loc) · 3.75 KB
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
import { loadFlow, clearFlow, exchangeCode, curlFor, redact } from './cimd.js';
const $ = (id) => document.getElementById(id);
const params = new URLSearchParams(location.search);
function row(tbody, cells, className) {
const tr = document.createElement('tr');
if (className) tr.className = className;
for (const cell of cells) {
const td = document.createElement('td');
td.textContent = cell;
tr.append(td);
}
tbody.append(tr);
}
function fail(title, body) {
$('errorTitle').textContent = title;
$('errorBody').textContent = body;
$('error').hidden = false;
$('subtitle').textContent = 'The flow stopped here.';
}
const flow = loadFlow();
const code = params.get('code');
const error = params.get('error');
if (error) {
fail(
`The server returned ${error}`,
[
params.get('error_description') || '(no description)',
params.get('error_uri') ? `More: ${params.get('error_uri')}` : '',
]
.filter(Boolean)
.join('\n\n')
);
} else if (!flow) {
fail(
'No flow in this tab',
'The code verifier and state live in sessionStorage for this tab only. ' +
'If you opened this URL directly, or in a different tab, start the flow again from the home page.'
);
} else if (!code) {
fail('No code in the response', `Query string was: ${location.search || '(empty)'}`);
} else {
render();
}
function render() {
$('subtitle').textContent = 'The server sent an authorization code back.';
const p = $('params');
row(p, ['code', code]);
row(p, ['state', params.get('state') || '(absent)']);
row(p, ['iss', params.get('iss') || '(absent)']);
const c = $('checks');
const stateOk = params.get('state') === flow.state;
row(
c,
['state matches the one this tab sent', stateOk ? 'pass' : 'FAIL'],
stateOk ? 'pass' : 'fail'
);
const iss = params.get('iss');
if (flow.issCheck) {
const issOk = iss === flow.issuer;
row(
c,
[`iss equals ${flow.issuer}`, issOk ? 'pass' : iss ? `FAIL — got ${iss}` : 'FAIL — absent'],
issOk ? 'pass' : 'fail'
);
} else {
row(c, ['iss returned (RFC 9207)', iss ? 'present, though not advertised' : 'not advertised, not sent']);
}
$('curl').textContent = curlFor({ flow, code });
$('response').hidden = false;
if (!stateOk) {
$('exchangeBtn').disabled = true;
$('exchangeIntro').textContent =
'The state does not match, so this response is not the one this tab asked for. The exchange is blocked.';
}
}
let tokenResponse = null;
let revealed = false;
$('exchangeBtn').addEventListener('click', async () => {
const btn = $('exchangeBtn');
btn.setAttribute('aria-busy', 'true');
btn.disabled = true;
$('error').hidden = true;
try {
tokenResponse = await exchangeCode({ flow, code });
$('tokenTitle').textContent = 'Token response';
$('tokenJson').textContent = JSON.stringify(redact(tokenResponse), null, 2);
$('tokenNote').textContent =
'Tokens are held in this tab only. Closing it discards them. Nothing is written to localStorage.';
$('tokenResult').hidden = false;
$('curlWrap').open = false;
btn.textContent = 'Exchanged';
clearFlow();
} catch (err) {
if (err.cors) {
$('curlWrap').open = true;
fail('The browser blocked the token request', err.message);
} else {
fail('The token endpoint rejected the exchange', err.message);
btn.disabled = false;
btn.textContent = 'Try the exchange again';
}
} finally {
btn.removeAttribute('aria-busy');
}
});
$('revealBtn').addEventListener('click', () => {
revealed = !revealed;
$('tokenJson').textContent = JSON.stringify(
revealed ? tokenResponse : redact(tokenResponse),
null,
2
);
$('revealBtn').textContent = revealed ? 'Hide the full token' : 'Show the full token';
});