Repository navigation
Expand file tree
/
Copy pathtools.html
More file actions
387 lines (370 loc) · 24.5 KB
/
Copy pathtools.html
File metadata and controls
387 lines (370 loc) · 24.5 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
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
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Tools · easyssf</title>
<meta name="description" content="Generate Security Event Tokens for the OpenID CAEP, RISC and SCIM Events profiles with any subject identifier, signed in your browser, and inspect SETs you received.">
<link rel="icon" href="easyssf-icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="apple-touch-icon.png">
<link rel="canonical" href="https://easyssf.org/tools.html">
<meta name="robots" content="index, follow">
<meta name="theme-color" content="#fdf8f1" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#171210" media="(prefers-color-scheme: dark)">
<meta property="og:site_name" content="easyssf">
<meta property="og:title" content="Tools · easyssf">
<meta property="og:description" content="Generate Security Event Tokens for the OpenID CAEP, RISC and SCIM Events profiles, signed in your browser, and inspect SETs you received.">
<meta property="og:url" content="https://easyssf.org/tools.html">
<meta property="og:type" content="website">
<meta property="og:image" content="https://easyssf.org/og-image.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://easyssf.org/og-image.png">
<link rel="stylesheet" href="site.css">
<script>/* the chosen theme, before the first paint */try{var t=localStorage.getItem('easyssf.theme');if(t==='light'||t==='dark'){document.documentElement.setAttribute('data-theme',t);}}catch(e){}</script>
</head>
<body>
<header class="site">
<div class="wrap">
<a class="brand" href="index.html" aria-label="easyssf">
<img src="easyssf-icon.svg" alt="" width="26" height="26">
easyssf
</a>
<nav class="top">
<a href="index.html#how">How it works</a>
<a href="index.html#usecases" class="hide-xs">Use cases</a>
<a href="index.html#start" class="hide-xs">Getting started</a>
<a href="index.html#specs" class="hide-sm">Specifications</a>
<a href="tools.html">Tools</a>
<a href="news.html">News</a>
<a href="index.html#code">Code <svg class="gh" width="14" height="14" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg></a>
<button type="button" class="theme-toggle" data-theme-toggle aria-label="Theme: follows the system" title="Theme: follows the system">
<svg class="icon-auto" viewBox="0 0 20 20" aria-hidden="true"><circle cx="10" cy="10" r="7.25" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M10 2.75a7.25 7.25 0 0 1 0 14.5z" fill="currentColor"/></svg>
<svg class="icon-sun" viewBox="0 0 20 20" aria-hidden="true"><circle cx="10" cy="10" r="3.75" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M10 1.5v2.5M10 16v2.5M1.5 10H4M16 10h2.5M4 4l1.8 1.8M14.2 14.2 16 16M4 16l1.8-1.8M14.2 5.8 16 4" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
<svg class="icon-moon" viewBox="0 0 20 20" aria-hidden="true"><path d="M16.5 12.2A7 7 0 0 1 7.8 3.5a7 7 0 1 0 8.7 8.7z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>
</button>
</nav>
</div>
</header>
<main id="top">
<div class="wrap page-title">
<h1>Tools</h1>
<p class="muted intro">Security Event Tokens to try a receiver with, or to show what a CAEP, RISC or SCIM
event looks like.</p>
<div class="note" style="max-width:70ch"><strong>Runs in your browser only.</strong> This page is static: the tokens are built and
signed by the JavaScript of the page, and nothing you enter, no claim, subject or token, is sent to any server. The
signing key is generated in your browser and kept in its local storage; the JWT tab shows it and lets you replace it.</div>
<div class="switch tools-switch" role="tablist" aria-label="Tool" id="tools">
<button type="button" role="tab" aria-selected="true" aria-controls="generator">SET generator</button>
<button type="button" role="tab" aria-selected="false" aria-controls="inspector" tabindex="-1">SET inspector</button>
<button type="button" role="tab" aria-selected="false" aria-controls="metadata" tabindex="-1">Transmitter metadata</button>
<button type="button" role="tab" aria-selected="false" aria-controls="events" tabindex="-1">Event types</button>
<button type="button" role="tab" aria-selected="false" aria-controls="more" tabindex="-1">Other tools</button>
</div>
</div>
<section id="generator" class="tool-panel" role="tabpanel">
<div class="wrap">
<h2>Security Event Token generator</h2>
<p class="muted intro">Pick an event type and get a complete example right away, as the SET's JSON and signed as a JWT with
a key generated in your browser; then change the subject, the claims and the token as you need. The event types
and their claims follow <a href="https://openid.net/specs/openid-caep-1_0.html">CAEP 1.0</a>,
<a href="https://openid.net/specs/openid-risc-1_0.html">RISC 1.0</a>,
<a href="https://www.rfc-editor.org/rfc/rfc9967">RFC 9967</a> (SCIM Events) and
<a href="https://openid.net/specs/openid-sharedsignals-framework-1_0.html">SSF 1.0</a>; the subject identifiers
<a href="https://www.rfc-editor.org/rfc/rfc9493">RFC 9493</a> and the additional formats of SSF.
The <a href="#events">event types</a> below list them all with their claims and the alias easyssf accepts for each.</p>
<div class="tool">
<form id="gen" class="tool-form" autocomplete="off" novalidate>
<div class="tool-actions">
<button type="button" class="btn small" data-action="fresh" title="New jti, iat and event timestamps">Fresh jti and timestamps</button>
<button type="button" class="btn small" data-action="link" title="A link that opens this page with this SET">Copy as link</button>
<button type="button" class="btn small" data-action="reset">Reset</button>
</div>
<fieldset>
<legend>Event</legend>
<div class="switch" role="tablist" aria-label="Event profile" id="profiles"></div>
<div class="field-row">
<div class="field" style="margin-top:0">
<label for="event-type">Event type</label>
<select id="event-type" data-path="event" data-rebuild></select>
</div>
<div class="field" style="margin-top:0">
<label for="spec-version">Specification version</label>
<select id="spec-version" data-rebuild></select>
</div>
</div>
<p class="event-info" id="event-info"></p>
<div id="event-fields"></div>
<div class="tool-actions" style="margin-top:16px;margin-bottom:0">
<button type="button" class="btn small" data-action="example" title="The example values of this event type">Example values</button>
<button type="button" class="btn small" data-action="minimal" title="Clear the optional claims">Required claims only</button>
</div>
<details class="extra">
<summary>Custom claims of the event payload</summary>
<div class="field">
<label for="ev-extra">A JSON object, merged into the payload of the event</label>
<textarea id="ev-extra" data-path="ev.$extra" spellcheck="false" placeholder='{"risk_score": 87}'></textarea>
<span class="hint">For claims a specification does not define yet, or to try a value: a claim with the name
of a standard one replaces it.</span>
</div>
</details>
</fieldset>
<fieldset>
<legend>Subject</legend>
<p class="muted" style="font-size:.9rem;margin:0">The <code>sub_id</code> claim of the SET: a simple identifier,
or a complex subject with a member per aspect of the same principal.</p>
<div id="subject"></div>
</fieldset>
<fieldset>
<legend>Token</legend>
<div class="preset-bar">
<div class="field" style="margin-top:0">
<label for="tok-preset">Preset</label>
<div class="with-btn">
<select id="tok-preset"><option value="">None saved yet</option></select>
<button type="button" class="btn small" data-action="preset-delete" title="Delete the selected preset">Delete</button>
</div>
</div>
<div class="field" style="margin-top:0">
<label for="tok-preset-name">Save the token settings as</label>
<div class="with-btn">
<input type="text" id="tok-preset-name" placeholder="A name for this setup">
<button type="button" class="btn small" data-action="preset-save">Save</button>
</div>
</div>
</div>
<p class="hint" style="margin:6px 0 0;font-size:.8rem;color:var(--fg-muted)">A preset holds the issuer, audience, signature, the
legacy subject option and the custom claims of the SET, for a demo setup you come back to. Presets stay in this
browser's storage. Saving under an existing name replaces it.</p>
<div class="field">
<label for="tok-iss">Issuer <code>iss</code><span class="req" title="required">*</span></label>
<input type="text" id="tok-iss" data-path="token.iss" placeholder="https://idp.example/realms/demo">
<span class="hint">The transmitter. A receiver verifies the signature with the keys this issuer publishes.</span>
</div>
<div class="field">
<label for="tok-aud">Audience <code>aud</code><span class="req" title="required">*</span></label>
<input type="text" id="tok-aud" data-path="token.aud" placeholder="https://my-app.example">
<span class="hint">The receiver. Several, separated by commas, become an array.</span>
</div>
<div class="field">
<label for="tok-jti">Token id <code>jti</code><span class="req" title="required">*</span></label>
<div class="with-btn">
<input type="text" id="tok-jti" class="mono" data-path="token.jti">
<button type="button" class="btn small" data-action="jti">New</button>
</div>
</div>
<div class="field">
<label for="tok-iat">Issued at <code>iat</code><span class="req" title="required">*</span></label>
<div class="with-btn">
<input type="number" id="tok-iat" data-path="token.iat" step="1">
<button type="button" class="btn small" data-action="iat">Now</button>
</div>
<span class="hint" id="iat-human"></span>
</div>
<div class="field">
<label for="tok-txn">Transaction <code>txn</code></label>
<div class="with-btn">
<input type="text" id="tok-txn" class="mono" data-path="token.txn">
<button type="button" class="btn small" data-action="txn">New</button>
</div>
<span class="hint">Groups the SETs of one underlying cause. SSF recommends it, SCIM Events need it for asynchronous requests.</span>
</div>
<div class="field-row">
<div class="field">
<label for="tok-alg">Signature</label>
<select id="tok-alg" data-path="token.alg">
<option value="RS256">RS256, RSA key</option>
<option value="ES256">ES256, P-256 key</option>
<option value="none">Unsigned</option>
</select>
<span class="hint">With a key generated in your browser and kept in its storage.</span>
</div>
<div class="field check" style="align-self:end">
<label><input type="checkbox" data-path="token.legacySubject"> Repeat the subject inside the event as <code>subject</code></label>
<span class="hint">As transmitters built on the drafts did. The receiver's legacy subject mode accepts it.</span>
</div>
</div>
<details class="extra">
<summary>Custom claims of the SET</summary>
<div class="field">
<label for="tok-extra">A JSON object, merged into the top-level claims</label>
<textarea id="tok-extra" data-path="token.extra" spellcheck="false" placeholder='{"toe": 1700000000, "exp": 1700000300}'></textarea>
<span class="hint">Added next to <code>iss</code>, <code>jti</code>, <code>aud</code> and the others; a claim with the
name of a standard one replaces it. Receivers ignore claims they do not know, SSF 1.0 section 4.2.3.</span>
</div>
</details>
</fieldset>
</form>
<div class="tool-splitter" role="separator" aria-orientation="vertical" aria-label="Width of the panes, drag or use the arrow keys; double-click resets" tabindex="0" title="Drag to resize the panes, double-click to reset"></div>
<div class="tool-output">
<div class="note" id="problems" aria-live="polite"></div>
<div class="switch" role="tablist" aria-label="Output" id="outputs">
<button type="button" role="tab" aria-selected="true" aria-controls="out-set">SET</button>
<button type="button" role="tab" aria-selected="false" aria-controls="out-jwt" tabindex="-1">JWT</button>
</div>
<div id="out-set" class="out-panel" role="tabpanel">
<pre><code id="set-json"></code></pre>
<div class="copy">
<button type="button" class="btn small" data-copy="set-json" data-copy-attr="eventText" data-copied-label="Event copied" title="The payload of the event only, the object under its type URI">Copy event</button>
<button type="button" class="btn small" data-copy="set-json" data-copied-label="SET copied">Copy</button>
</div>
<p class="after">The claims of the SET, RFC 8417: the event under its type URI in <code>events</code>, the subject in
<code>sub_id</code>. The JWT tab has it signed. "Copy event" copies the payload of the event alone, for a
transmitter that wraps it in a SET itself.</p>
</div>
<div id="out-jwt" class="out-panel" role="tabpanel" hidden>
<pre><code id="jwt-compact" class="jwt"></code></pre>
<div class="copy"><button type="button" class="btn small" data-copy="jwt-compact" data-copied-label="JWT copied">Copy</button></div>
<div class="after">
<p>Header, <code>typ</code> <code>secevent+jwt</code> as SSF requires:</p>
<pre><code id="jwt-header"></code></pre>
<p id="jwt-note" style="margin-top:12px"></p>
<p>A receiver verifies the signature with the keys the issuer publishes at the <code>jwks_uri</code> of its
<code>.well-known/ssf-configuration</code>, so it accepts this token only if that key set contains the
browser key. Set an <code>iss</code> you control and serve the key set there, or use the inspector below
to read the token.</p>
<details>
<summary>The key, as a JWK Set and as a private JWK</summary>
<pre><code id="jwks"></code></pre>
<pre><code id="jwk-private"></code></pre>
<div class="tool-actions" style="margin-top:12px;margin-bottom:0">
<button type="button" class="btn small" data-action="newkey">Generate a new key</button>
</div>
</details>
</div>
</div>
</div>
</div>
</div>
</section>
<section id="inspector" class="tool-panel" role="tabpanel" hidden>
<div class="wrap">
<h2>Security Event Token inspector</h2>
<p class="muted intro">Paste a SET you received, or one from the generator, to read its header and claims.
Event types are shown with their easyssf alias. The signature is not verified here.</p>
<div class="tool">
<div class="field" style="margin-top:0">
<label for="inspect-in">Compact JWT, or the claims as JSON</label>
<textarea id="inspect-in" spellcheck="false" placeholder="eyJhbGciOi..." style="min-height:12em"></textarea>
</div>
<div class="tool-splitter" role="separator" aria-orientation="vertical" aria-label="Width of the panes, drag or use the arrow keys; double-click resets" tabindex="0" title="Drag to resize the panes, double-click to reset"></div>
<div class="tool-output" style="position:static">
<div class="note" id="inspect-problems"></div>
<dl class="summary" id="inspect-summary" hidden></dl>
<div class="out-panel" id="inspect-out" hidden>
<pre><code id="inspect-json"></code></pre>
<div class="copy"><button type="button" class="btn small" data-copy="inspect-json">Copy</button></div>
</div>
</div>
</div>
</div>
</section>
<section id="events" class="tool-panel" role="tabpanel" hidden>
<div class="wrap">
<h2>Event types</h2>
<p class="muted intro">Every event type the specifications define and easyssf knows, with its claims. The
specifications identify event types by URI; easyssf accepts those URIs everywhere, and additionally the short alias
shown with each event, usable in <code>events-requested</code>, in <code>SsfEventContext.hasEvent()</code> and wherever
else an event type is named. The aliases are an easyssf convention, not part of the specifications; a SET always
carries the URI. You can register your own aliases for vendor-specific types, see the
<a href="https://github.com/easyssf/easyssf-java#readme">README</a>.</p>
<p class="muted intro">Each event can be linked to: <code>tools.html#event-CaepSessionRevoked</code> opens its
card here, <code>tools.html#generate=CaepSessionRevoked</code> opens it in the generator. Instead of the alias, the
fragment can name the event by its URI, as <code>caep/session-revoked</code> or just <code>session-revoked</code>.</p>
<div id="event-list"></div>
</div>
</section>
<section id="metadata" class="tool-panel" role="tabpanel" hidden>
<div class="wrap">
<h2>Transmitter metadata checker</h2>
<p class="muted intro">Checks a transmitter's configuration metadata, the document at
<code>/.well-known/ssf-configuration</code>, against <a href="https://openid.net/specs/openid-sharedsignals-framework-1_0.html#section-7">section 7 of SSF 1.0</a>:
the required and recommended members, the URLs, the delivery methods and the authorization schemes, and what a
receiver can do with the transmitter. Enter the issuer to fetch the document, or paste it.</p>
<div class="tool">
<form id="meta" class="tool-form" autocomplete="off" novalidate>
<div class="field" style="margin-top:0">
<label for="meta-issuer">Issuer, or the URL of its metadata</label>
<div class="with-btn">
<input type="text" id="meta-issuer" placeholder="https://idp.example/realms/demo" inputmode="url">
<button type="submit" class="btn small" id="meta-fetch">Fetch</button>
</div>
<span class="hint" id="meta-locations"></span>
</div>
<div class="field">
<label for="meta-in">Transmitter configuration metadata, JSON</label>
<textarea id="meta-in" spellcheck="false" placeholder='{"issuer": "https://tr.example.com", "jwks_uri": ...}' style="min-height:18em"></textarea>
<span class="hint">Fetching needs the transmitter to allow cross-origin reads, which few do. When it does not, open the
document in a new tab and paste it here. Nothing is sent anywhere but to the transmitter.</span>
</div>
<div class="tool-actions" style="margin-top:12px;margin-bottom:0">
<button type="button" class="btn small" id="meta-example" title="The example of SSF 1.0, section 7.2.3">Load the example of the spec</button>
<button type="button" class="btn small" id="meta-clear">Clear</button>
</div>
</form>
<div class="tool-splitter" role="separator" aria-orientation="vertical" aria-label="Width of the panes, drag or use the arrow keys; double-click resets" tabindex="0" title="Drag to resize the panes, double-click to reset"></div>
<div class="tool-output" style="position:static">
<div class="note" id="meta-fetch-note"></div>
<details class="checks" id="meta-checks-box" hidden>
<summary id="meta-checks-summary" aria-live="polite"></summary>
<ul id="meta-checks"></ul>
</details>
<dl class="summary" id="meta-summary" hidden></dl>
<div class="out-panel" id="meta-out" hidden>
<pre><code id="meta-json"></code></pre>
<div class="copy"><button type="button" class="btn small" data-copy="meta-json">Copy</button></div>
</div>
</div>
</div>
</div>
</section>
<section id="more" class="tool-panel" role="tabpanel" hidden>
<div class="wrap">
<h2>Other tools</h2>
<p class="muted intro">The generator shows what a SET looks like; to see a receiver react, it needs a
transmitter whose keys it can fetch. These do that.</p>
<div class="grid">
<div class="card">
<h3><a href="https://caep.dev">caep.dev</a></h3>
<p>A free, hosted Shared Signals transmitter by SGNL, made for trying receivers: sign in, create a stream with push
or poll delivery to your application, and send CAEP events to it from the browser. It publishes its transmitter
metadata and keys, so an easyssf receiver verifies the SETs like those of any identity provider. It can play the
receiver as well, for when easyssf gains a transmitter.</p>
</div>
<div class="card">
<h3><a href="https://openid.net/certification/">OpenID conformance suite</a></h3>
<p>The official test plans for SSF receivers, default and CAEP interoperability profile, with push and poll
delivery. <code>easyssf-test-conformance</code> runs them against a receiver from a JUnit test, with the suite in
a container.</p>
</div>
<div class="card">
<h3><a href="https://github.com/easyssf/easyssf-java#readme">easyssf-test</a></h3>
<p>A transmitter inside your test JVM: it signs SETs, serves metadata and keys, hands out tokens and emulates the
stream and poll endpoints, for the integration tests of your own receiver.</p>
</div>
<div class="card">
<h3><a href="https://shared-signals.dev">shared-signals.dev</a></h3>
<p>An independent developer guide to the Shared Signals standards: concepts (transmitters, receivers, streams,
SETs, subjects), summaries of SSF, CAEP and RISC, a catalogue of event types with their claims and example
tokens, and a directory of implementations. Its <a href="https://shared-signals.dev/playground/">playground</a>
runs a public transmitter and receiver to test your own implementation against.</p>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="wrap">
<span>easyssf is open source under the Apache License 2.0. Contact: <a href="mailto:oss@easyssf.org">oss@easyssf.org</a></span>
<span>
<a href="https://github.com/easyssf/easyssf-java">GitHub</a> ·
<a href="https://github.com/easyssf/easyssf-java/issues">Issues</a> ·
<a href="https://github.com/easyssf/easyssf-java#readme">Documentation</a>
</span>
</div>
</footer>
<script src="site.js"></script>
<script src="tools.js"></script>
</body>
</html>