@flow-industries/id 0.15.0 → 0.16.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/profile-button.js +14 -0
- package/dist/sdk/types/auth.d.ts +62 -0
- package/dist/sdk/types/dialog.d.ts +13 -0
- package/dist/sdk/types/events.d.ts +8 -2
- package/dist/sdk/types/index.d.ts +3 -3
- package/dist/sdk/types/messenger.d.ts +7 -0
- package/dist/sdk/types/rooms.d.ts +28 -0
- package/package.json +3 -2
|
@@ -81,6 +81,13 @@ export function createProfileButton(options) {
|
|
|
81
81
|
? { username: user.username, isGuest: user.isGuest === true }
|
|
82
82
|
: null;
|
|
83
83
|
}
|
|
84
|
+
// The widget's only credential. `getToken` refreshes a token that is missing
|
|
85
|
+
// or near expiry, so a widget open on a page left idle past the 1h access-
|
|
86
|
+
// token lifetime still gets a live one instead of a 401.
|
|
87
|
+
async function answerTokenRequest(bridge, id) {
|
|
88
|
+
const token = await flow.getToken().catch(() => null);
|
|
89
|
+
void bridge.send("__internal", { type: "profile-token", id, token });
|
|
90
|
+
}
|
|
84
91
|
// ----- the persistent pill (stays mounted; sizes to its own content) -----
|
|
85
92
|
const pill = makeFrame();
|
|
86
93
|
let pillWidth = 0;
|
|
@@ -105,6 +112,9 @@ export function createProfileButton(options) {
|
|
|
105
112
|
else if (payload.type === "profile-expand") {
|
|
106
113
|
showDialog();
|
|
107
114
|
}
|
|
115
|
+
else if (payload.type === "profile-token-request") {
|
|
116
|
+
void answerTokenRequest(pillBridge, payload.id);
|
|
117
|
+
}
|
|
108
118
|
});
|
|
109
119
|
// ----- the dialog overlay (lazy; a separate fullscreen iframe layered ABOVE
|
|
110
120
|
// the pill, so the pill shows through its dimmed backdrop) -----
|
|
@@ -131,6 +141,10 @@ export function createProfileButton(options) {
|
|
|
131
141
|
hideDialog();
|
|
132
142
|
void flow.logout().catch(() => { });
|
|
133
143
|
}
|
|
144
|
+
else if (payload.type === "profile-token-request") {
|
|
145
|
+
if (dialogBridge)
|
|
146
|
+
void answerTokenRequest(dialogBridge, payload.id);
|
|
147
|
+
}
|
|
134
148
|
});
|
|
135
149
|
void dialogBridge.send("__internal", {
|
|
136
150
|
type: "profile-identity",
|
package/dist/sdk/types/auth.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { JWTPayload } from "jose";
|
|
2
|
+
import type { AuthEventName } from "./events";
|
|
2
3
|
export type FlowUser = {
|
|
3
4
|
id: string;
|
|
4
5
|
username: string;
|
|
@@ -14,6 +15,17 @@ export type FlowCredential = {
|
|
|
14
15
|
id: string;
|
|
15
16
|
publicKey: string;
|
|
16
17
|
};
|
|
18
|
+
/** The better-auth session user with Flow's additional fields — the one
|
|
19
|
+
* shape routes cast `session.user` to instead of ad-hoc inline casts. */
|
|
20
|
+
export interface SessionUser {
|
|
21
|
+
id: string;
|
|
22
|
+
email?: string | null;
|
|
23
|
+
emailVerified?: boolean;
|
|
24
|
+
username?: string | null;
|
|
25
|
+
isGuest?: boolean;
|
|
26
|
+
role?: string | null;
|
|
27
|
+
image?: string | null;
|
|
28
|
+
}
|
|
17
29
|
/**
|
|
18
30
|
* A session minted for one of the ADDITIONAL audiences a login requested
|
|
19
31
|
* (AUTH-39): its own refresh-token lineage and access JWT, both bound to
|
|
@@ -61,6 +73,56 @@ export type VerifyOptions = {
|
|
|
61
73
|
audience: string;
|
|
62
74
|
issuerUrl?: string;
|
|
63
75
|
};
|
|
76
|
+
/**
|
|
77
|
+
* One entry in the signed-in user's session list (`GET /api/account/sessions`).
|
|
78
|
+
* An entry is either a cookie session (with the activity of its linked app
|
|
79
|
+
* refresh lineages folded in) or an app-only refresh lineage whose cookie
|
|
80
|
+
* session no longer exists. `id` is an opaque revocation handle — never a
|
|
81
|
+
* token, and never another user's row.
|
|
82
|
+
*/
|
|
83
|
+
export type AccountSession = {
|
|
84
|
+
id: string;
|
|
85
|
+
/** The app origin the session was opened for; null on a pre-audience row. */
|
|
86
|
+
audience: string | null;
|
|
87
|
+
/** Coarse "Browser · OS" from the sign-in user agent; null when unknown. */
|
|
88
|
+
device: string | null;
|
|
89
|
+
createdAt: string;
|
|
90
|
+
/** Latest activity across the cookie session and its app refresh lineages. */
|
|
91
|
+
lastSeenAt: string;
|
|
92
|
+
expiresAt: string;
|
|
93
|
+
/** True for the session serving this request. */
|
|
94
|
+
isCurrent: boolean;
|
|
95
|
+
};
|
|
96
|
+
export type AccountSessionsResponse = {
|
|
97
|
+
sessions: AccountSession[];
|
|
98
|
+
};
|
|
99
|
+
/** Result of `DELETE /api/account/sessions/:id`. */
|
|
100
|
+
export type RevokeSessionResponse = {
|
|
101
|
+
revoked: true;
|
|
102
|
+
/** True when the caller revoked the session serving the request itself. */
|
|
103
|
+
wasCurrent: boolean;
|
|
104
|
+
};
|
|
105
|
+
/** Result of `POST /api/account/sessions/revoke-all`. */
|
|
106
|
+
export type RevokeAllSessionsResponse = {
|
|
107
|
+
revokedSessions: number;
|
|
108
|
+
revokedLineages: number;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* One entry of the user-facing security feed
|
|
112
|
+
* (`GET /api/account/security-events`). Country and coarse device only —
|
|
113
|
+
* the stored ip and raw user agent never leave the server.
|
|
114
|
+
*/
|
|
115
|
+
export type SecurityActivityEntry = {
|
|
116
|
+
id: string;
|
|
117
|
+
event: AuthEventName;
|
|
118
|
+
audience: string | null;
|
|
119
|
+
country: string | null;
|
|
120
|
+
device: string | null;
|
|
121
|
+
createdAt: string;
|
|
122
|
+
};
|
|
123
|
+
export type SecurityActivityResponse = {
|
|
124
|
+
events: SecurityActivityEntry[];
|
|
125
|
+
};
|
|
64
126
|
export type VerifiedFlowJWT = JWTPayload & {
|
|
65
127
|
sub: string;
|
|
66
128
|
username?: string;
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async view state for a dialog panel — the three states every fetched
|
|
3
|
+
* section renders (spinner / retry row / content), so "empty" stays
|
|
4
|
+
* distinguishable from "not loaded yet".
|
|
5
|
+
*/
|
|
6
|
+
export type Loadable<T> = {
|
|
7
|
+
status: "loading";
|
|
8
|
+
} | {
|
|
9
|
+
status: "error";
|
|
10
|
+
} | {
|
|
11
|
+
status: "ready";
|
|
12
|
+
data: T;
|
|
13
|
+
};
|
|
1
14
|
export type DialogReferrer = {
|
|
2
15
|
icon?: string | {
|
|
3
16
|
light: string;
|
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
* here are the contract every dashboard/alert query depends on — changing one
|
|
5
5
|
* is a breaking change to the dashboard.
|
|
6
6
|
*/
|
|
7
|
+
import type { GlobalRole } from "./rooms";
|
|
7
8
|
export type AuthOutcome = "success" | "failure" | "info";
|
|
8
9
|
export type AuthMode = "sign-up" | "sign-in";
|
|
9
10
|
/** Client-only funnel steps reported via the `/api/events` beacon. */
|
|
10
11
|
export type FunnelStep = "mode_selected" | "email_entered" | "ceremony_started" | "ceremony_failed" | "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.passkey.added" | "auth.passkey.add_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.ceremony_failed" | "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" | "guest_session" | "passkey_exists" | "already_upgraded" | "malformed_token" | "unknown_key" | "verification_failed" | "webauthn_not_allowed" | "webauthn_security" | "webauthn_invalid_state" | "webauthn_not_supported" | "webauthn_constraint" | "webauthn_aborted" | "webauthn_unknown" | "internal_error";
|
|
12
|
+
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.passkey.added" | "auth.passkey.add_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.session.revoked" | "auth.session.revoked_all" | "auth.audience.rejected" | "auth.audience.minted" | "auth.guest.created" | "auth.guest.restored" | "auth.guest.upgraded" | "auth.guest.failed" | "auth.role.granted" | "auth.role.revoked" | "auth.funnel.mode_selected" | "auth.funnel.email_entered" | "auth.funnel.ceremony_started" | "auth.funnel.ceremony_failed" | "auth.funnel.done_shown";
|
|
13
|
+
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_user_epoch_stale" | "refresh_reuse_detected" | "guest_rate_limited" | "guest_global_limit" | "guest_username_exhausted" | "guest_session" | "passkey_exists" | "already_upgraded" | "malformed_token" | "unknown_key" | "verification_failed" | "webauthn_not_allowed" | "webauthn_security" | "webauthn_invalid_state" | "webauthn_not_supported" | "webauthn_constraint" | "webauthn_aborted" | "webauthn_unknown" | "internal_error";
|
|
13
14
|
/** One flat record per event = one row in the `auth_events` stream. */
|
|
14
15
|
export interface AuthEventRecord {
|
|
15
16
|
service: "auth";
|
|
@@ -26,6 +27,11 @@ export interface AuthEventRecord {
|
|
|
26
27
|
mode: AuthMode | null;
|
|
27
28
|
step: FunnelStep | null;
|
|
28
29
|
errorCode: AuthErrorCode | null;
|
|
30
|
+
/** Role-audit fields: who changed whose global role, from what, to what.
|
|
31
|
+
* `userId`/`username` carry the target. */
|
|
32
|
+
actorId: string | null;
|
|
33
|
+
roleFrom: GlobalRole | null;
|
|
34
|
+
roleTo: GlobalRole | null;
|
|
29
35
|
ip: string | null;
|
|
30
36
|
country: string | null;
|
|
31
37
|
userAgent: string | null;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export type { AdditionalSession, AuthConfig, AuthResponse, AuthResponseWithWebAuthn, FlowCredential, FlowUser, PasskeyPluginOptions, VerifiedFlowJWT, VerifyOptions, WebAuthnSignature, } from "./auth";
|
|
2
|
-
export type { BoundaryError, DialogCustomFeatures, DialogCustomLabels, DialogError, DialogReferrer, DialogState, ProfileData, ProfileIdentity, } from "./dialog";
|
|
1
|
+
export type { AccountSession, AccountSessionsResponse, AdditionalSession, AuthConfig, AuthResponse, AuthResponseWithWebAuthn, FlowCredential, FlowUser, PasskeyPluginOptions, RevokeAllSessionsResponse, RevokeSessionResponse, SecurityActivityEntry, SecurityActivityResponse, SessionUser, VerifiedFlowJWT, VerifyOptions, WebAuthnSignature, } from "./auth";
|
|
2
|
+
export type { BoundaryError, DialogCustomFeatures, DialogCustomLabels, DialogError, DialogReferrer, DialogState, Loadable, 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";
|
|
5
5
|
export type { Call, ConnectCapabilities, ConnectRequest, ConnectResponse, GuestRequest, GuestResponse, MethodName, MethodParams, MethodResult, RestoreResponse, RpcRequest, SendCallsParams, SendCallsRequest, SendCallsResponse, SendTransactionParams, SendTransactionRequest, SendTransactionResponse, SignMessageRequest, SignMessageResponse, SignOutRequest, SignOutResponse, SignTypedDataRequest, SignTypedDataResponse, TransactionArgs, TypedData, TypedDataDomain, TypedDataField, } from "./protocol";
|
|
6
6
|
export { getConnectCapabilities, isPersonalSignParams, isSendCallsParams, isSendTransactionParams, } from "./protocol";
|
|
7
|
-
export type { CreateRoomInput, MyRooms, RoomChannel, RoomDetail, RoomList, RoomMemberEntry, RoomMembers, RoomOccupancy, RoomOwner, RoomPresenceEntry, RoomPresenceSnapshot, RoomRestrictionKind, RoomRole, RoomSummary, RoomSurfaceSettings, RoomsApi, RoomVisibility, UpdateRoomInput, VerifyRoomContext, } from "./rooms";
|
|
7
|
+
export type { CreateRoomInput, GlobalRole, ModerationSubject, MyRooms, RoleChangeVerdict, RoomChannel, RoomDetail, RoomList, RoomMemberEntry, RoomMembers, RoomOccupancy, RoomOwner, RoomPresenceEntry, RoomPresenceSnapshot, RoomRestrictionKind, RoomRole, RoomSummary, RoomSurfaceSettings, RoomsApi, RoomVisibility, StaffEntry, UpdateRoomInput, VerifyRoomContext, } from "./rooms";
|
|
8
8
|
export type { AccessKeyOptions, AccessKeyPreparation, Address, CreateDialogHostOptions, CreateFlowOptions, DialogHost, DialogOpenOptions, FinalizeAccessKeyParams, Flow, FlowConnectorParameters, FlowCookieNames, FlowIdProviderProps, FlowSessionState, 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";
|
|
9
9
|
export type { ResolvedFlowSession, ResolveSessionOptions, SessionRouteOptions, SessionRouteResponse, SessionRouteSession, } from "./server";
|
|
10
10
|
export type { CoinAsset, IdentifiedTx, TxApprove, TxConvert, TxSend, TxSwap, } from "./tx";
|
|
@@ -90,6 +90,13 @@ export type Schema = [
|
|
|
90
90
|
username: string;
|
|
91
91
|
isGuest: boolean;
|
|
92
92
|
} | null;
|
|
93
|
+
} | {
|
|
94
|
+
type: "profile-token-request";
|
|
95
|
+
id: string;
|
|
96
|
+
} | {
|
|
97
|
+
type: "profile-token";
|
|
98
|
+
id: string;
|
|
99
|
+
token: string | null;
|
|
93
100
|
} | {
|
|
94
101
|
type: "profile-login";
|
|
95
102
|
mode: "signin" | "signup";
|
|
@@ -5,6 +5,34 @@ export type RoomVisibility = "public" | "unlisted" | "private";
|
|
|
5
5
|
* Member rows carry moderator|member.
|
|
6
6
|
*/
|
|
7
7
|
export type RoomRole = "owner" | "moderator" | "member";
|
|
8
|
+
/**
|
|
9
|
+
* Global permission tier held on the user row, composing over room roles: an
|
|
10
|
+
* admin holds owner-power in any room, a platform moderator holds mod-power
|
|
11
|
+
* anywhere. Read from the DB at enforcement time — never a JWT claim — so a
|
|
12
|
+
* demotion is instant.
|
|
13
|
+
*/
|
|
14
|
+
export type GlobalRole = "user" | "moderator" | "admin";
|
|
15
|
+
/** What the moderation lattice ranks: a subject's global tier plus their
|
|
16
|
+
* room-axis role (`owner` from `room.ownerId`, else the member row). */
|
|
17
|
+
export interface ModerationSubject {
|
|
18
|
+
globalRole: GlobalRole;
|
|
19
|
+
roomRole: RoomRole | null;
|
|
20
|
+
}
|
|
21
|
+
/** One row of `GET /api/admin/staff` — a user holding a non-`user` tier. */
|
|
22
|
+
export interface StaffEntry {
|
|
23
|
+
userId: string;
|
|
24
|
+
username: string;
|
|
25
|
+
role: GlobalRole;
|
|
26
|
+
}
|
|
27
|
+
/** Outcome of a global-role change request: refused with a reason, or
|
|
28
|
+
* allowed with the audit event it is (`null` for a no-op). */
|
|
29
|
+
export type RoleChangeVerdict = {
|
|
30
|
+
allowed: true;
|
|
31
|
+
event: "auth.role.granted" | "auth.role.revoked" | null;
|
|
32
|
+
} | {
|
|
33
|
+
allowed: false;
|
|
34
|
+
reason: "self_target" | "guest_target";
|
|
35
|
+
};
|
|
8
36
|
export type RoomChannel = "game" | "voice" | "text";
|
|
9
37
|
export type RoomRestrictionKind = "ban" | "mute";
|
|
10
38
|
export interface RoomOwner {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flow-industries/id",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.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",
|
|
@@ -91,6 +91,7 @@
|
|
|
91
91
|
"devDependencies": {
|
|
92
92
|
"@better-auth/cli": "^1.4.17",
|
|
93
93
|
"@biomejs/biome": "2.4.12",
|
|
94
|
+
"@electric-sql/pglite": "^0.5.4",
|
|
94
95
|
"@flow-industries/lint": "0.1.0",
|
|
95
96
|
"@react-email/ui": "^6.6.0",
|
|
96
97
|
"@tailwindcss/vite": "^4.1.18",
|
|
@@ -112,7 +113,7 @@
|
|
|
112
113
|
},
|
|
113
114
|
"dependencies": {
|
|
114
115
|
"@aws-sdk/client-s3": "^3.1073.0",
|
|
115
|
-
"@flow-industries/ui": "^0.
|
|
116
|
+
"@flow-industries/ui": "^0.19.0",
|
|
116
117
|
"@hono/otel": "^1.1.2",
|
|
117
118
|
"@openobserve/browser-logs": "^0.3.1",
|
|
118
119
|
"@openobserve/browser-rum": "^0.3.1",
|