@flow-industries/id 0.12.0 → 0.14.0
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/dist/sdk/client/create-flow.js +53 -2
- package/dist/sdk/client/index.d.ts +1 -1
- package/dist/sdk/client/session.js +2 -1
- package/dist/sdk/client/static-flow.js +1 -0
- package/dist/sdk/session-route.js +29 -0
- package/dist/sdk/start/index.d.ts +6 -2
- package/dist/sdk/start/index.js +8 -2
- package/dist/sdk/types/auth.d.ts +13 -0
- package/dist/sdk/types/events.d.ts +2 -2
- package/dist/sdk/types/index.d.ts +1 -1
- package/dist/sdk/types/protocol.d.ts +12 -2
- package/dist/sdk/types/sdk.d.ts +29 -1
- package/dist/sdk/types/server.d.ts +7 -1
- package/package.json +1 -1
|
@@ -151,6 +151,46 @@ export function createFlow(options = {}) {
|
|
|
151
151
|
return false;
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Silently mints a session for an additional audience via this app's own
|
|
156
|
+
* session route (which authorizes it with the primary refresh cookie and
|
|
157
|
+
* proxies to the issuer). The consumer calls this only when its embed for
|
|
158
|
+
* `audience` reports it has no session, so nothing is minted for an audience
|
|
159
|
+
* that already holds a live cookie. Returns null when there's no primary
|
|
160
|
+
* session or the audience isn't provisioned/allowed for this app.
|
|
161
|
+
*/
|
|
162
|
+
async function mintAudience(audience) {
|
|
163
|
+
try {
|
|
164
|
+
const res = await fetch(sessionPath, {
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers: { "Content-Type": "application/json" },
|
|
167
|
+
body: JSON.stringify({ mint: [audience] }),
|
|
168
|
+
});
|
|
169
|
+
if (!res.ok)
|
|
170
|
+
return null;
|
|
171
|
+
const { additionalSessions } = (await res.json());
|
|
172
|
+
return additionalSessions?.find((s) => s.audience === audience) ?? null;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* One-time delivery of any additional-audience sessions a mint returned
|
|
180
|
+
* (AUTH-39). Handed straight to the consumer's callback and dropped — the
|
|
181
|
+
* SDK retains nothing; the consumer forwards each to its audience's own
|
|
182
|
+
* origin (the embed bootstrap) to be sealed HttpOnly there.
|
|
183
|
+
*/
|
|
184
|
+
function deliverAdditionalSessions(sessions) {
|
|
185
|
+
if (!sessions?.length || !options.onAdditionalSessions)
|
|
186
|
+
return;
|
|
187
|
+
try {
|
|
188
|
+
options.onAdditionalSessions(sessions);
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
console.warn("Flow ID: onAdditionalSessions handler threw", err);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
154
194
|
/**
|
|
155
195
|
* One-time handoff of a freshly minted session (dialog login or guest
|
|
156
196
|
* mint) to the app's server, which verifies the JWT against the issuer's
|
|
@@ -247,7 +287,9 @@ export function createFlow(options = {}) {
|
|
|
247
287
|
if (await doRefresh())
|
|
248
288
|
return true;
|
|
249
289
|
try {
|
|
250
|
-
const result = await getDialog().requestSilent(METHODS.guest,
|
|
290
|
+
const result = await getDialog().requestSilent(METHODS.guest, options.additionalAudiences?.length
|
|
291
|
+
? [{ additionalAudiences: options.additionalAudiences }]
|
|
292
|
+
: []);
|
|
251
293
|
if (!result.user)
|
|
252
294
|
return false;
|
|
253
295
|
store.setState({
|
|
@@ -259,6 +301,7 @@ export function createFlow(options = {}) {
|
|
|
259
301
|
...(result.user.isGuest ? { credential: null, address: null } : {}),
|
|
260
302
|
});
|
|
261
303
|
await installSession(result.refreshToken, result.jwt);
|
|
304
|
+
deliverAdditionalSessions(result.additionalSessions);
|
|
262
305
|
return true;
|
|
263
306
|
}
|
|
264
307
|
catch {
|
|
@@ -336,13 +379,20 @@ export function createFlow(options = {}) {
|
|
|
336
379
|
extraCapabilities = { ...(extraCapabilities ?? {}), guestToken };
|
|
337
380
|
}
|
|
338
381
|
}
|
|
339
|
-
|
|
382
|
+
if (options.additionalAudiences?.length) {
|
|
383
|
+
extraCapabilities = {
|
|
384
|
+
...(extraCapabilities ?? {}),
|
|
385
|
+
additionalAudiences: options.additionalAudiences,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
const { session, webauthn, refreshToken, additionalSessions } = await runLogin({
|
|
340
389
|
dialog: dialogHost,
|
|
341
390
|
store,
|
|
342
391
|
options: loginOpts,
|
|
343
392
|
...(extraCapabilities ? { extraCapabilities } : {}),
|
|
344
393
|
});
|
|
345
394
|
await installSession(refreshToken, session.jwt);
|
|
395
|
+
deliverAdditionalSessions(additionalSessions);
|
|
346
396
|
if (accessKeyModule && accessKeyPrep && webauthn) {
|
|
347
397
|
await accessKeyModule.finalizeAccessKey({
|
|
348
398
|
address: session.address,
|
|
@@ -410,6 +460,7 @@ export function createFlow(options = {}) {
|
|
|
410
460
|
ensureGuest,
|
|
411
461
|
refreshJwt,
|
|
412
462
|
getToken,
|
|
463
|
+
mintAudience,
|
|
413
464
|
rooms: createRoomsApi(host, getToken),
|
|
414
465
|
signMessage: async (args) => {
|
|
415
466
|
const mod = await import("./signing");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { defaultIdHost, isLocalHostname } from "../id-host";
|
|
2
|
-
export type { AccessKeyOptions, Address, ConnectCapabilities, ConnectResponse, CreateFlowOptions, DialogHost, Flow, FlowCredential, FlowSessionState, FlowState, FlowUser, LoginOptions, MethodName, MountProfileOptions, ProfileButtonHandle, ProfilePosition, Session, } from "../types";
|
|
2
|
+
export type { AccessKeyOptions, AdditionalSession, Address, ConnectCapabilities, ConnectResponse, CreateFlowOptions, DialogHost, Flow, FlowCredential, FlowSessionState, FlowState, FlowUser, LoginOptions, MethodName, MountProfileOptions, ProfileButtonHandle, ProfilePosition, Session, } from "../types";
|
|
3
3
|
export { createFlow, getFlow, requireFlow, resetFlow } from "./create-flow";
|
|
4
4
|
export { createDialogHost } from "./dialog-host";
|
|
5
5
|
export { METHODS } from "./methods";
|
|
@@ -40,7 +40,7 @@ export async function runLogin(params) {
|
|
|
40
40
|
const result = (await dialog.request(METHODS.connect, [
|
|
41
41
|
{ capabilities },
|
|
42
42
|
]));
|
|
43
|
-
const { jwt, refreshToken, user, credential, webauthn } = result;
|
|
43
|
+
const { jwt, refreshToken, user, credential, webauthn, additionalSessions } = result;
|
|
44
44
|
const address = credentialToAddress(credential);
|
|
45
45
|
await idb.set(CREDENTIAL_KEY, credential);
|
|
46
46
|
store.setState({ user, jwt, credential, address });
|
|
@@ -48,6 +48,7 @@ export async function runLogin(params) {
|
|
|
48
48
|
session: { user, jwt, credential, address },
|
|
49
49
|
refreshToken,
|
|
50
50
|
webauthn,
|
|
51
|
+
...(additionalSessions ? { additionalSessions } : {}),
|
|
51
52
|
};
|
|
52
53
|
}
|
|
53
54
|
/**
|
|
@@ -49,6 +49,7 @@ export function createStaticFlow(state, options = {}) {
|
|
|
49
49
|
ensureGuest: () => unavailable("ensureGuest"),
|
|
50
50
|
refreshJwt: async () => false,
|
|
51
51
|
getToken,
|
|
52
|
+
mintAudience: async () => null,
|
|
52
53
|
rooms: createRoomsApi(host, getToken),
|
|
53
54
|
signMessage: () => unavailable("signMessage"),
|
|
54
55
|
signTypedData: () => unavailable("signTypedData"),
|
|
@@ -66,6 +66,35 @@ export async function handleSessionRequest(request, opts = {}) {
|
|
|
66
66
|
}
|
|
67
67
|
case "POST": {
|
|
68
68
|
const body = (await request.json().catch(() => null));
|
|
69
|
+
// Reconcile mint: the browser asks its own server to obtain additional-
|
|
70
|
+
// audience sessions (e.g. talk.flow.game) authorized by the PRIMARY
|
|
71
|
+
// refresh cookie, for an embed that booted with no session of its own.
|
|
72
|
+
// The refresh token never touches page JS — it's read from the HttpOnly
|
|
73
|
+
// cookie here and presented to the issuer as a bearer.
|
|
74
|
+
if (Array.isArray(body?.mint)) {
|
|
75
|
+
const audiences = body.mint.filter((a) => typeof a === "string");
|
|
76
|
+
const names = cookieNamesFor(audience);
|
|
77
|
+
const token = parseCookieHeader(request.headers.get("Cookie"))[names.refresh];
|
|
78
|
+
let additionalSessions = [];
|
|
79
|
+
if (token && audiences.length > 0) {
|
|
80
|
+
try {
|
|
81
|
+
const res = await fetch(`${issuerUrl}/api/session/additional`, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: {
|
|
84
|
+
"Content-Type": "application/json",
|
|
85
|
+
Authorization: `Bearer ${token}`,
|
|
86
|
+
},
|
|
87
|
+
body: JSON.stringify({ audiences }),
|
|
88
|
+
});
|
|
89
|
+
if (res.ok) {
|
|
90
|
+
const data = (await res.json());
|
|
91
|
+
additionalSessions = data.additionalSessions ?? [];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch { }
|
|
95
|
+
}
|
|
96
|
+
return json({ state: null, additionalSessions }, 200);
|
|
97
|
+
}
|
|
69
98
|
const refreshToken = body?.refreshToken;
|
|
70
99
|
const jwt = body?.jwt;
|
|
71
100
|
if (typeof refreshToken !== "string" ||
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* into the server bundle there; everything it does lives here.
|
|
22
22
|
*/
|
|
23
23
|
import { type ReactNode } from "react";
|
|
24
|
-
import type { FlowSessionState } from "../types";
|
|
24
|
+
import type { AdditionalSession, FlowSessionState } from "../types";
|
|
25
25
|
/**
|
|
26
26
|
* The `beforeLoad` body: during SSR resolve the session server-side (via the
|
|
27
27
|
* app's `getFlowSession` server fn, which appends rotated Set-Cookie values);
|
|
@@ -37,6 +37,10 @@ export type FlowRootProps = {
|
|
|
37
37
|
autoGuest?: boolean;
|
|
38
38
|
/** Override the Flow ID origin; defaults per `defaultIdHost`. */
|
|
39
39
|
host?: string;
|
|
40
|
+
/** Extra audiences every mint requests (see `CreateFlowOptions`). */
|
|
41
|
+
additionalAudiences?: readonly string[];
|
|
42
|
+
/** One-time delivery of the extra sessions (see `CreateFlowOptions`). */
|
|
43
|
+
onAdditionalSessions?: (sessions: AdditionalSession[]) => void;
|
|
40
44
|
children: ReactNode;
|
|
41
45
|
};
|
|
42
46
|
/**
|
|
@@ -45,4 +49,4 @@ export type FlowRootProps = {
|
|
|
45
49
|
* matches the SSR HTML and no bootstrap refresh runs while the hydrated JWT
|
|
46
50
|
* is fresh.
|
|
47
51
|
*/
|
|
48
|
-
export declare function FlowRoot({ session, autoGuest, host, children, }: FlowRootProps): import("react/jsx-runtime").JSX.Element;
|
|
52
|
+
export declare function FlowRoot({ session, autoGuest, host, additionalAudiences, onAdditionalSessions, children, }: FlowRootProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/sdk/start/index.js
CHANGED
|
@@ -44,9 +44,15 @@ export async function flowSessionContext(getSession) {
|
|
|
44
44
|
* matches the SSR HTML and no bootstrap refresh runs while the hydrated JWT
|
|
45
45
|
* is fresh.
|
|
46
46
|
*/
|
|
47
|
-
export function FlowRoot({ session, autoGuest, host, children, }) {
|
|
47
|
+
export function FlowRoot({ session, autoGuest, host, additionalAudiences, onAdditionalSessions, children, }) {
|
|
48
48
|
const flow = useMemo(() => typeof window === "undefined"
|
|
49
49
|
? createStaticFlow(session, { host })
|
|
50
|
-
: createFlow({
|
|
50
|
+
: createFlow({
|
|
51
|
+
initialState: session,
|
|
52
|
+
autoGuest,
|
|
53
|
+
host,
|
|
54
|
+
additionalAudiences,
|
|
55
|
+
onAdditionalSessions,
|
|
56
|
+
}), [session, autoGuest, host, additionalAudiences, onAdditionalSessions]);
|
|
51
57
|
return _jsx(FlowIdProvider, { flow: flow, children: children });
|
|
52
58
|
}
|
package/dist/sdk/types/auth.d.ts
CHANGED
|
@@ -14,11 +14,24 @@ export type FlowCredential = {
|
|
|
14
14
|
id: string;
|
|
15
15
|
publicKey: string;
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* A session minted for one of the ADDITIONAL audiences a login requested
|
|
19
|
+
* (AUTH-39): its own refresh-token lineage and access JWT, both bound to
|
|
20
|
+
* `audience`. Installed once into that origin's own HttpOnly cookies via its
|
|
21
|
+
* `/flow/session` route (the embed bootstrap) — never held by page JS beyond
|
|
22
|
+
* the one-time handoff.
|
|
23
|
+
*/
|
|
24
|
+
export type AdditionalSession = {
|
|
25
|
+
audience: string;
|
|
26
|
+
jwt: string;
|
|
27
|
+
refreshToken: string;
|
|
28
|
+
};
|
|
17
29
|
export type AuthResponse = {
|
|
18
30
|
jwt: string;
|
|
19
31
|
refreshToken: string;
|
|
20
32
|
user: FlowUser;
|
|
21
33
|
credential: FlowCredential;
|
|
34
|
+
additionalSessions?: AdditionalSession[];
|
|
22
35
|
};
|
|
23
36
|
export type AuthConfig = {
|
|
24
37
|
rpId: string;
|
|
@@ -8,8 +8,8 @@ export type AuthOutcome = "success" | "failure" | "info";
|
|
|
8
8
|
export type AuthMode = "sign-up" | "sign-in";
|
|
9
9
|
/** Client-only funnel steps reported via the `/api/events` beacon. */
|
|
10
10
|
export type FunnelStep = "mode_selected" | "email_entered" | "ceremony_started" | "done_shown";
|
|
11
|
-
export type AuthEventName = "auth.username.checked" | "auth.signup.succeeded" | "auth.signup.failed" | "auth.otp.sent" | "auth.otp.verified" | "auth.otp.failed" | "auth.email.verify.sent" | "auth.email.verify.succeeded" | "auth.email.verify.failed" | "auth.email.changed" | "auth.avatar.upload.succeeded" | "auth.avatar.upload.failed" | "auth.challenge.issued" | "auth.signin.succeeded" | "auth.signin.failed" | "auth.restore.succeeded" | "auth.restore.failed" | "auth.restore.no_session" | "auth.refresh.succeeded" | "auth.refresh.failed" | "auth.refresh.reuse" | "auth.refresh.grace" | "auth.jwt.verified" | "auth.jwt.rejected" | "auth.signout" | "auth.audience.rejected" | "auth.guest.created" | "auth.guest.restored" | "auth.guest.upgraded" | "auth.guest.failed" | "auth.funnel.mode_selected" | "auth.funnel.email_entered" | "auth.funnel.ceremony_started" | "auth.funnel.done_shown";
|
|
12
|
-
export type AuthErrorCode = "username_taken" | "credential_taken" | "email_taken" | "email_not_verified" | "no_email" | "image_upload_forbidden" | "image_upload_rate_limited" | "invalid_image_type" | "image_too_large" | "image_upload_failed" | "otp_invalid" | "otp_expired" | "otp_attempts_exceeded" | "otp_resend_cooldown" | "otp_resend_limit" | "otp_global_limit" | "otp_send_failed" | "challenge_expired" | "unknown_credential" | "invalid_assertion_type" | "invalid_assertion_origin" | "user_verification_required" | "invalid_signature" | "user_not_found" | "missing_audience" | "audience_not_allowed" | "audience_mismatch" | "no_session" | "no_passkey" | "refresh_token_invalid" | "refresh_token_expired" | "refresh_reuse_detected" | "guest_rate_limited" | "guest_global_limit" | "guest_username_exhausted" | "already_upgraded" | "malformed_token" | "unknown_key" | "verification_failed" | "internal_error";
|
|
11
|
+
export type AuthEventName = "auth.username.checked" | "auth.signup.succeeded" | "auth.signup.failed" | "auth.otp.sent" | "auth.otp.verified" | "auth.otp.failed" | "auth.email.verify.sent" | "auth.email.verify.succeeded" | "auth.email.verify.failed" | "auth.email.changed" | "auth.avatar.upload.succeeded" | "auth.avatar.upload.failed" | "auth.challenge.issued" | "auth.signin.succeeded" | "auth.signin.failed" | "auth.restore.succeeded" | "auth.restore.failed" | "auth.restore.no_session" | "auth.refresh.succeeded" | "auth.refresh.failed" | "auth.refresh.reuse" | "auth.refresh.grace" | "auth.jwt.verified" | "auth.jwt.rejected" | "auth.signout" | "auth.audience.rejected" | "auth.audience.minted" | "auth.guest.created" | "auth.guest.restored" | "auth.guest.upgraded" | "auth.guest.failed" | "auth.funnel.mode_selected" | "auth.funnel.email_entered" | "auth.funnel.ceremony_started" | "auth.funnel.done_shown";
|
|
12
|
+
export type AuthErrorCode = "username_taken" | "credential_taken" | "email_taken" | "email_not_verified" | "no_email" | "image_upload_forbidden" | "image_upload_rate_limited" | "invalid_image_type" | "image_too_large" | "image_upload_failed" | "otp_invalid" | "otp_expired" | "otp_attempts_exceeded" | "otp_resend_cooldown" | "otp_resend_limit" | "otp_global_limit" | "otp_send_failed" | "challenge_expired" | "unknown_credential" | "invalid_assertion_type" | "invalid_assertion_origin" | "user_verification_required" | "invalid_signature" | "user_not_found" | "missing_audience" | "audience_not_allowed" | "audience_mismatch" | "no_session" | "no_passkey" | "refresh_token_invalid" | "refresh_token_expired" | "refresh_epoch_stale" | "refresh_reuse_detected" | "guest_rate_limited" | "guest_global_limit" | "guest_username_exhausted" | "already_upgraded" | "malformed_token" | "unknown_key" | "verification_failed" | "internal_error";
|
|
13
13
|
/** One flat record per event = one row in the `auth_events` stream. */
|
|
14
14
|
export interface AuthEventRecord {
|
|
15
15
|
service: "auth";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { AuthConfig, AuthResponse, AuthResponseWithWebAuthn, FlowCredential, FlowUser, PasskeyPluginOptions, VerifiedFlowJWT, VerifyOptions, WebAuthnSignature, } from "./auth";
|
|
1
|
+
export type { AdditionalSession, AuthConfig, AuthResponse, AuthResponseWithWebAuthn, FlowCredential, FlowUser, PasskeyPluginOptions, VerifiedFlowJWT, VerifyOptions, WebAuthnSignature, } from "./auth";
|
|
2
2
|
export type { BoundaryError, DialogCustomFeatures, DialogCustomLabels, DialogError, DialogReferrer, DialogState, ProfileData, ProfileIdentity, } from "./dialog";
|
|
3
3
|
export type { AuthErrorCode, AuthEventName, AuthEventRecord, AuthMode, AuthOutcome, BeaconBody, FunnelStep, } from "./events";
|
|
4
4
|
export type { Bridge, BridgeParameters, FlowAccount, FlowRemote, FlowRemoteConfig, FromWindowOptions, MessageResponse, Messenger, OneOf, Payload, QueuedRequest, ReadyOptions, RemoteFlowState, RemoteState, Schema, Storage, Topic, WithReady, } from "./messenger";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FlowCredential, FlowUser, WebAuthnSignature } from "./auth";
|
|
1
|
+
import type { AdditionalSession, FlowCredential, FlowUser, WebAuthnSignature } from "./auth";
|
|
2
2
|
import type { Address } from "./sdk";
|
|
3
3
|
export type MethodName = "wallet_connect" | "personal_sign" | "eth_signTypedData" | "eth_sendTransaction" | "wallet_sendCalls" | "wallet_guest" | "wallet_signout";
|
|
4
4
|
export type ConnectCapabilities = {
|
|
@@ -14,6 +14,12 @@ export type ConnectCapabilities = {
|
|
|
14
14
|
* cookie session; see the register endpoint.
|
|
15
15
|
*/
|
|
16
16
|
guestToken?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Extra audiences to mint refresh tokens for in the same login (AUTH-39).
|
|
19
|
+
* The server refuses any entry outside its explicit origin -> audiences
|
|
20
|
+
* allowlist; allowed entries come back as `additionalSessions`.
|
|
21
|
+
*/
|
|
22
|
+
additionalAudiences?: readonly string[];
|
|
17
23
|
};
|
|
18
24
|
export type ConnectRequest = [{
|
|
19
25
|
capabilities?: ConnectCapabilities;
|
|
@@ -25,6 +31,7 @@ export type ConnectResponse = {
|
|
|
25
31
|
user: FlowUser;
|
|
26
32
|
credential: FlowCredential;
|
|
27
33
|
webauthn?: WebAuthnSignature;
|
|
34
|
+
additionalSessions?: AdditionalSession[];
|
|
28
35
|
};
|
|
29
36
|
export type SignMessageRequest = [message: string, address: string];
|
|
30
37
|
export type SignMessageResponse = `0x${string}`;
|
|
@@ -74,7 +81,9 @@ export type RestoreResponse = {
|
|
|
74
81
|
credential: FlowCredential | null;
|
|
75
82
|
address: Address | null;
|
|
76
83
|
};
|
|
77
|
-
export type GuestRequest = []
|
|
84
|
+
export type GuestRequest = [] | [{
|
|
85
|
+
additionalAudiences?: readonly string[];
|
|
86
|
+
}];
|
|
78
87
|
/**
|
|
79
88
|
* A guest has no passkey and no derivable wallet address yet, so `credential`
|
|
80
89
|
* and `address` are always null — the shape mirrors RestoreResponse so the SDK
|
|
@@ -86,6 +95,7 @@ export type GuestResponse = {
|
|
|
86
95
|
user: FlowUser;
|
|
87
96
|
credential: null;
|
|
88
97
|
address: null;
|
|
98
|
+
additionalSessions?: AdditionalSession[];
|
|
89
99
|
};
|
|
90
100
|
export type SignOutRequest = [];
|
|
91
101
|
export type SignOutResponse = {
|
package/dist/sdk/types/sdk.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { Chain, Hex, PrepareTransactionRequestParameters, SendTransactionParameters, SignTypedDataParameters, Transport, WalletClient } from "viem";
|
|
3
3
|
import type { SendCallsParameters } from "viem/actions";
|
|
4
|
-
import type { FlowCredential, FlowUser, WebAuthnSignature } from "./auth";
|
|
4
|
+
import type { AdditionalSession, FlowCredential, FlowUser, WebAuthnSignature } from "./auth";
|
|
5
5
|
import type { ConnectCapabilities, ConnectResponse } from "./protocol";
|
|
6
6
|
import type { RoomsApi } from "./rooms";
|
|
7
7
|
export type Address = `0x${string}`;
|
|
@@ -88,6 +88,22 @@ export type CreateFlowOptions = {
|
|
|
88
88
|
* bootstrap.
|
|
89
89
|
*/
|
|
90
90
|
initialState?: FlowSessionState | null;
|
|
91
|
+
/**
|
|
92
|
+
* Extra audiences every dialog mint (login, guest) requests refresh tokens
|
|
93
|
+
* for, on top of the app's own (AUTH-39). Only audiences the auth server
|
|
94
|
+
* explicitly allowlists for this origin succeed — anything else fails the
|
|
95
|
+
* mint. The minted sessions are delivered once via `onAdditionalSessions`
|
|
96
|
+
* and never stored; the app forwards each to its own origin (e.g. the
|
|
97
|
+
* same-site chat embed's `/flow/session`) to be sealed HttpOnly.
|
|
98
|
+
*/
|
|
99
|
+
additionalAudiences?: readonly string[];
|
|
100
|
+
/**
|
|
101
|
+
* One-time delivery of the additional-audience sessions minted at login or
|
|
102
|
+
* guest creation. Fired after the app's own session installs; the receiver
|
|
103
|
+
* must hand each session to its audience's origin (the embed bootstrap) and
|
|
104
|
+
* drop it — nothing here is retained by the SDK.
|
|
105
|
+
*/
|
|
106
|
+
onAdditionalSessions?: (sessions: AdditionalSession[]) => void;
|
|
91
107
|
};
|
|
92
108
|
export type FlowState = {
|
|
93
109
|
user: FlowUser | null;
|
|
@@ -234,6 +250,17 @@ export type Flow = {
|
|
|
234
250
|
* Prefer this over reading `jwt` directly when calling a backend.
|
|
235
251
|
*/
|
|
236
252
|
getToken(): Promise<string | null>;
|
|
253
|
+
/**
|
|
254
|
+
* Silently obtains a session for an ADDITIONAL audience (e.g. an in-game
|
|
255
|
+
* chat embed's `talk.flow.game`) authorized by this app's primary cookie,
|
|
256
|
+
* for the consumer to install into that audience's own origin. Returns null
|
|
257
|
+
* when there is no primary session or the audience isn't provisioned for this
|
|
258
|
+
* app. Nothing is minted unless called — a consumer calls it only when its
|
|
259
|
+
* embed reports it has no session, so an audience that already holds a live
|
|
260
|
+
* cookie is never re-minted. This is the reconcile path that heals a
|
|
261
|
+
* returning/guest user whose extra-audience cookie is missing or was retired.
|
|
262
|
+
*/
|
|
263
|
+
mintAudience(audience: string): Promise<AdditionalSession | null>;
|
|
237
264
|
signMessage(args: SignMessageArgs): Promise<Hex>;
|
|
238
265
|
signTypedData(args: SignTypedDataArgs): Promise<Hex>;
|
|
239
266
|
sendTransaction(args: SendTransactionArgs): Promise<Hex>;
|
|
@@ -297,6 +324,7 @@ export type RunLoginResult = {
|
|
|
297
324
|
session: Session;
|
|
298
325
|
refreshToken: string;
|
|
299
326
|
webauthn: ConnectResponse["webauthn"];
|
|
327
|
+
additionalSessions?: AdditionalSession[];
|
|
300
328
|
};
|
|
301
329
|
export type FinalizeAccessKeyParams = {
|
|
302
330
|
address: Address;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FlowCredential, FlowUser, VerifiedFlowJWT } from "./auth";
|
|
1
|
+
import type { AdditionalSession, FlowCredential, FlowUser, VerifiedFlowJWT } from "./auth";
|
|
2
2
|
import type { Address, FlowSessionState } from "./sdk";
|
|
3
3
|
import type { PublicProfile } from "./xp";
|
|
4
4
|
export type ResolveSessionOptions = {
|
|
@@ -53,4 +53,10 @@ export type SessionRouteSession = {
|
|
|
53
53
|
};
|
|
54
54
|
export type SessionRouteResponse = {
|
|
55
55
|
state: SessionRouteSession | null;
|
|
56
|
+
/**
|
|
57
|
+
* Additional-audience sessions minted by a reconcile request (POST `{ mint }`)
|
|
58
|
+
* — one per allowed audience the caller asked to provision. The consumer
|
|
59
|
+
* forwards each to its audience's own origin to be sealed HttpOnly there.
|
|
60
|
+
*/
|
|
61
|
+
additionalSessions?: AdditionalSession[];
|
|
56
62
|
};
|