@zoreal/oauth2-react 0.1.11 → 0.2.6
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 +80 -33
- package/dist/index.cjs +783 -112
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +72 -18
- package/dist/index.d.ts +72 -18
- package/dist/index.js +775 -106
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @zoreal/oauth2-react
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@zoreal/oauth2-react) [](https://www.npmjs.com/package/@zoreal/oauth2-react) [](https://github.com/Bynn-Intelligence/zoreal-oauth2-react/actions/workflows/ci.yml) [](./LICENSE)
|
|
3
|
+
[](https://www.npmjs.com/package/@zoreal/oauth2-react) [](https://www.npmjs.com/package/@zoreal/oauth2-react) [](https://github.com/Bynn-Intelligence/zoreal-oauth2-react/actions/workflows/ci.yml) [](https://scorecard.dev/viewer/?uri=github.com/Bynn-Intelligence/zoreal-oauth2-react) [](./LICENSE)
|
|
4
4
|
|
|
5
5
|
Login with ZOREAL for React: a ZOREAL Verified Proof-of-Human behind every sign-in.
|
|
6
6
|
|
|
@@ -76,20 +76,28 @@ origin.
|
|
|
76
76
|
|
|
77
77
|
## Quick start: auth-code (email and name, needs your backend)
|
|
78
78
|
|
|
79
|
+
Two pieces: the provider once, and a button wherever you sign people in. The
|
|
80
|
+
QR, its live status, the countdown and the cancel wiring are the SDK's job.
|
|
81
|
+
|
|
79
82
|
```tsx
|
|
80
|
-
|
|
83
|
+
// app.tsx — once, around the part of your app that signs in.
|
|
84
|
+
import { ZorealOAuthProvider } from '@zoreal/oauth2-react';
|
|
85
|
+
|
|
86
|
+
<ZorealOAuthProvider clientId={ZOREAL_CLIENT_ID} locale="en" theme="auto">
|
|
87
|
+
<App />
|
|
88
|
+
</ZorealOAuthProvider>;
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
// ZorealSignIn.tsx
|
|
81
93
|
import { useZorealLogin } from '@zoreal/oauth2-react';
|
|
82
94
|
|
|
83
95
|
function ZorealSignIn() {
|
|
84
|
-
const [pairing, setPairing] = useState(null);
|
|
85
|
-
|
|
86
96
|
// `email` (and profile.name, etc.) are returned from /userinfo on your backend.
|
|
87
97
|
const login = useZorealLogin({
|
|
88
98
|
flow: 'auth-code',
|
|
89
99
|
scope: 'openid email profile.name',
|
|
90
|
-
onPairingStateChange: setPairing,
|
|
91
100
|
onSuccess: async ({ code, code_verifier, nonce }) => {
|
|
92
|
-
setPairing(null);
|
|
93
101
|
// Send ALL THREE to your backend over TLS. It calls POST /token with the
|
|
94
102
|
// code and verifier plus its client authentication, verifies the ID
|
|
95
103
|
// token's nonce is this one, then reads the email and name from
|
|
@@ -100,35 +108,22 @@ function ZorealSignIn() {
|
|
|
100
108
|
body: JSON.stringify({ code, code_verifier, nonce }),
|
|
101
109
|
});
|
|
102
110
|
},
|
|
111
|
+
onError: (e) => console.error(e.description ?? e.error),
|
|
112
|
+
onNonOAuthError: (e) => {
|
|
113
|
+
// The holder closing or ignoring the request is not an error to surface.
|
|
114
|
+
if (e.type === 'request_denied' || e.type === 'request_expired') return;
|
|
115
|
+
console.error(e.description ?? e.type);
|
|
116
|
+
},
|
|
103
117
|
});
|
|
104
118
|
|
|
105
|
-
return
|
|
106
|
-
<div>
|
|
107
|
-
<button onClick={login}>Continue with ZOREAL</button>
|
|
108
|
-
{pairing && !pairing.appLink && ['pending', 'claimed'].includes(pairing.status) && (
|
|
109
|
-
<div>
|
|
110
|
-
<img src={pairing.qrUrl} alt="Log in with ZOREAL" width={200} height={200} />
|
|
111
|
-
<p>
|
|
112
|
-
{pairing.status === 'claimed'
|
|
113
|
-
? 'Approve the login in your ZOREAL ID app.'
|
|
114
|
-
: 'Scan with your phone camera or the ZOREAL ID app.'}
|
|
115
|
-
</p>
|
|
116
|
-
<button onClick={pairing.cancel}>Cancel</button>
|
|
117
|
-
</div>
|
|
118
|
-
)}
|
|
119
|
-
</div>
|
|
120
|
-
);
|
|
119
|
+
return <button onClick={login}>Continue with ZOREAL</button>;
|
|
121
120
|
}
|
|
122
121
|
```
|
|
123
122
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
`appLink` and `cancel` on every callback, including one fired immediately when
|
|
129
|
-
the pairing starts. On a phone (`appLink: true`) the SDK opens the pairing
|
|
130
|
-
link itself and no panel is needed. Only the drop-in `<ZorealLogin>` button
|
|
131
|
-
below renders its own QR panel, and that component is browser-direct only.
|
|
123
|
+
That is the whole integration. When `login()` runs, the provider puts the
|
|
124
|
+
pairing modal on screen; on a phone it skips the QR and opens the ZOREAL ID
|
|
125
|
+
app instead. See [The pairing modal](#the-pairing-modal) for what it does and
|
|
126
|
+
how to theme, translate, time out or replace it.
|
|
132
127
|
|
|
133
128
|
## Quick start: the button (no backend, pseudonymous)
|
|
134
129
|
|
|
@@ -148,9 +143,10 @@ import { ZorealOAuthProvider, ZorealLogin } from '@zoreal/oauth2-react';
|
|
|
148
143
|
</ZorealOAuthProvider>
|
|
149
144
|
```
|
|
150
145
|
|
|
151
|
-
On desktop the
|
|
152
|
-
approves in the ZOREAL ID app. On a phone it
|
|
153
|
-
way your page just receives
|
|
146
|
+
On desktop the provider opens the [pairing modal](#the-pairing-modal); the user
|
|
147
|
+
scans the QR with their phone and approves in the ZOREAL ID app. On a phone it
|
|
148
|
+
skips the QR and opens the app directly. Either way your page just receives
|
|
149
|
+
`onSuccess`.
|
|
154
150
|
|
|
155
151
|
### On your backend
|
|
156
152
|
|
|
@@ -209,6 +205,57 @@ Things a backend implementer needs to know, learned the concrete way:
|
|
|
209
205
|
`ux_mode: 'redirect'` is not supported in v1: it would put the PKCE verifier
|
|
210
206
|
in a URL, which is a credential in every access log on the path.
|
|
211
207
|
|
|
208
|
+
## The pairing modal
|
|
209
|
+
|
|
210
|
+
On desktop, a QR sign-in cannot complete unless something puts the pairing code
|
|
211
|
+
on screen. Since 0.2.0 that something is this package: `ZorealOAuthProvider`
|
|
212
|
+
renders the modal for **both** flows, so `useZorealLogin` integrators get the
|
|
213
|
+
whole pairing UI without writing (or styling, or translating) a line of it.
|
|
214
|
+
|
|
215
|
+
```jsx
|
|
216
|
+
<ZorealOAuthProvider clientId={CLIENT_ID} locale={i18n.language} theme="auto">
|
|
217
|
+
<App />
|
|
218
|
+
</ZorealOAuthProvider>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
That is the entire integration. Your button stays a button:
|
|
222
|
+
|
|
223
|
+
```jsx
|
|
224
|
+
const login = useZorealLogin({ flow: 'auth-code', scope: 'openid email', onSuccess });
|
|
225
|
+
return <button onClick={login}>Continue with ZOREAL</button>;
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
What the modal does:
|
|
229
|
+
|
|
230
|
+
| | |
|
|
231
|
+
| --- | --- |
|
|
232
|
+
| **Mobile** | No QR. The SDK opens the pairing link, which the ZOREAL ID app claims; the modal never appears. Force one or the other with `display: 'qr'` / `'link'`. |
|
|
233
|
+
| **Live status** | The copy and the title follow the pairing: waiting for a scan, then waiting for approval once the holder has claimed the code (the spent QR blurs out behind a phone glyph). |
|
|
234
|
+
| **Countdown** | Counts down to expiry, turning amber under 20s. Reads the clock each tick rather than decrementing, so a backgrounded tab comes back honest. |
|
|
235
|
+
| **Timeout** | Closes and cancels at zero. Defaults to 120s; override with `pairingTimeoutMs`. The provider's own expiry wins when it is shorter. |
|
|
236
|
+
| **Cancel** | The X, the Cancel button, `Escape`, clicking outside and the timeout are one behaviour: abort the poll, close the modal. An orphaned poll is exactly how a request gets cancelled for over-polling. |
|
|
237
|
+
| **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. |
|
|
238
|
+
| **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. |
|
|
239
|
+
| **Language** | Ships its own copy in 13 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 flips the dialog to RTL. |
|
|
240
|
+
| **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. |
|
|
241
|
+
|
|
242
|
+
Styling is a single `<style>` tag injected once, every selector prefixed `zrl-`
|
|
243
|
+
and scoped to the dialog. There is no CSS file to import and nothing to add to
|
|
244
|
+
your build, because a required import step is a required support ticket.
|
|
245
|
+
|
|
246
|
+
### Rendering it yourself
|
|
247
|
+
|
|
248
|
+
If you already have a design system you want the QR to live inside, opt out:
|
|
249
|
+
|
|
250
|
+
```jsx
|
|
251
|
+
<ZorealOAuthProvider clientId={CLIENT_ID} pairingUI="none">
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
`onPairingStateChange` then carries everything you need on every state:
|
|
255
|
+
`qrUrl`, `pairUrl`, `status`, `expiresIn` and `cancel`. Render `qrUrl` in an
|
|
256
|
+
`<img>`; do not draw your own. `PairingModal` is also exported if you want the
|
|
257
|
+
real dialog but on your own terms.
|
|
258
|
+
|
|
212
259
|
## Scopes and claims
|
|
213
260
|
|
|
214
261
|
Request scopes in the `scope` string, space-separated, always starting with
|