@flow-industries/id 0.3.1 → 0.6.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/access-key.d.ts +5 -3
- package/dist/sdk/client/access-key.js +6 -4
- package/dist/sdk/client/create-flow.js +145 -3
- package/dist/sdk/client/dialog-host.js +24 -18
- package/dist/sdk/client/iframe-host.d.ts +23 -0
- package/dist/sdk/client/iframe-host.js +34 -0
- package/dist/sdk/client/index.d.ts +2 -1
- package/dist/sdk/client/index.js +1 -0
- package/dist/sdk/client/methods.d.ts +1 -0
- package/dist/sdk/client/methods.js +1 -0
- package/dist/sdk/client/profile-button.d.ts +15 -0
- package/dist/sdk/client/profile-button.js +189 -0
- package/dist/sdk/client/signing.d.ts +1 -1
- package/dist/sdk/client/signing.js +3 -1
- package/dist/sdk/client/store.js +4 -1
- package/dist/sdk/dialog/remote/Messenger.d.ts +1 -2
- package/dist/sdk/dialog/remote/Messenger.js +3 -1
- package/dist/sdk/react/hooks.d.ts +6 -2
- package/dist/sdk/react/hooks.js +6 -2
- package/dist/sdk/react/index.d.ts +3 -2
- package/dist/sdk/react/index.js +2 -1
- package/dist/sdk/react/profile-button.d.ts +9 -0
- package/dist/sdk/react/profile-button.js +28 -0
- package/dist/sdk/react/provider.js +1 -1
- package/dist/sdk/types/auth.d.ts +13 -0
- package/dist/sdk/types/dialog.d.ts +22 -0
- package/dist/sdk/types/events.d.ts +40 -0
- package/dist/sdk/types/events.js +6 -0
- package/dist/sdk/types/index.d.ts +5 -4
- package/dist/sdk/types/messenger.d.ts +30 -0
- package/dist/sdk/types/protocol.d.ts +15 -1
- package/dist/sdk/types/sdk.d.ts +72 -1
- package/dist/sdk/verify.d.ts +7 -0
- package/dist/sdk/verify.js +7 -0
- package/dist/sdk/wagmi/index.js +8 -3
- package/package.json +30 -4
- package/dist/sdk/client/protocol.d.ts +0 -97
- package/dist/sdk/client/protocol.js +0 -9
- package/dist/sdk/client/types.d.ts +0 -35
- package/dist/sdk/client/types.js +0 -0
- package/dist/sdk/types.d.ts +0 -32
- package/dist/sdk/types.js +0 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Bridge, BridgeParameters, FromWindowOptions, Messenger } from "../../types";
|
|
2
|
-
export type { Bridge, BridgeParameters, FromWindowOptions, Messenger, Payload, QueuedRequest, ReadyOptions, Schema, Topic, WithReady, } from "../../types";
|
|
3
|
-
export type { MessageResponse as Response } from "../../types";
|
|
2
|
+
export type { Bridge, BridgeParameters, FromWindowOptions, MessageResponse as Response, Messenger, Payload, QueuedRequest, ReadyOptions, Schema, Topic, WithReady, } from "../../types";
|
|
4
3
|
export declare function from(messenger: Messenger): Messenger;
|
|
5
4
|
/**
|
|
6
5
|
* Wraps a Window in the Messenger interface. Reads come from
|
|
@@ -38,7 +38,7 @@ export function from(messenger) {
|
|
|
38
38
|
* isolation that prevents arbitrary pages from injecting messages.
|
|
39
39
|
*/
|
|
40
40
|
export function fromWindow(w, options = {}) {
|
|
41
|
-
const { targetOrigin } = options;
|
|
41
|
+
const { targetOrigin, source } = options;
|
|
42
42
|
const listeners = new Map();
|
|
43
43
|
return from({
|
|
44
44
|
destroy() {
|
|
@@ -54,6 +54,8 @@ export function fromWindow(w, options = {}) {
|
|
|
54
54
|
return;
|
|
55
55
|
if (targetOrigin && event.origin !== targetOrigin)
|
|
56
56
|
return;
|
|
57
|
+
if (source && event.source !== source)
|
|
58
|
+
return;
|
|
57
59
|
listener(event.data.payload, event);
|
|
58
60
|
}
|
|
59
61
|
w.addEventListener("message", handler);
|
|
@@ -18,16 +18,20 @@ export declare function useFlowState(): FlowState;
|
|
|
18
18
|
/**
|
|
19
19
|
* Convenience hook that returns the most commonly needed identity data and
|
|
20
20
|
* actions in one go: user/jwt/address plus `login`, `logout`, `restore`,
|
|
21
|
-
* and `
|
|
22
|
-
* being set so it tracks reactive state
|
|
21
|
+
* `refreshJwt`, and `getToken`. The `isAuthenticated` boolean is derived from
|
|
22
|
+
* `user` being set so it tracks reactive state; `isGuest` is true while the
|
|
23
|
+
* user is a guest — gate sensitive actions on `!isGuest`, not `isAuthenticated`.
|
|
23
24
|
*/
|
|
24
25
|
export declare function useFlowId(): {
|
|
25
26
|
user: import("../types").FlowUser | null;
|
|
26
27
|
jwt: string | null;
|
|
27
28
|
address: `0x${string}` | null;
|
|
28
29
|
isAuthenticated: boolean;
|
|
30
|
+
isGuest: boolean;
|
|
29
31
|
login: (options?: import("../types").LoginOptions) => Promise<import("../types").Session>;
|
|
30
32
|
logout: () => Promise<void>;
|
|
31
33
|
restore: () => Promise<boolean>;
|
|
34
|
+
ensureGuest: () => Promise<boolean>;
|
|
32
35
|
refreshJwt: () => Promise<boolean>;
|
|
36
|
+
getToken: () => Promise<string | null>;
|
|
33
37
|
};
|
package/dist/sdk/react/hooks.js
CHANGED
|
@@ -32,8 +32,9 @@ export function useFlowState() {
|
|
|
32
32
|
/**
|
|
33
33
|
* Convenience hook that returns the most commonly needed identity data and
|
|
34
34
|
* actions in one go: user/jwt/address plus `login`, `logout`, `restore`,
|
|
35
|
-
* and `
|
|
36
|
-
* being set so it tracks reactive state
|
|
35
|
+
* `refreshJwt`, and `getToken`. The `isAuthenticated` boolean is derived from
|
|
36
|
+
* `user` being set so it tracks reactive state; `isGuest` is true while the
|
|
37
|
+
* user is a guest — gate sensitive actions on `!isGuest`, not `isAuthenticated`.
|
|
37
38
|
*/
|
|
38
39
|
export function useFlowId() {
|
|
39
40
|
const flow = useFlow();
|
|
@@ -43,9 +44,12 @@ export function useFlowId() {
|
|
|
43
44
|
jwt: state.jwt,
|
|
44
45
|
address: state.address,
|
|
45
46
|
isAuthenticated: state.user !== null,
|
|
47
|
+
isGuest: state.user?.isGuest ?? false,
|
|
46
48
|
login: flow.login,
|
|
47
49
|
logout: flow.logout,
|
|
48
50
|
restore: flow.restore,
|
|
51
|
+
ensureGuest: flow.ensureGuest,
|
|
49
52
|
refreshJwt: flow.refreshJwt,
|
|
53
|
+
getToken: flow.getToken,
|
|
50
54
|
};
|
|
51
55
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export type { FlowIdProviderProps, ProfileButtonProps } from "../types";
|
|
2
2
|
export { useFlow, useFlowId, useFlowState } from "./hooks";
|
|
3
|
-
export
|
|
3
|
+
export { ProfileButton } from "./profile-button";
|
|
4
|
+
export { FlowIdProvider } from "./provider";
|
package/dist/sdk/react/index.js
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ProfileButtonProps } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Drops the universal Flow ID profile button into a React tree. Renders a host
|
|
4
|
+
* element (style it via `className` for placement/padding — e.g. top-right) that
|
|
5
|
+
* the pill iframe mounts into; the iframe itself is sized to its content and
|
|
6
|
+
* expands to the profile dialog on click. Resolves the Flow instance from a
|
|
7
|
+
* `<FlowIdProvider>` or the createFlow() singleton.
|
|
8
|
+
*/
|
|
9
|
+
export declare function ProfileButton({ className, host, theme }: ProfileButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { createProfileButton } from "../client/profile-button";
|
|
4
|
+
import { useFlow } from "./hooks";
|
|
5
|
+
/**
|
|
6
|
+
* Drops the universal Flow ID profile button into a React tree. Renders a host
|
|
7
|
+
* element (style it via `className` for placement/padding — e.g. top-right) that
|
|
8
|
+
* the pill iframe mounts into; the iframe itself is sized to its content and
|
|
9
|
+
* expands to the profile dialog on click. Resolves the Flow instance from a
|
|
10
|
+
* `<FlowIdProvider>` or the createFlow() singleton.
|
|
11
|
+
*/
|
|
12
|
+
export function ProfileButton({ className, host, theme }) {
|
|
13
|
+
const flow = useFlow();
|
|
14
|
+
const ref = useRef(null);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const container = ref.current;
|
|
17
|
+
if (!container)
|
|
18
|
+
return;
|
|
19
|
+
const handle = createProfileButton({
|
|
20
|
+
container,
|
|
21
|
+
flow,
|
|
22
|
+
...(host ? { host } : {}),
|
|
23
|
+
...(theme ? { theme } : {}),
|
|
24
|
+
});
|
|
25
|
+
return () => handle.destroy();
|
|
26
|
+
}, [flow, host, theme]);
|
|
27
|
+
return _jsx("div", { ref: ref, className: className });
|
|
28
|
+
}
|
|
@@ -11,5 +11,5 @@ export const FlowContext = createContext(null);
|
|
|
11
11
|
* the dependency explicit at a particular boundary.
|
|
12
12
|
*/
|
|
13
13
|
export function FlowIdProvider({ flow, children }) {
|
|
14
|
-
return _jsx(FlowContext.Provider, { value: flow ?? null, children: children });
|
|
14
|
+
return (_jsx(FlowContext.Provider, { value: flow ?? null, children: children }));
|
|
15
15
|
}
|
package/dist/sdk/types/auth.d.ts
CHANGED
|
@@ -2,6 +2,13 @@ import type { JWTPayload } from "jose";
|
|
|
2
2
|
export type FlowUser = {
|
|
3
3
|
id: string;
|
|
4
4
|
username: string;
|
|
5
|
+
/**
|
|
6
|
+
* True only for guest accounts (no passkey/email yet). Optional so the many
|
|
7
|
+
* `{ id, username }` producers stay valid; readers treat `undefined`/`false`
|
|
8
|
+
* as "not a guest". Sensitive actions should gate on `!isGuest` (or the JWT
|
|
9
|
+
* `guest` claim), never on session presence alone.
|
|
10
|
+
*/
|
|
11
|
+
isGuest?: boolean;
|
|
5
12
|
};
|
|
6
13
|
export type FlowCredential = {
|
|
7
14
|
id: string;
|
|
@@ -42,4 +49,10 @@ export type VerifyOptions = {
|
|
|
42
49
|
export type VerifiedFlowJWT = JWTPayload & {
|
|
43
50
|
sub: string;
|
|
44
51
|
username?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Present and `true` only on guest tokens. A relying party gating sensitive
|
|
54
|
+
* actions MUST treat a MISSING claim as `guest === true` (fail-safe), so a
|
|
55
|
+
* future token-format change can never silently grant full privileges.
|
|
56
|
+
*/
|
|
57
|
+
guest?: boolean;
|
|
45
58
|
};
|
|
@@ -37,6 +37,27 @@ export type DialogCustomLabels = {
|
|
|
37
37
|
switchAccount?: string;
|
|
38
38
|
signUpLink?: string;
|
|
39
39
|
};
|
|
40
|
+
/**
|
|
41
|
+
* The profile fields the widget renders, read from `/api/me` (the cookie
|
|
42
|
+
* session's `session.user`). `image`/`createdAt` are not on the SDK's
|
|
43
|
+
* `FlowUser`, so they only come from this authenticated fetch.
|
|
44
|
+
*/
|
|
45
|
+
export type ProfileData = {
|
|
46
|
+
username: string;
|
|
47
|
+
image?: string | null;
|
|
48
|
+
createdAt?: string;
|
|
49
|
+
isGuest?: boolean;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Identity the embedding host pushes into the widget over postMessage. Lets the
|
|
53
|
+
* pill render the username immediately (and reactively on login/logout) without
|
|
54
|
+
* waiting on `/api/me` — the fast path that also works where third-party cookies
|
|
55
|
+
* are blocked (Safari/Firefox).
|
|
56
|
+
*/
|
|
57
|
+
export type ProfileIdentity = {
|
|
58
|
+
username: string;
|
|
59
|
+
isGuest: boolean;
|
|
60
|
+
};
|
|
40
61
|
export type DialogState = {
|
|
41
62
|
mode: string;
|
|
42
63
|
display: "floating" | "drawer" | "full";
|
|
@@ -50,6 +71,7 @@ export type DialogState = {
|
|
|
50
71
|
username?: string;
|
|
51
72
|
email?: string;
|
|
52
73
|
}>;
|
|
74
|
+
shown: boolean;
|
|
53
75
|
customFeatures?: DialogCustomFeatures;
|
|
54
76
|
customLabels?: DialogCustomLabels;
|
|
55
77
|
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured auth-observability events. Emitted server-side to the dedicated
|
|
3
|
+
* OpenObserve `auth_events` stream (and mirrored to stdout). The field names
|
|
4
|
+
* here are the contract every dashboard/alert query depends on — changing one
|
|
5
|
+
* is a breaking change to the dashboard.
|
|
6
|
+
*/
|
|
7
|
+
export type AuthOutcome = "success" | "failure" | "info";
|
|
8
|
+
export type AuthMode = "sign-up" | "sign-in";
|
|
9
|
+
/** Client-only funnel steps reported via the `/api/events` beacon. */
|
|
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.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" | "guest_rate_limited" | "guest_global_limit" | "guest_username_exhausted" | "already_upgraded" | "malformed_token" | "unknown_key" | "verification_failed" | "internal_error";
|
|
13
|
+
/** One flat record per event = one row in the `auth_events` stream. */
|
|
14
|
+
export interface AuthEventRecord {
|
|
15
|
+
service: "auth";
|
|
16
|
+
env: string;
|
|
17
|
+
event: AuthEventName;
|
|
18
|
+
outcome: AuthOutcome;
|
|
19
|
+
app: string | null;
|
|
20
|
+
audience: string | null;
|
|
21
|
+
userId: string | null;
|
|
22
|
+
username: string | null;
|
|
23
|
+
address: string | null;
|
|
24
|
+
credentialId: string | null;
|
|
25
|
+
funnelId: string | null;
|
|
26
|
+
mode: AuthMode | null;
|
|
27
|
+
step: FunnelStep | null;
|
|
28
|
+
errorCode: AuthErrorCode | null;
|
|
29
|
+
ip: string | null;
|
|
30
|
+
country: string | null;
|
|
31
|
+
userAgent: string | null;
|
|
32
|
+
durationMs: number | null;
|
|
33
|
+
traceId: string | null;
|
|
34
|
+
}
|
|
35
|
+
/** Body accepted by `POST /api/events` from the dialog. */
|
|
36
|
+
export interface BeaconBody {
|
|
37
|
+
step: FunnelStep;
|
|
38
|
+
funnelId: string;
|
|
39
|
+
mode?: AuthMode | null;
|
|
40
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured auth-observability events. Emitted server-side to the dedicated
|
|
3
|
+
* OpenObserve `auth_events` stream (and mirrored to stdout). The field names
|
|
4
|
+
* here are the contract every dashboard/alert query depends on — changing one
|
|
5
|
+
* is a breaking change to the dashboard.
|
|
6
|
+
*/
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export type { AuthConfig, AuthResponse, AuthResponseWithWebAuthn, FlowCredential, FlowUser, PasskeyPluginOptions, VerifiedFlowJWT, VerifyOptions, WebAuthnSignature, } from "./auth";
|
|
2
|
-
export type {
|
|
3
|
-
export {
|
|
4
|
-
export type { Call, ConnectCapabilities, ConnectRequest, ConnectResponse, MethodName, MethodParams, MethodResult, RestoreRequest, RestoreResponse, RpcRequest, SendCallsParams, SendCallsRequest, SendCallsResponse, SendTransactionParams, SendTransactionRequest, SendTransactionResponse, SignMessageRequest, SignMessageResponse, SignOutRequest, SignOutResponse, SignTypedDataRequest, SignTypedDataResponse, TransactionArgs, TypedData, TypedDataDomain, TypedDataField, } from "./protocol";
|
|
2
|
+
export type { BoundaryError, DialogCustomFeatures, DialogCustomLabels, DialogError, DialogReferrer, DialogState, ProfileData, ProfileIdentity, } from "./dialog";
|
|
3
|
+
export type { AuthErrorCode, AuthEventName, AuthEventRecord, AuthMode, AuthOutcome, BeaconBody, FunnelStep, } from "./events";
|
|
5
4
|
export type { Bridge, BridgeParameters, FlowAccount, FlowRemote, FlowRemoteConfig, FromWindowOptions, MessageResponse, Messenger, OneOf, Payload, QueuedRequest, ReadyOptions, RemoteFlowState, RemoteState, Schema, Storage, Topic, WithReady, } from "./messenger";
|
|
6
|
-
export type {
|
|
5
|
+
export type { Call, ConnectCapabilities, ConnectRequest, ConnectResponse, GuestRequest, GuestResponse, MethodName, MethodParams, MethodResult, RestoreRequest, RestoreResponse, RpcRequest, SendCallsParams, SendCallsRequest, SendCallsResponse, SendTransactionParams, SendTransactionRequest, SendTransactionResponse, SignMessageRequest, SignMessageResponse, SignOutRequest, SignOutResponse, SignTypedDataRequest, SignTypedDataResponse, TransactionArgs, TypedData, TypedDataDomain, TypedDataField, } from "./protocol";
|
|
6
|
+
export { isPersonalSignParams, isSendCallsParams, isSendTransactionParams, } from "./protocol";
|
|
7
|
+
export type { AccessKeyOptions, AccessKeyPreparation, Address, CreateDialogHostOptions, CreateFlowOptions, DialogHost, DialogOpenOptions, FinalizeAccessKeyParams, Flow, FlowConnectorParameters, FlowIdProviderProps, FlowState, Listener, LoginOptions, MountProfileOptions, PrepareArgsWithAuth, PrepareTransactionRequestPhase, ProfileButtonHandle, ProfileButtonProps, ProfilePosition, ResolveAccountParams, ResolvedAccessKeyOptions, RootCredential, RunLoginParams, RunLoginResult, SendCallsArgs, SendTransactionArgs, Session, SigningContext, SignMessageArgs, SignTypedDataArgs, Store, StoredAccessKey, WagmiConnectCapabilities, WagmiConnectParams, } from "./sdk";
|
|
7
8
|
export type { CoinAsset, IdentifiedTx, TxApprove, TxConvert, TxSend, TxSwap, } from "./tx";
|
|
@@ -65,6 +65,7 @@ export type Schema = [
|
|
|
65
65
|
};
|
|
66
66
|
features?: Record<string, boolean>;
|
|
67
67
|
labels?: Record<string, string>;
|
|
68
|
+
route?: string;
|
|
68
69
|
} | {
|
|
69
70
|
type: "switch";
|
|
70
71
|
mode: "iframe" | "popup" | "popup-standalone" | "page";
|
|
@@ -77,6 +78,27 @@ export type Schema = [
|
|
|
77
78
|
theme: {
|
|
78
79
|
colorScheme?: string;
|
|
79
80
|
};
|
|
81
|
+
} | {
|
|
82
|
+
type: "profile-resize";
|
|
83
|
+
width: number;
|
|
84
|
+
height: number;
|
|
85
|
+
} | {
|
|
86
|
+
type: "profile-expand";
|
|
87
|
+
} | {
|
|
88
|
+
type: "profile-identity";
|
|
89
|
+
identity: {
|
|
90
|
+
username: string;
|
|
91
|
+
isGuest: boolean;
|
|
92
|
+
} | null;
|
|
93
|
+
} | {
|
|
94
|
+
type: "profile-login";
|
|
95
|
+
mode: "signin" | "signup";
|
|
96
|
+
} | {
|
|
97
|
+
type: "profile-logout";
|
|
98
|
+
} | {
|
|
99
|
+
type: "dialog-shown";
|
|
100
|
+
} | {
|
|
101
|
+
type: "dialog-hidden";
|
|
80
102
|
};
|
|
81
103
|
response: undefined;
|
|
82
104
|
}
|
|
@@ -106,6 +128,14 @@ export type Bridge = WithReady & {
|
|
|
106
128
|
};
|
|
107
129
|
export type FromWindowOptions = {
|
|
108
130
|
targetOrigin?: string;
|
|
131
|
+
/**
|
|
132
|
+
* When set, inbound messages are only accepted from this exact window
|
|
133
|
+
* (`event.source`). Required when more than one same-origin iframe posts to
|
|
134
|
+
* the same host window (e.g. the auth dialog and the profile widget) —
|
|
135
|
+
* origin filtering alone can't tell same-origin frames apart, so without this
|
|
136
|
+
* one frame's `ready` handshake would resolve the other bridge's gate.
|
|
137
|
+
*/
|
|
138
|
+
source?: MessageEventSource | null;
|
|
109
139
|
};
|
|
110
140
|
export type BridgeParameters = {
|
|
111
141
|
from: Messenger;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FlowCredential, FlowUser, WebAuthnSignature } from "./auth";
|
|
2
2
|
import type { Address } from "./sdk";
|
|
3
|
-
export type MethodName = "wallet_connect" | "personal_sign" | "eth_signTypedData" | "eth_sendTransaction" | "wallet_sendCalls" | "wallet_restore" | "wallet_signout";
|
|
3
|
+
export type MethodName = "wallet_connect" | "personal_sign" | "eth_signTypedData" | "eth_sendTransaction" | "wallet_sendCalls" | "wallet_restore" | "wallet_guest" | "wallet_signout";
|
|
4
4
|
export type ConnectCapabilities = {
|
|
5
5
|
createAccount?: boolean;
|
|
6
6
|
signIn?: boolean;
|
|
@@ -65,6 +65,18 @@ export type RestoreResponse = {
|
|
|
65
65
|
credential: FlowCredential;
|
|
66
66
|
address: Address;
|
|
67
67
|
};
|
|
68
|
+
export type GuestRequest = [];
|
|
69
|
+
/**
|
|
70
|
+
* A guest has no passkey and no derivable wallet address yet, so `credential`
|
|
71
|
+
* and `address` are always null — the shape mirrors RestoreResponse so the SDK
|
|
72
|
+
* can commit either to the same store fields.
|
|
73
|
+
*/
|
|
74
|
+
export type GuestResponse = {
|
|
75
|
+
jwt: string;
|
|
76
|
+
user: FlowUser;
|
|
77
|
+
credential: null;
|
|
78
|
+
address: null;
|
|
79
|
+
};
|
|
68
80
|
export type SignOutRequest = [];
|
|
69
81
|
export type SignOutResponse = {
|
|
70
82
|
ok: true;
|
|
@@ -76,6 +88,7 @@ export type MethodParams = {
|
|
|
76
88
|
eth_sendTransaction: SendTransactionRequest;
|
|
77
89
|
wallet_sendCalls: SendCallsRequest;
|
|
78
90
|
wallet_restore: RestoreRequest;
|
|
91
|
+
wallet_guest: GuestRequest;
|
|
79
92
|
wallet_signout: SignOutRequest;
|
|
80
93
|
};
|
|
81
94
|
export type MethodResult = {
|
|
@@ -85,6 +98,7 @@ export type MethodResult = {
|
|
|
85
98
|
eth_sendTransaction: SendTransactionResponse;
|
|
86
99
|
wallet_sendCalls: SendCallsResponse;
|
|
87
100
|
wallet_restore: RestoreResponse;
|
|
101
|
+
wallet_guest: GuestResponse;
|
|
88
102
|
wallet_signout: SignOutResponse;
|
|
89
103
|
};
|
|
90
104
|
export type RpcRequest = {
|
package/dist/sdk/types/sdk.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
1
2
|
import type { Chain, Hex, PrepareTransactionRequestParameters, SendTransactionParameters, SignTypedDataParameters, Transport, WalletClient } from "viem";
|
|
2
3
|
import type { SendCallsParameters } from "viem/actions";
|
|
3
|
-
import type { ReactNode } from "react";
|
|
4
4
|
import type { FlowCredential, FlowUser, WebAuthnSignature } from "./auth";
|
|
5
5
|
import type { ConnectCapabilities, ConnectResponse } from "./protocol";
|
|
6
6
|
export type Address = `0x${string}`;
|
|
@@ -63,6 +63,13 @@ export type CreateFlowOptions = {
|
|
|
63
63
|
* if the user has a valid Flow cookie session, state populates without UI.
|
|
64
64
|
*/
|
|
65
65
|
autoRestore?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* If true, every visitor without an existing session is silently given a
|
|
68
|
+
* persistent guest account on startup (no UI, no passkey). Off by default —
|
|
69
|
+
* the consumer app opts in. Signing up later upgrades the guest in place, so
|
|
70
|
+
* data keyed on the Flow user id survives. See `ensureGuest`.
|
|
71
|
+
*/
|
|
72
|
+
autoGuest?: boolean;
|
|
66
73
|
};
|
|
67
74
|
export type FlowState = {
|
|
68
75
|
user: FlowUser | null;
|
|
@@ -122,6 +129,36 @@ export type DialogOpenOptions = {
|
|
|
122
129
|
icon?: string;
|
|
123
130
|
};
|
|
124
131
|
};
|
|
132
|
+
/** Anchor for an auto-created (host-less) profile widget container. */
|
|
133
|
+
export type ProfilePosition = "top-right" | "top-left" | "bottom-right" | "bottom-left";
|
|
134
|
+
export type MountProfileOptions = {
|
|
135
|
+
/**
|
|
136
|
+
* Element to mount the pill iframe into. When omitted, a fixed-position
|
|
137
|
+
* container is created and anchored via `position` — the path of least
|
|
138
|
+
* resistance for non-DOM hosts (the Godot canvas overlay).
|
|
139
|
+
*/
|
|
140
|
+
container?: HTMLElement;
|
|
141
|
+
/** Anchor used only when `container` is omitted. Defaults to "top-right". */
|
|
142
|
+
position?: ProfilePosition;
|
|
143
|
+
/** CSS padding for the auto-created container. Defaults to a responsive inset. */
|
|
144
|
+
padding?: string;
|
|
145
|
+
/**
|
|
146
|
+
* The Flow ID origin serving the widget (e.g. https://id.flow.industries).
|
|
147
|
+
* Defaults to the resolved Flow instance's `host`, then the SDK default.
|
|
148
|
+
*/
|
|
149
|
+
host?: string;
|
|
150
|
+
/**
|
|
151
|
+
* Color scheme for the widget chrome. Set this to match the host (the dark
|
|
152
|
+
* game UI passes "dark"). Defaults to "light dark" (follows the OS).
|
|
153
|
+
*/
|
|
154
|
+
theme?: "light" | "dark" | "light dark";
|
|
155
|
+
/** Explicit Flow instance. Defaults to the createFlow() singleton. */
|
|
156
|
+
flow?: Flow;
|
|
157
|
+
};
|
|
158
|
+
export type ProfileButtonHandle = {
|
|
159
|
+
/** Tears down the iframe, listeners, and any auto-created container. */
|
|
160
|
+
destroy: () => void;
|
|
161
|
+
};
|
|
125
162
|
export type DialogHost = {
|
|
126
163
|
open: (options?: DialogOpenOptions) => void;
|
|
127
164
|
close: () => void;
|
|
@@ -136,10 +173,29 @@ export type Flow = {
|
|
|
136
173
|
readonly credential: FlowState["credential"];
|
|
137
174
|
readonly address: Address | null;
|
|
138
175
|
readonly isAuthenticated: boolean;
|
|
176
|
+
/**
|
|
177
|
+
* True when the current session is a guest (authenticated but not yet a full
|
|
178
|
+
* account). `isAuthenticated` is also true for guests — gate sensitive
|
|
179
|
+
* actions on `!isGuest`, not on `isAuthenticated`.
|
|
180
|
+
*/
|
|
181
|
+
readonly isGuest: boolean;
|
|
139
182
|
login(options?: LoginOptions): Promise<Session>;
|
|
140
183
|
logout(): Promise<void>;
|
|
141
184
|
restore(): Promise<boolean>;
|
|
185
|
+
/**
|
|
186
|
+
* Ensures a session exists: restores an existing one, otherwise silently
|
|
187
|
+
* creates a persistent guest account. Idempotent and single-flight; resolves
|
|
188
|
+
* true if a session (guest or full) is now active, false if creation failed.
|
|
189
|
+
* Called automatically on startup when `autoGuest` is set.
|
|
190
|
+
*/
|
|
191
|
+
ensureGuest(): Promise<boolean>;
|
|
142
192
|
refreshJwt(): Promise<boolean>;
|
|
193
|
+
/**
|
|
194
|
+
* Returns a currently-valid JWT, silently refreshing from the cookie session
|
|
195
|
+
* if the cached token is expired or near expiry; null if there is no session.
|
|
196
|
+
* Prefer this over reading `jwt` directly when calling a backend.
|
|
197
|
+
*/
|
|
198
|
+
getToken(): Promise<string | null>;
|
|
143
199
|
signMessage(args: SignMessageArgs): Promise<Hex>;
|
|
144
200
|
signTypedData(args: SignTypedDataArgs): Promise<Hex>;
|
|
145
201
|
sendTransaction(args: SendTransactionArgs): Promise<Hex>;
|
|
@@ -152,6 +208,13 @@ export type Flow = {
|
|
|
152
208
|
subscribe(listener: Listener<FlowState>): () => void;
|
|
153
209
|
getState(): FlowState;
|
|
154
210
|
dialog: DialogHost;
|
|
211
|
+
/**
|
|
212
|
+
* The resolved Flow ID origin this instance talks to (no trailing slash),
|
|
213
|
+
* e.g. `https://id.flow.industries`. Exposed so widgets like the profile
|
|
214
|
+
* button build their iframe URL from the single host configured on createFlow
|
|
215
|
+
* instead of re-resolving it.
|
|
216
|
+
*/
|
|
217
|
+
readonly host: string;
|
|
155
218
|
};
|
|
156
219
|
export type FlowConnectorParameters = Omit<CreateFlowOptions, "chains" | "transports"> & {
|
|
157
220
|
/**
|
|
@@ -176,6 +239,14 @@ export type FlowIdProviderProps = {
|
|
|
176
239
|
flow?: Flow;
|
|
177
240
|
children: ReactNode;
|
|
178
241
|
};
|
|
242
|
+
export type ProfileButtonProps = {
|
|
243
|
+
/** Class applied to the host element the pill iframe mounts into. */
|
|
244
|
+
className?: string;
|
|
245
|
+
/** Override the Flow ID origin; defaults to the resolved Flow's `host`. */
|
|
246
|
+
host?: string;
|
|
247
|
+
/** Color scheme for the widget chrome; defaults to "light dark" (the OS). */
|
|
248
|
+
theme?: "light" | "dark" | "light dark";
|
|
249
|
+
};
|
|
179
250
|
export type RunLoginParams = {
|
|
180
251
|
dialog: DialogHost;
|
|
181
252
|
store: Store<FlowState>;
|
package/dist/sdk/verify.d.ts
CHANGED
|
@@ -11,5 +11,12 @@ export type { VerifiedFlowJWT, VerifyOptions } from "./types";
|
|
|
11
11
|
*
|
|
12
12
|
* `audience` MUST match the calling app's origin — this is what prevents
|
|
13
13
|
* a token issued for `flow.talk` from being replayed against `flow.game`.
|
|
14
|
+
*
|
|
15
|
+
* Every Flow token carries an explicit boolean `guest` claim (`true` for guest
|
|
16
|
+
* sessions with no passkey yet, `false` for full accounts). When gating
|
|
17
|
+
* sensitive actions, require `payload.guest === false`; treat anything else —
|
|
18
|
+
* `true`, or a MISSING claim (not a Flow token, or a future format change) — as
|
|
19
|
+
* guest and deny. This fail-safe means a dropped or renamed claim can never
|
|
20
|
+
* silently grant a guest full privileges.
|
|
14
21
|
*/
|
|
15
22
|
export declare function verifyFlowJWT(token: string, opts: VerifyOptions): Promise<VerifiedFlowJWT>;
|
package/dist/sdk/verify.js
CHANGED
|
@@ -12,6 +12,13 @@ const DEFAULT_ISSUER_URL = "https://id.flow.industries";
|
|
|
12
12
|
*
|
|
13
13
|
* `audience` MUST match the calling app's origin — this is what prevents
|
|
14
14
|
* a token issued for `flow.talk` from being replayed against `flow.game`.
|
|
15
|
+
*
|
|
16
|
+
* Every Flow token carries an explicit boolean `guest` claim (`true` for guest
|
|
17
|
+
* sessions with no passkey yet, `false` for full accounts). When gating
|
|
18
|
+
* sensitive actions, require `payload.guest === false`; treat anything else —
|
|
19
|
+
* `true`, or a MISSING claim (not a Flow token, or a future format change) — as
|
|
20
|
+
* guest and deny. This fail-safe means a dropped or renamed claim can never
|
|
21
|
+
* silently grant a guest full privileges.
|
|
15
22
|
*/
|
|
16
23
|
export async function verifyFlowJWT(token, opts) {
|
|
17
24
|
const issuerUrl = opts.issuerUrl ?? DEFAULT_ISSUER_URL;
|
package/dist/sdk/wagmi/index.js
CHANGED
|
@@ -8,7 +8,8 @@ function loginOptionsFromCapabilities(capabilities) {
|
|
|
8
8
|
return {
|
|
9
9
|
...(signUp ? { signUp: true } : {}),
|
|
10
10
|
...(!signUp && signIn ? { signIn: true } : {}),
|
|
11
|
-
...(!signUp &&
|
|
11
|
+
...(!signUp &&
|
|
12
|
+
(capabilities?.signInHeadless || capabilities?.type === "sign-in")
|
|
12
13
|
? { signInHeadless: true }
|
|
13
14
|
: {}),
|
|
14
15
|
};
|
|
@@ -137,8 +138,12 @@ export function flowConnector(parameters = {}) {
|
|
|
137
138
|
return f.walletClient({ ...(chainId ? { chainId } : {}) });
|
|
138
139
|
},
|
|
139
140
|
async getProvider({ chainId } = {}) {
|
|
140
|
-
const client = await this.getClient({
|
|
141
|
-
|
|
141
|
+
const client = await this.getClient({
|
|
142
|
+
...(chainId ? { chainId } : {}),
|
|
143
|
+
});
|
|
144
|
+
return {
|
|
145
|
+
request: client.request,
|
|
146
|
+
};
|
|
142
147
|
},
|
|
143
148
|
onAccountsChanged() { },
|
|
144
149
|
onChainChanged(chain) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flow-industries/id",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"main": "./dist/sdk/client/index.js",
|
|
5
5
|
"module": "./dist/sdk/client/index.js",
|
|
6
6
|
"types": "./dist/sdk/client/index.d.ts",
|
|
@@ -46,10 +46,16 @@
|
|
|
46
46
|
"build:sdk": "tsc --project tsconfig.sdk.json",
|
|
47
47
|
"prepublishOnly": "bun run build:sdk",
|
|
48
48
|
"start": "bun run src/index.ts",
|
|
49
|
+
"email:dev": "email dev --dir src/emails --port 3010",
|
|
49
50
|
"db:generate": "bunx @better-auth/cli generate",
|
|
50
|
-
"db:migrate": "bunx drizzle-kit generate
|
|
51
|
+
"db:migrate:gen": "bunx drizzle-kit generate",
|
|
52
|
+
"db:migrate": "bunx drizzle-kit migrate",
|
|
51
53
|
"db:push": "bunx drizzle-kit push",
|
|
52
|
-
"db:studio": "bunx drizzle-kit studio"
|
|
54
|
+
"db:studio": "bunx drizzle-kit studio",
|
|
55
|
+
"lint": "biome check",
|
|
56
|
+
"format": "biome format --write",
|
|
57
|
+
"check": "biome check --write",
|
|
58
|
+
"typecheck": "tsr generate && tsc --noEmit"
|
|
53
59
|
},
|
|
54
60
|
"peerDependencies": {
|
|
55
61
|
"viem": ">=2.47.10",
|
|
@@ -67,21 +73,39 @@
|
|
|
67
73
|
},
|
|
68
74
|
"devDependencies": {
|
|
69
75
|
"@better-auth/cli": "^1.4.17",
|
|
76
|
+
"@biomejs/biome": "2.4.12",
|
|
77
|
+
"@flow-industries/lint": "0.1.0",
|
|
78
|
+
"@react-email/ui": "^6.6.0",
|
|
70
79
|
"@tailwindcss/vite": "^4.1.18",
|
|
71
80
|
"@tanstack/react-router-devtools": "^1.160.0",
|
|
81
|
+
"@tanstack/router-cli": "^1.167.17",
|
|
72
82
|
"@tanstack/router-plugin": "^1.160.0",
|
|
73
83
|
"@types/bun": "latest",
|
|
74
84
|
"@types/react": "^19.2.13",
|
|
75
85
|
"@types/react-dom": "^19.2.3",
|
|
76
86
|
"@vitejs/plugin-react": "^5.1.3",
|
|
77
87
|
"drizzle-kit": "^0.31.8",
|
|
88
|
+
"pino-pretty": "^13.1.3",
|
|
89
|
+
"react-email": "^6.6.0",
|
|
78
90
|
"tailwindcss": "^4.1.18",
|
|
79
91
|
"tw-animate-css": "^1.4.0",
|
|
80
92
|
"typescript": "^5",
|
|
81
93
|
"vite": "^7.3.1"
|
|
82
94
|
},
|
|
83
95
|
"dependencies": {
|
|
84
|
-
"@
|
|
96
|
+
"@aws-sdk/client-s3": "^3.1073.0",
|
|
97
|
+
"@flow-industries/ui": "^0.15.3",
|
|
98
|
+
"@hono/otel": "^1.1.2",
|
|
99
|
+
"@openobserve/browser-logs": "^0.3.1",
|
|
100
|
+
"@openobserve/browser-rum": "^0.3.1",
|
|
101
|
+
"@opentelemetry/api": "^1.9.1",
|
|
102
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.218.0",
|
|
103
|
+
"@opentelemetry/resources": "^2.7.1",
|
|
104
|
+
"@opentelemetry/sdk-trace-base": "^2.7.1",
|
|
105
|
+
"@opentelemetry/sdk-trace-node": "^2.7.1",
|
|
106
|
+
"@opentelemetry/semantic-conventions": "^1.41.1",
|
|
107
|
+
"@react-email/components": "^1.0.12",
|
|
108
|
+
"@react-email/render": "^2.0.8",
|
|
85
109
|
"@tanstack/query-sync-storage-persister": "^5.90.22",
|
|
86
110
|
"@tanstack/react-query": "^5.90.20",
|
|
87
111
|
"@tanstack/react-query-persist-client": "^5.90.22",
|
|
@@ -93,10 +117,12 @@
|
|
|
93
117
|
"jose": "^6.1.3",
|
|
94
118
|
"lucide-react": "^0.563.0",
|
|
95
119
|
"motion": "^12.33.0",
|
|
120
|
+
"pino": "^10.3.1",
|
|
96
121
|
"postgres": "^3.4.8",
|
|
97
122
|
"react": "^19.2.4",
|
|
98
123
|
"react-dom": "^19.2.4",
|
|
99
124
|
"react-intersection-observer": "^10.0.2",
|
|
125
|
+
"sharp": "^0.35.2",
|
|
100
126
|
"tempo.ts": "^0.14.2",
|
|
101
127
|
"viem": "2.47.10",
|
|
102
128
|
"wagmi": "^3.4.2",
|