@zoreal/oauth2-react 0.2.18 → 0.2.20
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 +86 -42
- package/dist/index.cjs +163 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +162 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.d.cts
CHANGED
|
@@ -254,6 +254,13 @@ declare function ZorealBusyRing({ busy, radius, theme, block, className, style,
|
|
|
254
254
|
children: ReactNode;
|
|
255
255
|
}): react.JSX.Element;
|
|
256
256
|
|
|
257
|
+
declare function ZorealMark({ size, brand, className, }: {
|
|
258
|
+
size?: number;
|
|
259
|
+
/** Paint the brand blue instead of inheriting currentColor. */
|
|
260
|
+
brand?: boolean;
|
|
261
|
+
className?: string;
|
|
262
|
+
}): react.JSX.Element;
|
|
263
|
+
|
|
257
264
|
/**
|
|
258
265
|
* The pairing modal, rendered by the SDK rather than by every integrator.
|
|
259
266
|
*
|
|
@@ -323,4 +330,4 @@ declare function hasGrantedAnyScopeZoreal(response: Pick<ZorealCodeResponse, 'sc
|
|
|
323
330
|
*/
|
|
324
331
|
declare function resolveIntent(intent: LoginIntent | undefined, scope: string | undefined, acrValues: string | readonly string[] | undefined): LoginIntent;
|
|
325
332
|
|
|
326
|
-
export { type AcrValue, type AuthCodeFlowOptions, type BrowserDirectFlowOptions, DEFAULT_PAIRING_TIMEOUT_MS, type ErrorCode, type LoginIntent, type NonOAuthError, PairingModal, type PairingModalProps, type PairingState, type PairingUI, type SelectBy, type UseZorealAutoLoginOptions, ZorealBusyRing, type ZorealButtonConfiguration, type ZorealCodeResponse, type ZorealCredentialResponse, ZorealLogin, type ZorealLoginProps, type ZorealLoginRequestOptions, type ZorealOAuthContextProps, ZorealOAuthProvider, type ZorealOAuthProviderProps, type ZorealTheme, hasGrantedAllScopesZoreal, hasGrantedAnyScopeZoreal, resolveIntent, useZorealAutoLogin, useZorealLogin, useZorealOAuth, zorealLogout };
|
|
333
|
+
export { type AcrValue, type AuthCodeFlowOptions, type BrowserDirectFlowOptions, DEFAULT_PAIRING_TIMEOUT_MS, type ErrorCode, type LoginIntent, type NonOAuthError, PairingModal, type PairingModalProps, type PairingState, type PairingUI, type SelectBy, type UseZorealAutoLoginOptions, ZorealBusyRing, type ZorealButtonConfiguration, type ZorealCodeResponse, type ZorealCredentialResponse, ZorealLogin, type ZorealLoginProps, type ZorealLoginRequestOptions, ZorealMark, type ZorealOAuthContextProps, ZorealOAuthProvider, type ZorealOAuthProviderProps, type ZorealTheme, hasGrantedAllScopesZoreal, hasGrantedAnyScopeZoreal, resolveIntent, useZorealAutoLogin, useZorealLogin, useZorealOAuth, zorealLogout };
|
package/dist/index.d.ts
CHANGED
|
@@ -254,6 +254,13 @@ declare function ZorealBusyRing({ busy, radius, theme, block, className, style,
|
|
|
254
254
|
children: ReactNode;
|
|
255
255
|
}): react.JSX.Element;
|
|
256
256
|
|
|
257
|
+
declare function ZorealMark({ size, brand, className, }: {
|
|
258
|
+
size?: number;
|
|
259
|
+
/** Paint the brand blue instead of inheriting currentColor. */
|
|
260
|
+
brand?: boolean;
|
|
261
|
+
className?: string;
|
|
262
|
+
}): react.JSX.Element;
|
|
263
|
+
|
|
257
264
|
/**
|
|
258
265
|
* The pairing modal, rendered by the SDK rather than by every integrator.
|
|
259
266
|
*
|
|
@@ -323,4 +330,4 @@ declare function hasGrantedAnyScopeZoreal(response: Pick<ZorealCodeResponse, 'sc
|
|
|
323
330
|
*/
|
|
324
331
|
declare function resolveIntent(intent: LoginIntent | undefined, scope: string | undefined, acrValues: string | readonly string[] | undefined): LoginIntent;
|
|
325
332
|
|
|
326
|
-
export { type AcrValue, type AuthCodeFlowOptions, type BrowserDirectFlowOptions, DEFAULT_PAIRING_TIMEOUT_MS, type ErrorCode, type LoginIntent, type NonOAuthError, PairingModal, type PairingModalProps, type PairingState, type PairingUI, type SelectBy, type UseZorealAutoLoginOptions, ZorealBusyRing, type ZorealButtonConfiguration, type ZorealCodeResponse, type ZorealCredentialResponse, ZorealLogin, type ZorealLoginProps, type ZorealLoginRequestOptions, type ZorealOAuthContextProps, ZorealOAuthProvider, type ZorealOAuthProviderProps, type ZorealTheme, hasGrantedAllScopesZoreal, hasGrantedAnyScopeZoreal, resolveIntent, useZorealAutoLogin, useZorealLogin, useZorealOAuth, zorealLogout };
|
|
333
|
+
export { type AcrValue, type AuthCodeFlowOptions, type BrowserDirectFlowOptions, DEFAULT_PAIRING_TIMEOUT_MS, type ErrorCode, type LoginIntent, type NonOAuthError, PairingModal, type PairingModalProps, type PairingState, type PairingUI, type SelectBy, type UseZorealAutoLoginOptions, ZorealBusyRing, type ZorealButtonConfiguration, type ZorealCodeResponse, type ZorealCredentialResponse, ZorealLogin, type ZorealLoginProps, type ZorealLoginRequestOptions, ZorealMark, type ZorealOAuthContextProps, ZorealOAuthProvider, type ZorealOAuthProviderProps, type ZorealTheme, hasGrantedAllScopesZoreal, hasGrantedAnyScopeZoreal, resolveIntent, useZorealAutoLogin, useZorealLogin, useZorealOAuth, zorealLogout };
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { createContext, useContext, useMemo, useState as useState2 } from "react
|
|
|
5
5
|
|
|
6
6
|
// src/wire.ts
|
|
7
7
|
var WIRE_VERSION = 1;
|
|
8
|
-
var SDK_VERSION = "0.2.
|
|
8
|
+
var SDK_VERSION = "0.2.20";
|
|
9
9
|
var DEFAULT_ISSUER = "https://id.zoreal.com";
|
|
10
10
|
var POLL_INTERVAL_MS = 2e3;
|
|
11
11
|
var POLL_INTERVAL_ENROLLING_MS = 5e3;
|
|
@@ -1600,6 +1600,76 @@ import { useMemo as useMemo2, useState as useState4 } from "react";
|
|
|
1600
1600
|
// src/useZorealLogin.ts
|
|
1601
1601
|
import { useCallback, useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
|
|
1602
1602
|
|
|
1603
|
+
// src/return.ts
|
|
1604
|
+
var PREFIX2 = "zoreal:oauth2:return:";
|
|
1605
|
+
var DONE = "zoreal:oauth2:done:";
|
|
1606
|
+
var MAX_AGE_MS = 10 * 60 * 1e3;
|
|
1607
|
+
function storage() {
|
|
1608
|
+
try {
|
|
1609
|
+
return typeof localStorage === "undefined" ? null : localStorage;
|
|
1610
|
+
} catch {
|
|
1611
|
+
return null;
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
function saveReturnFlow(flow) {
|
|
1615
|
+
try {
|
|
1616
|
+
storage()?.setItem(PREFIX2 + flow.requestId, JSON.stringify(flow));
|
|
1617
|
+
} catch {
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
function peekReturnFlow(requestId) {
|
|
1621
|
+
const store = storage();
|
|
1622
|
+
if (!store) return null;
|
|
1623
|
+
const raw = store.getItem(PREFIX2 + requestId);
|
|
1624
|
+
if (!raw) return null;
|
|
1625
|
+
try {
|
|
1626
|
+
const flow = JSON.parse(raw);
|
|
1627
|
+
if (flow.v !== 1 || flow.requestId !== requestId) return null;
|
|
1628
|
+
if (Date.now() - flow.createdAt > MAX_AGE_MS) return null;
|
|
1629
|
+
return flow;
|
|
1630
|
+
} catch {
|
|
1631
|
+
return null;
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
function forgetReturnFlow(requestId) {
|
|
1635
|
+
try {
|
|
1636
|
+
storage()?.removeItem(PREFIX2 + requestId);
|
|
1637
|
+
} catch {
|
|
1638
|
+
}
|
|
1639
|
+
if (pending === requestId) pending = null;
|
|
1640
|
+
}
|
|
1641
|
+
function markReturnDone(requestId) {
|
|
1642
|
+
try {
|
|
1643
|
+
const store = storage();
|
|
1644
|
+
store?.setItem(DONE + requestId, String(Date.now()));
|
|
1645
|
+
store?.removeItem(PREFIX2 + requestId);
|
|
1646
|
+
} catch {
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
function isReturnDone(requestId) {
|
|
1650
|
+
return storage()?.getItem(DONE + requestId) !== null && storage()?.getItem(DONE + requestId) !== void 0;
|
|
1651
|
+
}
|
|
1652
|
+
function returnToUrl() {
|
|
1653
|
+
if (typeof window === "undefined") return void 0;
|
|
1654
|
+
const { href } = window.location;
|
|
1655
|
+
const hash = href.indexOf("#");
|
|
1656
|
+
return hash === -1 ? href : href.slice(0, hash);
|
|
1657
|
+
}
|
|
1658
|
+
var RETURN_MARK = /(?:^|[#&])zoreal_return=([A-Za-z0-9]{32})(?:&|$)/;
|
|
1659
|
+
var pending = null;
|
|
1660
|
+
function pendingReturnId() {
|
|
1661
|
+
if (pending) return pending;
|
|
1662
|
+
if (typeof window === "undefined") return null;
|
|
1663
|
+
const match = RETURN_MARK.exec(window.location.hash);
|
|
1664
|
+
if (!match) return null;
|
|
1665
|
+
pending = match[1];
|
|
1666
|
+
try {
|
|
1667
|
+
window.history.replaceState(window.history.state, "", returnToUrl());
|
|
1668
|
+
} catch {
|
|
1669
|
+
}
|
|
1670
|
+
return pending;
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1603
1673
|
// src/jwt.ts
|
|
1604
1674
|
function unsafeClaims(idToken) {
|
|
1605
1675
|
try {
|
|
@@ -1979,6 +2049,7 @@ function generateRequestId() {
|
|
|
1979
2049
|
}
|
|
1980
2050
|
|
|
1981
2051
|
// src/useZorealLogin.ts
|
|
2052
|
+
var resumedReturns = /* @__PURE__ */ new Set();
|
|
1982
2053
|
function useZorealFlow(options) {
|
|
1983
2054
|
const { clientId, issuer, locale } = useZorealOAuth();
|
|
1984
2055
|
const [pairing, setPairing] = useState3(null);
|
|
@@ -1988,6 +2059,7 @@ function useZorealFlow(options) {
|
|
|
1988
2059
|
const abortRef = useRef2(null);
|
|
1989
2060
|
const optionsRef = useRef2(options);
|
|
1990
2061
|
optionsRef.current = options;
|
|
2062
|
+
const closedByPerson = useRef2(false);
|
|
1991
2063
|
useEffect2(
|
|
1992
2064
|
() => () => {
|
|
1993
2065
|
abortRef.current?.abort();
|
|
@@ -1995,6 +2067,61 @@ function useZorealFlow(options) {
|
|
|
1995
2067
|
},
|
|
1996
2068
|
[]
|
|
1997
2069
|
);
|
|
2070
|
+
useEffect2(() => {
|
|
2071
|
+
const id = pendingReturnId();
|
|
2072
|
+
if (!id || resumedReturns.has(id)) return;
|
|
2073
|
+
const saved = peekReturnFlow(id);
|
|
2074
|
+
if (!saved || saved.clientId !== clientId) return;
|
|
2075
|
+
forgetReturnFlow(id);
|
|
2076
|
+
resumedReturns.add(id);
|
|
2077
|
+
const controller = new AbortController();
|
|
2078
|
+
abortRef.current = controller;
|
|
2079
|
+
void (async () => {
|
|
2080
|
+
const opts = optionsRef.current;
|
|
2081
|
+
try {
|
|
2082
|
+
const code = await pollUntilApproved(issuer, id, void 0, controller.signal, {
|
|
2083
|
+
tolerateUnknownUntil: Date.now() + 5e3
|
|
2084
|
+
});
|
|
2085
|
+
if (saved.flow === "auth-code") {
|
|
2086
|
+
opts.onCode?.({
|
|
2087
|
+
code,
|
|
2088
|
+
scope: saved.scope,
|
|
2089
|
+
app_state: saved.appState,
|
|
2090
|
+
code_verifier: saved.verifier,
|
|
2091
|
+
nonce: saved.nonce
|
|
2092
|
+
});
|
|
2093
|
+
} else {
|
|
2094
|
+
const tokens = await exchangeCode(issuer, {
|
|
2095
|
+
code,
|
|
2096
|
+
code_verifier: saved.verifier,
|
|
2097
|
+
client_id: clientId
|
|
2098
|
+
});
|
|
2099
|
+
const claims = unsafeClaims(tokens.id_token);
|
|
2100
|
+
opts.onCredential?.({
|
|
2101
|
+
credential: tokens.id_token,
|
|
2102
|
+
clientId,
|
|
2103
|
+
select_by: "app_link",
|
|
2104
|
+
acr: claims.acr ?? "zoreal.device"
|
|
2105
|
+
});
|
|
2106
|
+
}
|
|
2107
|
+
markReturnDone(id);
|
|
2108
|
+
} catch (e) {
|
|
2109
|
+
if (e instanceof DOMException && e.name === "AbortError") return;
|
|
2110
|
+
if (e instanceof FlowAbandonedError) {
|
|
2111
|
+
opts.onNonOAuthError?.(e.reason);
|
|
2112
|
+
return;
|
|
2113
|
+
}
|
|
2114
|
+
if (e instanceof OAuthFlowError) {
|
|
2115
|
+
opts.onError?.({ error: e.error, description: e.description });
|
|
2116
|
+
return;
|
|
2117
|
+
}
|
|
2118
|
+
opts.onNonOAuthError?.({
|
|
2119
|
+
type: "unknown",
|
|
2120
|
+
description: e instanceof Error ? e.message : String(e)
|
|
2121
|
+
});
|
|
2122
|
+
}
|
|
2123
|
+
})();
|
|
2124
|
+
}, [clientId, issuer]);
|
|
1998
2125
|
const login = useCallback(() => {
|
|
1999
2126
|
const opts = optionsRef.current;
|
|
2000
2127
|
const run = async () => {
|
|
@@ -2011,8 +2138,23 @@ function useZorealFlow(options) {
|
|
|
2011
2138
|
try {
|
|
2012
2139
|
let code;
|
|
2013
2140
|
let selectBy = "device";
|
|
2141
|
+
let returnId = null;
|
|
2014
2142
|
if (useAppLink) {
|
|
2015
2143
|
const requestId = generateRequestId();
|
|
2144
|
+
saveReturnFlow({
|
|
2145
|
+
v: 1,
|
|
2146
|
+
issuer,
|
|
2147
|
+
clientId,
|
|
2148
|
+
flow,
|
|
2149
|
+
verifier,
|
|
2150
|
+
nonce,
|
|
2151
|
+
state,
|
|
2152
|
+
scope: opts.scope ?? "openid",
|
|
2153
|
+
appState: opts.app_state,
|
|
2154
|
+
requestId,
|
|
2155
|
+
createdAt: Date.now()
|
|
2156
|
+
});
|
|
2157
|
+
returnId = requestId;
|
|
2016
2158
|
const startUrl = sameDeviceStartUrl(issuer, {
|
|
2017
2159
|
client_id: clientId,
|
|
2018
2160
|
scope: opts.scope ?? "openid",
|
|
@@ -2025,10 +2167,12 @@ function useZorealFlow(options) {
|
|
|
2025
2167
|
prompt: opts.prompt,
|
|
2026
2168
|
locale,
|
|
2027
2169
|
request_id: requestId,
|
|
2028
|
-
origin: window.location.origin
|
|
2170
|
+
origin: window.location.origin,
|
|
2171
|
+
return_to: returnToUrl()
|
|
2029
2172
|
});
|
|
2030
2173
|
selectBy = "app_link";
|
|
2031
2174
|
const cancel = () => {
|
|
2175
|
+
closedByPerson.current = true;
|
|
2032
2176
|
controller.abort();
|
|
2033
2177
|
setPairing(null);
|
|
2034
2178
|
};
|
|
@@ -2055,6 +2199,9 @@ function useZorealFlow(options) {
|
|
|
2055
2199
|
controller.signal,
|
|
2056
2200
|
{ tolerateUnknownUntil: Date.now() + 15e3 }
|
|
2057
2201
|
);
|
|
2202
|
+
if (isReturnDone(requestId)) {
|
|
2203
|
+
throw new DOMException("aborted", "AbortError");
|
|
2204
|
+
}
|
|
2058
2205
|
} else {
|
|
2059
2206
|
const started = await startPairing(issuer, {
|
|
2060
2207
|
client_id: clientId,
|
|
@@ -2076,6 +2223,7 @@ function useZorealFlow(options) {
|
|
|
2076
2223
|
selectBy = "qr";
|
|
2077
2224
|
const qrRefreshSeconds = qrRefreshSecondsOf(started);
|
|
2078
2225
|
const cancel = () => {
|
|
2226
|
+
closedByPerson.current = true;
|
|
2079
2227
|
controller.abort();
|
|
2080
2228
|
setPairing(null);
|
|
2081
2229
|
publishRef.current?.(null);
|
|
@@ -2125,6 +2273,7 @@ function useZorealFlow(options) {
|
|
|
2125
2273
|
}
|
|
2126
2274
|
setPairing(null);
|
|
2127
2275
|
publishRef.current?.(null);
|
|
2276
|
+
if (returnId) markReturnDone(returnId);
|
|
2128
2277
|
if (flow === "auth-code") {
|
|
2129
2278
|
opts.onCode?.({
|
|
2130
2279
|
code,
|
|
@@ -2151,7 +2300,16 @@ function useZorealFlow(options) {
|
|
|
2151
2300
|
} catch (e) {
|
|
2152
2301
|
setPairing(null);
|
|
2153
2302
|
publishRef.current?.(null);
|
|
2154
|
-
if (e instanceof DOMException && e.name === "AbortError")
|
|
2303
|
+
if (e instanceof DOMException && e.name === "AbortError") {
|
|
2304
|
+
if (closedByPerson.current) {
|
|
2305
|
+
closedByPerson.current = false;
|
|
2306
|
+
opts.onNonOAuthError?.({
|
|
2307
|
+
type: "popup_closed",
|
|
2308
|
+
description: "the sign-in dialog was closed before the holder approved"
|
|
2309
|
+
});
|
|
2310
|
+
}
|
|
2311
|
+
return;
|
|
2312
|
+
}
|
|
2155
2313
|
if (e instanceof FlowAbandonedError) {
|
|
2156
2314
|
opts.onNonOAuthError?.(e.reason);
|
|
2157
2315
|
return;
|
|
@@ -2395,6 +2553,7 @@ export {
|
|
|
2395
2553
|
PairingModal,
|
|
2396
2554
|
ZorealBusyRing,
|
|
2397
2555
|
ZorealLogin,
|
|
2556
|
+
ZorealMark,
|
|
2398
2557
|
ZorealOAuthProvider,
|
|
2399
2558
|
hasGrantedAllScopesZoreal,
|
|
2400
2559
|
hasGrantedAnyScopeZoreal,
|