@zoreal/oauth2-js 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -15
- package/dist/index.cjs +1195 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +64 -2
- package/dist/index.d.ts +64 -2
- package/dist/index.js +1193 -3
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -89,11 +89,8 @@ const handle = startLogin({
|
|
|
89
89
|
flow: 'auth-code',
|
|
90
90
|
clientId: 'ast_your_asset_id',
|
|
91
91
|
scope: 'openid email profile.name',
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
// s.cancel on your close button. Every callback carries all of it.
|
|
95
|
-
renderPairing(s);
|
|
96
|
-
},
|
|
92
|
+
// The QR, its live status, the countdown and the cancel wiring are drawn
|
|
93
|
+
// by this package. Nothing to render, nothing to translate.
|
|
97
94
|
});
|
|
98
95
|
|
|
99
96
|
const { code, code_verifier, nonce } = await handle.promise;
|
|
@@ -112,10 +109,7 @@ await fetch('/api/auth/zoreal', {
|
|
|
112
109
|
```ts
|
|
113
110
|
import { startLogin } from '@zoreal/oauth2-js';
|
|
114
111
|
|
|
115
|
-
const handle = startLogin({
|
|
116
|
-
clientId: 'ast_your_asset_id',
|
|
117
|
-
onState: (s) => renderPairing(s),
|
|
118
|
-
});
|
|
112
|
+
const handle = startLogin({ clientId: 'ast_your_asset_id' });
|
|
119
113
|
|
|
120
114
|
const { credential } = await handle.promise;
|
|
121
115
|
// `credential` is an ID token carrying a stable per-user identifier (`sub`)
|
|
@@ -124,10 +118,71 @@ const { credential } = await handle.promise;
|
|
|
124
118
|
// JWKS before trusting it.
|
|
125
119
|
```
|
|
126
120
|
|
|
127
|
-
On desktop
|
|
128
|
-
phone and approves in the ZOREAL ID app. On a phone
|
|
129
|
-
app directly through the pairing link
|
|
130
|
-
|
|
121
|
+
On desktop this package opens the [pairing modal](#the-pairing-modal); the user
|
|
122
|
+
scans the QR with their phone and approves in the ZOREAL ID app. On a phone it
|
|
123
|
+
skips the QR and opens the app directly through the pairing link. Either way
|
|
124
|
+
your page just awaits `handle.promise`.
|
|
125
|
+
|
|
126
|
+
## The pairing modal
|
|
127
|
+
|
|
128
|
+
On desktop a QR sign-in cannot complete unless something puts the pairing code
|
|
129
|
+
on screen, so this package does it. `startLogin` mounts a dialog, keeps it in
|
|
130
|
+
step with the pairing, and takes it down when the flow settles. You render
|
|
131
|
+
nothing.
|
|
132
|
+
|
|
133
|
+
| | |
|
|
134
|
+
| --- | --- |
|
|
135
|
+
| **Mobile** | No QR. The pairing link opens the ZOREAL ID app and the modal never appears. Force one or the other with `display: 'qr'` / `'link'`. |
|
|
136
|
+
| **Live status** | Copy and title follow the pairing: waiting for a scan, then waiting for approval once the code is claimed (the spent QR blurs out behind a phone glyph). |
|
|
137
|
+
| **Countdown** | Counts down to expiry, turning amber under 20s. Reads the clock each tick rather than decrementing, so a backgrounded tab comes back honest. |
|
|
138
|
+
| **Timeout** | Closes and cancels at zero. Defaults to 120s; override with `pairingTimeoutMs`. The provider's own expiry wins when it is shorter. |
|
|
139
|
+
| **Cancel** | The X, the Cancel button, `Escape`, clicking outside and the timeout are one behaviour: abort the poll, close, reject the promise with `AbortError`. |
|
|
140
|
+
| **No ZOREAL ID yet** | A footer says the same code also installs the app. Without it the panel reads as "scan this with something I do not have", and the flow dead-ends at the one moment it can still be recovered. |
|
|
141
|
+
| **Themes** | `theme: 'auto'` (default) follows `prefers-color-scheme`; `'light'` and `'dark'` force it. The QR well stays white in dark mode on purpose — an inverted QR fails on a good number of phone cameras. |
|
|
142
|
+
| **Language** | Ships its own copy in 39 locales. Pass `locale` and the modal matches the page it opened on; omit it and it follows the browser's preference list, English as the floor. Arabic, Hebrew and Urdu flip the dialog to RTL. |
|
|
143
|
+
| **Accessibility** | `role="dialog"`, `aria-modal`, labelled by its title, focus moved in on open, scroll locked, visible focus rings, and a `prefers-reduced-motion` fallback. |
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
startLogin({
|
|
147
|
+
clientId: 'ast_your_asset_id',
|
|
148
|
+
locale: 'sv', // omit to follow the browser
|
|
149
|
+
theme: 'auto', // 'light' | 'dark'
|
|
150
|
+
pairingTimeoutMs: 120_000,
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Styling is a single `<style>` tag injected once, every selector prefixed `zrl-`
|
|
155
|
+
and scoped to the dialog. There is no CSS file to import and nothing to add to
|
|
156
|
+
your build, because a required import step is a required support ticket. The
|
|
157
|
+
DOM is built with `createElement`, never `innerHTML`: this renders on someone
|
|
158
|
+
else's sign-in page.
|
|
159
|
+
|
|
160
|
+
### Languages
|
|
161
|
+
|
|
162
|
+
Arabic · Bengali · Bosnian · Bulgarian · Chinese (Simplified) · Chinese (Traditional) ·
|
|
163
|
+
Croatian · Czech · Danish · Dutch · English · Filipino/Tagalog · Finnish · French ·
|
|
164
|
+
German · Greek · Hebrew · Hindi · Hungarian · Indonesian · Italian · Japanese · Korean ·
|
|
165
|
+
Malay · Norwegian · Polish · Portuguese · Portuguese (Brazil) · Romanian · Russian ·
|
|
166
|
+
Serbian · Spanish · Spanish (Latin America) · Swedish · Thai · Turkish · Ukrainian ·
|
|
167
|
+
Urdu · Vietnamese
|
|
168
|
+
|
|
169
|
+
Resolution handles the cases that usually get missed: `zh` splits by script rather
|
|
170
|
+
than region, `es-MX` and the other Latin American regions resolve to Latin American
|
|
171
|
+
Spanish instead of peninsular, `pt-BR` stays out of European Portuguese, and the
|
|
172
|
+
superseded codes (`iw`, `in`) plus `nb`/`nn` and `fil` reach the right table.
|
|
173
|
+
|
|
174
|
+
### Rendering it yourself
|
|
175
|
+
|
|
176
|
+
Framework wrappers and anyone with their own design system opt out:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
startLogin({ clientId: 'ast_your_asset_id', pairingUI: 'none' });
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`onState` then carries everything you need on every state: `qrUrl`, `pairUrl`,
|
|
183
|
+
`status`, `expiresIn` and `cancel`. Render `qrUrl` in an `<img>`; do not draw
|
|
184
|
+
your own. `mountPairingModal` is also exported if you want the real dialog but
|
|
185
|
+
driven on your own terms.
|
|
131
186
|
|
|
132
187
|
## The handle
|
|
133
188
|
|
|
@@ -478,8 +533,14 @@ identical in the browser.
|
|
|
478
533
|
|
|
479
534
|
A wrapper owns exactly two things: calling `startLogin` on the user's
|
|
480
535
|
gesture, and rendering what `onState` carries. Everything else - PKCE, state,
|
|
481
|
-
nonce, poll cadence, cancellation - is this package's job.
|
|
482
|
-
|
|
536
|
+
nonce, poll cadence, cancellation - is this package's job.
|
|
537
|
+
|
|
538
|
+
Decide first whether you are rendering the pairing UI at all. If the built-in
|
|
539
|
+
modal is what you want, drop `onState` and there is nothing to build. If you
|
|
540
|
+
are drawing your own, pass `pairingUI: 'none'` or your users get two QRs on
|
|
541
|
+
screen. The example below draws its own, so it opts out.
|
|
542
|
+
|
|
543
|
+
A minimal Vue 3 composable:
|
|
483
544
|
|
|
484
545
|
```ts
|
|
485
546
|
// useZorealLogin.ts
|
|
@@ -500,6 +561,8 @@ export function useZorealLogin(clientId: string) {
|
|
|
500
561
|
active?.cancel();
|
|
501
562
|
const handle = startLogin({
|
|
502
563
|
clientId,
|
|
564
|
+
pairingUI: 'none', // this wrapper renders its own
|
|
565
|
+
|
|
503
566
|
onState: (s) => (pairing.value = s),
|
|
504
567
|
});
|
|
505
568
|
active = handle;
|