@ikonai/sdk-react-ui 1.3.0 → 1.3.2
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/app/use-ikon-app.d.ts +20 -0
- package/auth/auth-service.d.ts +37 -2
- package/auth/index.d.ts +2 -1
- package/auth/oauth-continuation.d.ts +36 -0
- package/auth/storage.d.ts +25 -1
- package/auth/types.d.ts +19 -0
- package/auth/use-auth-guard.d.ts +7 -0
- package/index.js +991 -719
- package/package.json +1 -1
- package/theme/ikon-surface.css +1 -0
package/app/use-ikon-app.d.ts
CHANGED
|
@@ -28,6 +28,14 @@ declare global {
|
|
|
28
28
|
* Module registration function type.
|
|
29
29
|
*/
|
|
30
30
|
export type IkonUiModuleRegistration = (registry: IkonUiRegistry) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Connect parameters for one session: the query string, then whatever the app resolved itself.
|
|
33
|
+
*
|
|
34
|
+
* The app wins on a shared key. An app derives its parameters from something the visitor does not
|
|
35
|
+
* control — the hostname it was served on, a build-time flag — and those choices decide which
|
|
36
|
+
* SessionIdentity the connect resolves to, so a hand-typed query parameter must not displace one.
|
|
37
|
+
*/
|
|
38
|
+
export declare function mergeConnectParameters(urlParameters: Record<string, string>, appParameters?: Record<string, string>): Record<string, string>;
|
|
31
39
|
/**
|
|
32
40
|
* Options for the useIkonApp hook.
|
|
33
41
|
*/
|
|
@@ -43,6 +51,18 @@ export interface UseIkonAppOptions {
|
|
|
43
51
|
* modules: [registerStandardUiModule]
|
|
44
52
|
*/
|
|
45
53
|
modules?: IkonUiModuleRegistration[];
|
|
54
|
+
/**
|
|
55
|
+
* Connect parameters the app supplies itself, merged over the ones parsed from the URL.
|
|
56
|
+
*
|
|
57
|
+
* They reach the app twice: as the `params` the backend hashes into SessionIdentity, and as
|
|
58
|
+
* `Context.Parameters` on each client. Without this, the only way to steer SessionIdentity is to
|
|
59
|
+
* put a value in the address bar — which a frontend deriving state from its own environment (the
|
|
60
|
+
* hostname it was served on, a build-time flag) cannot do without rewriting the URL.
|
|
61
|
+
*
|
|
62
|
+
* App-supplied keys win over the same key in the query string, so a hand-typed parameter cannot
|
|
63
|
+
* override what the app resolved.
|
|
64
|
+
*/
|
|
65
|
+
parameters?: Record<string, string>;
|
|
46
66
|
/**
|
|
47
67
|
* Timeout configuration passed to IkonClient.
|
|
48
68
|
* If not provided, SDK defaults are used.
|
package/auth/auth-service.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AuthenticationResponseJSON, PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialRequestOptionsJSON, RegistrationResponseJSON } from '@simplewebauthn/browser';
|
|
2
|
-
import { AuthSession, LoginMethod } from './types';
|
|
2
|
+
import { AuthSession, LoginMethod, RefreshableSession } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* Result of parsing an OAuth callback from the URL.
|
|
5
5
|
*/
|
|
@@ -54,6 +54,31 @@ export declare function verifyLoginCode({ email, code, authUrl }: VerifyLoginCod
|
|
|
54
54
|
* Returns the token and provider if present, or null if not an OAuth callback.
|
|
55
55
|
*/
|
|
56
56
|
export declare function parseOAuthCallback(): OAuthCallbackResult | null;
|
|
57
|
+
/** The single-use code an opted-in callback carries, waiting to be exchanged for a session. */
|
|
58
|
+
export declare function parseOAuthCodeCallback(): {
|
|
59
|
+
code: string;
|
|
60
|
+
provider: LoginMethod;
|
|
61
|
+
} | null;
|
|
62
|
+
/**
|
|
63
|
+
* Trades the callback's code for the session it stands for, using the verifier this browser kept.
|
|
64
|
+
*
|
|
65
|
+
* A missing verifier is a real failure rather than something to paper over: it means this is not
|
|
66
|
+
* the browser (or not the tab) that started the sign-in — which is exactly the case PKCE exists to
|
|
67
|
+
* refuse — so the caller reports a failed sign-in instead of silently leaving the visitor
|
|
68
|
+
* anonymous with a code still in the address bar.
|
|
69
|
+
*/
|
|
70
|
+
export declare function exchangeOAuthCode(code: string, provider: LoginMethod, authUrlOverride?: string): Promise<AuthSession>;
|
|
71
|
+
/**
|
|
72
|
+
* Renews the access token from the refresh cookie. Returns null when the session is definitively
|
|
73
|
+
* over — the chain was spent, revoked, or never existed — and throws when the answer is simply
|
|
74
|
+
* unknown, so a caller can tell "signed out" from "the network is down" and only act on the first.
|
|
75
|
+
*
|
|
76
|
+
* Deliberately not routed through `authFetch`. The cookie is issued only when the exchange was
|
|
77
|
+
* answered same-origin, so the same-origin base is the only one that can hold it; authFetch's
|
|
78
|
+
* cross-origin fallback would send a cookie-less renewal to the shared auth host, take the 401 for
|
|
79
|
+
* a dead session, and sign out someone whose session was never in trouble.
|
|
80
|
+
*/
|
|
81
|
+
export declare function refreshBrowserSession(marker: RefreshableSession): Promise<AuthSession | null>;
|
|
57
82
|
/**
|
|
58
83
|
* Sign-in failure parsed from an OAuth callback redirect: the machine code from the `error` query
|
|
59
84
|
* param, plus the provider the callback names when it knows which one failed.
|
|
@@ -98,7 +123,7 @@ export declare function clearOAuthParams(): void;
|
|
|
98
123
|
/**
|
|
99
124
|
* Build an OAuth redirect URL for the given provider.
|
|
100
125
|
*/
|
|
101
|
-
export declare function buildOAuthRedirectUrl(provider: LoginMethod, spaceId: string, authUrl: string, returnUrl?: string): string
|
|
126
|
+
export declare function buildOAuthRedirectUrl(provider: LoginMethod, spaceId: string, authUrl: string, returnUrl?: string): Promise<string>;
|
|
102
127
|
/**
|
|
103
128
|
* Check if passkey/WebAuthn is supported in the current browser.
|
|
104
129
|
*/
|
|
@@ -119,3 +144,13 @@ export declare function getPasskeyAuthenticationOptions(authUrl: string, email?:
|
|
|
119
144
|
* Verify passkey authentication with the auth service.
|
|
120
145
|
*/
|
|
121
146
|
export declare function verifyPasskeyAuthentication(authUrl: string, response: AuthenticationResponseJSON): Promise<AuthSession>;
|
|
147
|
+
/**
|
|
148
|
+
* Ends the session on the server as well as locally, so a copy of the token taken from the URL or
|
|
149
|
+
* from storage stops working instead of outliving the sign-out by up to a week.
|
|
150
|
+
*
|
|
151
|
+
* Best effort by design: logging out must succeed even offline, against an older backend with no
|
|
152
|
+
* such route, or when the request is simply slow. The local session is cleared by the caller
|
|
153
|
+
* regardless — a client that cannot reach the server ends up exactly where it was before this
|
|
154
|
+
* existed, which is also where app bundles shipping an older SDK copy stay.
|
|
155
|
+
*/
|
|
156
|
+
export declare function revokeSessionOnServer(token: string, authUrlOverride?: string): Promise<void>;
|
package/auth/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type { AuthConfig, AuthContextValue, AuthSession, AuthState, AuthUser, LoginMethod } from './types';
|
|
2
2
|
export { clearAuthSession, isSignedInBootLikely, loadAuthSession, saveAuthSession, sessionToUser } from './storage';
|
|
3
|
-
export { authenticateAnonymous, buildOAuthRedirectUrl, clearOAuthParams, describeAuthError, getPasskeyAuthenticationOptions, getPasskeyRegistrationOptions, isPasskeySupported, parseOAuthCallback, parseOAuthError, sendLoginCode, verifyLoginCode, verifyPasskeyAuthentication, verifyPasskeyRegistration, type OAuthCallbackResult, type OAuthErrorResult, type SendLoginCodeOptions, type VerifyLoginCodeOptions, } from './auth-service';
|
|
3
|
+
export { authenticateAnonymous, buildOAuthRedirectUrl, clearOAuthParams, describeAuthError, exchangeOAuthCode, getPasskeyAuthenticationOptions, getPasskeyRegistrationOptions, isPasskeySupported, parseOAuthCallback, parseOAuthCodeCallback, parseOAuthError, revokeSessionOnServer, sendLoginCode, verifyLoginCode, verifyPasskeyAuthentication, verifyPasskeyRegistration, type OAuthCallbackResult, type OAuthErrorResult, type SendLoginCodeOptions, type VerifyLoginCodeOptions, } from './auth-service';
|
|
4
4
|
export { AuthProvider, useAuth, useAuthOptional, type AuthProviderProps } from './auth-context';
|
|
5
5
|
export { useAuthGuard, type UseAuthGuardOptions, type UseAuthGuardResult } from './use-auth-guard';
|
|
6
|
+
export { OAUTH_CONTINUE_PARAM, captureOAuthContinuation, completeOAuthContinuation, hasPendingOAuthContinuation } from './oauth-continuation';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** What `/ikon/oauth/authorize` puts on the app's own URL when it sends the browser here to sign in. */
|
|
2
|
+
export declare const OAUTH_CONTINUE_PARAM = "ikon-oauth";
|
|
3
|
+
/**
|
|
4
|
+
* The app-rendered half of a space's OAuth flow.
|
|
5
|
+
*
|
|
6
|
+
* `/authorize` cannot show a sign-in screen itself — the app owns that screen, its methods and its
|
|
7
|
+
* branding, and the person is being asked to grant access to *this app*, so this app is what they should
|
|
8
|
+
* be looking at while they decide. So it parks the request and bounces the browser back here with a
|
|
9
|
+
* pointer to it; the SDK raises the login prompt it already has; and a completed sign-in reports back.
|
|
10
|
+
*
|
|
11
|
+
* The pointer is kept in BOTH the URL and sessionStorage until the flow ends, and that redundancy is
|
|
12
|
+
* the point. Signing in with an external provider hands the browser to another origin and back, and
|
|
13
|
+
* what comes back is not always the same browsing context — a popup, a reopened tab, a session-storage
|
|
14
|
+
* partition we did not choose. Storage alone lost the pointer in exactly that case, and because the
|
|
15
|
+
* param had already been stripped there was nothing left to recover it from: the authorization sat
|
|
16
|
+
* parked, the client waited for a code forever, and the user saw an ordinary app with no sign of it.
|
|
17
|
+
*
|
|
18
|
+
* The URL copy is what survives, because the sign-in return URL is built from the current location.
|
|
19
|
+
* That is safe here for a reason this flow states explicitly elsewhere: the pointer is NOT a
|
|
20
|
+
* credential. It names a parked authorization, and the user binding is made server-side from the
|
|
21
|
+
* session token posted below, so a copied or referrer-leaked pointer authorizes nobody. It is cleared
|
|
22
|
+
* from both places the moment the flow finishes or fails.
|
|
23
|
+
*/
|
|
24
|
+
export declare function captureOAuthContinuation(): boolean;
|
|
25
|
+
export declare function hasPendingOAuthContinuation(): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Report the completed sign-in and hand the browser to the consent screen.
|
|
28
|
+
*
|
|
29
|
+
* Only ever called after a REAL sign-in — `useAuthGuard` treats an anonymous session as not satisfying a
|
|
30
|
+
* prompt, which is the guest/global exclusion this flow needs and already had. A `global` visitor is one
|
|
31
|
+
* shared space-wide user, so authorizing as one would hand every client the same identity.
|
|
32
|
+
*
|
|
33
|
+
* Failure clears the pointer and returns false: the app then carries on as a normally signed-in session
|
|
34
|
+
* rather than trapping the user in a flow that cannot finish.
|
|
35
|
+
*/
|
|
36
|
+
export declare function completeOAuthContinuation(): Promise<boolean>;
|
package/auth/storage.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AuthSession, AuthUser } from './types';
|
|
1
|
+
import { AuthSession, AuthUser, RefreshableSession } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Check whether a JWT token has expired by decoding its `exp` claim.
|
|
4
4
|
* Returns true when the token is expired or cannot be decoded.
|
|
@@ -13,6 +13,30 @@ export declare function saveAuthSession(session: AuthSession): void;
|
|
|
13
13
|
* Returns null if no session exists or if the session has expired.
|
|
14
14
|
*/
|
|
15
15
|
export declare function loadAuthSession(): AuthSession | null;
|
|
16
|
+
/**
|
|
17
|
+
* Record that this browser has a refresh cookie behind the given sign-in. Replaces the stored
|
|
18
|
+
* session rather than joining it: the two describe the same thing, and a leftover token record
|
|
19
|
+
* would be preferred over the live one on the next load.
|
|
20
|
+
*/
|
|
21
|
+
export declare function saveRefreshableSession(session: RefreshableSession): void;
|
|
22
|
+
/**
|
|
23
|
+
* The refresh-cookie marker, or null when there is none or it is old enough that the cookie behind
|
|
24
|
+
* it has certainly expired. Dropping a stale one is what stops a long-abandoned browser starting
|
|
25
|
+
* every load with a renewal that cannot succeed.
|
|
26
|
+
*/
|
|
27
|
+
export declare function loadRefreshableSession(): RefreshableSession | null;
|
|
28
|
+
/** Move the marker's renewal stamp forward, mirroring the cookie the server just replaced. */
|
|
29
|
+
export declare function touchRefreshableSession(): void;
|
|
30
|
+
export declare function clearRefreshableSession(): void;
|
|
31
|
+
/**
|
|
32
|
+
* Drop a stored anonymous session — token record and refresh marker alike.
|
|
33
|
+
*
|
|
34
|
+
* For an app whose sign-in wall offers no not-signed-in entry. Such a session can only date from a
|
|
35
|
+
* time when one was offered, or from another app sharing the origin, and it must not be what walks
|
|
36
|
+
* a visitor through the wall. Discarded rather than ignored: a session the app will not honour has
|
|
37
|
+
* no business leaving a usable token in storage.
|
|
38
|
+
*/
|
|
39
|
+
export declare function discardAnonymousSession(): void;
|
|
16
40
|
/**
|
|
17
41
|
* Pre-auth heuristic for "this load will connect as a signed-in user": a stored non-anonymous
|
|
18
42
|
* session, or an OAuth callback in flight (the provider just redirected back with `ikon_token` in
|
package/auth/types.d.ts
CHANGED
|
@@ -15,6 +15,12 @@ export interface AuthUser {
|
|
|
15
15
|
provider: LoginMethod | 'anonymous' | 'dev';
|
|
16
16
|
token: string;
|
|
17
17
|
authenticatedAt: number;
|
|
18
|
+
/**
|
|
19
|
+
* When {@link token} stops being accepted, for the sign-ins whose token is short-lived enough to
|
|
20
|
+
* need renewing before then. Absent on the long-lived tokens, which are simply used until they
|
|
21
|
+
* expire.
|
|
22
|
+
*/
|
|
23
|
+
expiresAt?: string;
|
|
18
24
|
}
|
|
19
25
|
/**
|
|
20
26
|
* Authentication state.
|
|
@@ -64,6 +70,19 @@ export interface AuthSession {
|
|
|
64
70
|
token: string;
|
|
65
71
|
provider: LoginMethod | 'anonymous' | 'dev';
|
|
66
72
|
authenticatedAt: number;
|
|
73
|
+
/** Set when the token is short-lived and a refresh cookie stands behind it. */
|
|
74
|
+
expiresAt?: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* What is persisted for a sign-in whose access token is NOT persisted: enough to know on the next
|
|
78
|
+
* load that a session exists and whose it is, and nothing that can be used as a credential.
|
|
79
|
+
*/
|
|
80
|
+
export interface RefreshableSession {
|
|
81
|
+
provider: LoginMethod | 'anonymous' | 'dev';
|
|
82
|
+
/** When the person actually signed in. Unchanged by renewals — those renew a token, not a login. */
|
|
83
|
+
authenticatedAt: number;
|
|
84
|
+
/** Last successful renewal, which is what the cookie's own lifetime runs from. */
|
|
85
|
+
renewedAt: number;
|
|
67
86
|
}
|
|
68
87
|
/**
|
|
69
88
|
* Auth context value returned by useAuth hook.
|
package/auth/use-auth-guard.d.ts
CHANGED
|
@@ -37,6 +37,13 @@ export interface UseAuthGuardResult {
|
|
|
37
37
|
* Dismiss an on-demand login prompt and return to the app. No effect on a hard sign-in wall.
|
|
38
38
|
*/
|
|
39
39
|
dismissLoginPrompt: () => void;
|
|
40
|
+
/**
|
|
41
|
+
* Why the prompt was raised, when whoever raised it said. Render it: a sign-in screen that appears
|
|
42
|
+
* without explanation is one a person cannot judge. Someone sent here by an application asking to
|
|
43
|
+
* act on their behalf needs to know that is what they are agreeing to before they choose an account,
|
|
44
|
+
* not after.
|
|
45
|
+
*/
|
|
46
|
+
loginPromptReason: string | null;
|
|
40
47
|
}
|
|
41
48
|
/**
|
|
42
49
|
* Headless hook for route protection logic.
|