@authyon/auth 0.1.3 → 0.1.4
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/index.cjs +38 -20
- package/dist/index.d.cts +58 -14
- package/dist/index.d.ts +58 -14
- package/dist/index.js +38 -20
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -113,7 +113,7 @@ var AuthyonClient = class {
|
|
|
113
113
|
loginFinish: (assertion) => this.request("/auth/webauthn/login/finish", {
|
|
114
114
|
method: "POST",
|
|
115
115
|
body: assertion
|
|
116
|
-
}).then((data) => this.setSession(data, "signed_in"))
|
|
116
|
+
}).then((data) => this.setSession({ tokens: data.tokens }, "signed_in")).then((session) => this.hydrateUser(session))
|
|
117
117
|
};
|
|
118
118
|
// ── Social sign-in (SSO) ─────────────────────────────────────────────────
|
|
119
119
|
this.sso = {
|
|
@@ -134,9 +134,7 @@ var AuthyonClient = class {
|
|
|
134
134
|
* POST /auth/sso/exchange — swaps the one-time code from the provider
|
|
135
135
|
* callback for tokens and stores the session.
|
|
136
136
|
*/
|
|
137
|
-
exchange: (code) => this.request("/auth/sso/exchange", { method: "POST", body: { code } }).then(
|
|
138
|
-
(data) => this.setSession(data, "signed_in")
|
|
139
|
-
)
|
|
137
|
+
exchange: (code) => this.request("/auth/sso/exchange", { method: "POST", body: { code } }).then((data) => this.setSession({ tokens: data.tokens }, "signed_in")).then((session) => this.hydrateUser(session))
|
|
140
138
|
};
|
|
141
139
|
// ── User ─────────────────────────────────────────────────────────────────
|
|
142
140
|
this.user = {
|
|
@@ -144,7 +142,7 @@ var AuthyonClient = class {
|
|
|
144
142
|
me: () => this.request("/auth/me", { bearer: true }).then(normalizeUser),
|
|
145
143
|
/** GET /auth/sessions — active refresh-token sessions with device/IP data. */
|
|
146
144
|
sessions: () => this.request("/auth/sessions", { bearer: true }),
|
|
147
|
-
/** GET /auth/me/activities — recent account activity for the current user. */
|
|
145
|
+
/** GET /auth/me/activities — paginated recent account activity for the current user. */
|
|
148
146
|
activities: (params = {}) => this.request(`/auth/me/activities?${toQuery(params)}`, { bearer: true }),
|
|
149
147
|
/**
|
|
150
148
|
* Revokes a single session by id (e.g. one entry from `sessions()`),
|
|
@@ -194,7 +192,7 @@ var AuthyonClient = class {
|
|
|
194
192
|
method: "POST",
|
|
195
193
|
bearer: true,
|
|
196
194
|
body: { tenantSlug: organizationSlug }
|
|
197
|
-
}).then((data) => this.setSession(data, "refreshed")),
|
|
195
|
+
}).then((data) => this.setSession({ tokens: data.tokens }, "refreshed")).then((session) => this.hydrateUser(session)),
|
|
198
196
|
/** The organization the current session is scoped to, from the cached session — no network call. */
|
|
199
197
|
current: () => this.getSession()?.user?.activeOrganization ?? null,
|
|
200
198
|
members: {
|
|
@@ -332,9 +330,9 @@ var AuthyonClient = class {
|
|
|
332
330
|
}
|
|
333
331
|
setSession(raw, event) {
|
|
334
332
|
const session = {
|
|
335
|
-
...raw,
|
|
333
|
+
...raw.tokens,
|
|
336
334
|
user: raw.user ? normalizeUser(raw.user) : void 0,
|
|
337
|
-
expiresAt: Date.now() + raw.expiresIn * 1e3
|
|
335
|
+
expiresAt: Date.now() + raw.tokens.expiresIn * 1e3
|
|
338
336
|
};
|
|
339
337
|
this.storage.set(session);
|
|
340
338
|
this.emit(event === "signed_out" ? { type: "signed_out" } : { type: event, session });
|
|
@@ -344,6 +342,23 @@ var AuthyonClient = class {
|
|
|
344
342
|
this.storage.clear();
|
|
345
343
|
this.emit({ type: "signed_out" });
|
|
346
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* `/auth/login` and the other endpoints that mint a session don't return
|
|
347
|
+
* a `user` object on the wire — only `tokens` (plus `twoFactor`, when a
|
|
348
|
+
* challenge is required). Fetch the profile right after so callers get a
|
|
349
|
+
* fully-populated `session.user` without an extra manual round trip.
|
|
350
|
+
* Best-effort: keeps the session usable even if this fetch fails.
|
|
351
|
+
*/
|
|
352
|
+
async hydrateUser(session) {
|
|
353
|
+
try {
|
|
354
|
+
const user = await this.user.me();
|
|
355
|
+
const hydrated = { ...session, user };
|
|
356
|
+
this.storage.set(hydrated);
|
|
357
|
+
return hydrated;
|
|
358
|
+
} catch {
|
|
359
|
+
return session;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
347
362
|
// ── HTTP core ────────────────────────────────────────────────────────────
|
|
348
363
|
async request(path, options = {}, isRetry = false) {
|
|
349
364
|
const headers = {
|
|
@@ -395,20 +410,20 @@ var AuthyonClient = class {
|
|
|
395
410
|
async login(params) {
|
|
396
411
|
const { organizationSlug, ...rest } = params;
|
|
397
412
|
const body = organizationSlug ? { ...rest, tenantSlug: organizationSlug } : rest;
|
|
398
|
-
const data = await this.request("/auth/login", {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
});
|
|
402
|
-
if (data.twoFactorRequired) {
|
|
403
|
-
return data;
|
|
413
|
+
const data = await this.request("/auth/login", { method: "POST", body });
|
|
414
|
+
if (data.twoFactor) {
|
|
415
|
+
return { twoFactorRequired: true, ...data.twoFactor };
|
|
404
416
|
}
|
|
405
|
-
const session = this.setSession(data, "signed_in");
|
|
417
|
+
const session = await this.hydrateUser(this.setSession({ tokens: data.tokens }, "signed_in"));
|
|
406
418
|
return { twoFactorRequired: false, session };
|
|
407
419
|
}
|
|
408
420
|
/** POST /auth/2fa/verify — redeems a 2FA challenge from `login()` and stores the session. */
|
|
409
421
|
async verifyTwoFactor(params) {
|
|
410
|
-
const data = await this.request("/auth/2fa/verify", {
|
|
411
|
-
|
|
422
|
+
const data = await this.request("/auth/2fa/verify", {
|
|
423
|
+
method: "POST",
|
|
424
|
+
body: params
|
|
425
|
+
});
|
|
426
|
+
return this.hydrateUser(this.setSession({ tokens: data.tokens }, "signed_in"));
|
|
412
427
|
}
|
|
413
428
|
/** POST /auth/refresh — rotates the single-use refresh token (single-flight). */
|
|
414
429
|
async refresh() {
|
|
@@ -420,7 +435,10 @@ var AuthyonClient = class {
|
|
|
420
435
|
method: "POST",
|
|
421
436
|
body: { refreshToken: current.refreshToken }
|
|
422
437
|
}).then(
|
|
423
|
-
(data) => this.setSession(
|
|
438
|
+
(data) => this.setSession(
|
|
439
|
+
{ tokens: data.tokens, user: current.user },
|
|
440
|
+
"refreshed"
|
|
441
|
+
)
|
|
424
442
|
).catch((error) => {
|
|
425
443
|
if (error instanceof AuthyonError && (error.status === 401 || error.status === 403)) {
|
|
426
444
|
this.clearSession();
|
|
@@ -472,11 +490,11 @@ var AuthyonClient = class {
|
|
|
472
490
|
}
|
|
473
491
|
};
|
|
474
492
|
function normalizeUser(raw) {
|
|
475
|
-
const { tenants, activeTenant, ...rest } = raw;
|
|
493
|
+
const { tenant, tenants, activeTenant, ...rest } = raw;
|
|
476
494
|
return {
|
|
477
495
|
...rest,
|
|
478
496
|
organizations: raw.organizations ?? tenants,
|
|
479
|
-
activeOrganization: raw.activeOrganization ?? activeTenant ?? null
|
|
497
|
+
activeOrganization: tenant ?? raw.activeOrganization ?? activeTenant ?? null
|
|
480
498
|
};
|
|
481
499
|
}
|
|
482
500
|
function createClient(options) {
|
package/dist/index.d.cts
CHANGED
|
@@ -35,9 +35,17 @@ interface User {
|
|
|
35
35
|
id: string;
|
|
36
36
|
email: string;
|
|
37
37
|
username?: string;
|
|
38
|
+
emailConfirmed?: boolean;
|
|
39
|
+
firstName?: string | null;
|
|
40
|
+
lastName?: string | null;
|
|
41
|
+
roles?: string[];
|
|
42
|
+
permissions?: string[];
|
|
43
|
+
createdAt?: string;
|
|
44
|
+
lastLoginAt?: string;
|
|
38
45
|
organizations?: Organization[];
|
|
39
46
|
activeOrganization?: Organization | null;
|
|
40
|
-
|
|
47
|
+
/** Actions the user must complete before continuing (e.g. confirm e-mail). */
|
|
48
|
+
pendencies?: string[];
|
|
41
49
|
}
|
|
42
50
|
/** Token pair issued by login / refresh / tenant switch. */
|
|
43
51
|
interface Session {
|
|
@@ -89,9 +97,18 @@ interface TwoFactorVerifyParams {
|
|
|
89
97
|
/** Required when `method` is `"webauthn"`. */
|
|
90
98
|
webAuthnAssertion?: WebAuthnAssertion;
|
|
91
99
|
}
|
|
100
|
+
/** GET /auth/2fa/status — per-method enrolment flags, confirmed against the live API. */
|
|
92
101
|
interface TwoFactorStatus {
|
|
93
|
-
|
|
94
|
-
|
|
102
|
+
authenticatorEnabled: boolean;
|
|
103
|
+
authenticatorConfirmedAt?: string | null;
|
|
104
|
+
emailEnabled: boolean;
|
|
105
|
+
emailEnabledAt?: string | null;
|
|
106
|
+
/** Partially redacted (e.g. `"n**********@h***.com"`). */
|
|
107
|
+
emailHint?: string | null;
|
|
108
|
+
webAuthnEnabled: boolean;
|
|
109
|
+
webAuthnCredentialCount: number;
|
|
110
|
+
webAuthnCredentials: WebAuthnCredential[];
|
|
111
|
+
remainingRecoveryCodes: number;
|
|
95
112
|
}
|
|
96
113
|
interface AuthenticatorSetup {
|
|
97
114
|
secret: string;
|
|
@@ -123,12 +140,27 @@ interface SsoProvider {
|
|
|
123
140
|
/** URL to redirect the browser to in order to start this provider's flow. */
|
|
124
141
|
startUrl: string;
|
|
125
142
|
}
|
|
143
|
+
/** GET /auth/me/activities — one audit-trail entry, confirmed against the live API. */
|
|
126
144
|
interface Activity {
|
|
127
145
|
id: string;
|
|
128
|
-
|
|
129
|
-
|
|
146
|
+
eventType: string;
|
|
147
|
+
occurredAt: string;
|
|
148
|
+
environmentId?: string;
|
|
130
149
|
ip?: string;
|
|
131
|
-
|
|
150
|
+
userAgent?: string;
|
|
151
|
+
/** JSON-encoded string — `JSON.parse` it for the event-specific payload. */
|
|
152
|
+
payloadJson?: string;
|
|
153
|
+
}
|
|
154
|
+
/** Paginated list envelope returned by `user.activities()`. */
|
|
155
|
+
interface Page<T> {
|
|
156
|
+
data: T[];
|
|
157
|
+
/** Item count actually returned for this page. */
|
|
158
|
+
perPage?: number;
|
|
159
|
+
pageSize: number;
|
|
160
|
+
total: number;
|
|
161
|
+
pages: number;
|
|
162
|
+
hasNext: boolean;
|
|
163
|
+
hasPrev: boolean;
|
|
132
164
|
}
|
|
133
165
|
/** A role available within an organization (tenant). */
|
|
134
166
|
interface Role {
|
|
@@ -137,13 +169,17 @@ interface Role {
|
|
|
137
169
|
description?: string;
|
|
138
170
|
permissions?: string[];
|
|
139
171
|
}
|
|
172
|
+
/** GET /auth/sessions — confirmed against the live API. */
|
|
140
173
|
interface SessionInfo {
|
|
141
174
|
id: string;
|
|
142
|
-
createdAt
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
175
|
+
createdAt: string;
|
|
176
|
+
expiresAt: string;
|
|
177
|
+
revokedAt?: string | null;
|
|
178
|
+
createdFromIp?: string;
|
|
179
|
+
isActive: boolean;
|
|
180
|
+
userAgent?: string;
|
|
181
|
+
lastUsedAt?: string | null;
|
|
182
|
+
lastUsedFromIp?: string | null;
|
|
147
183
|
}
|
|
148
184
|
interface IntrospectResult {
|
|
149
185
|
active: boolean;
|
|
@@ -211,6 +247,14 @@ declare class AuthyonClient {
|
|
|
211
247
|
private emit;
|
|
212
248
|
private setSession;
|
|
213
249
|
private clearSession;
|
|
250
|
+
/**
|
|
251
|
+
* `/auth/login` and the other endpoints that mint a session don't return
|
|
252
|
+
* a `user` object on the wire — only `tokens` (plus `twoFactor`, when a
|
|
253
|
+
* challenge is required). Fetch the profile right after so callers get a
|
|
254
|
+
* fully-populated `session.user` without an extra manual round trip.
|
|
255
|
+
* Best-effort: keeps the session usable even if this fetch fails.
|
|
256
|
+
*/
|
|
257
|
+
private hydrateUser;
|
|
214
258
|
private request;
|
|
215
259
|
private toError;
|
|
216
260
|
/** POST /auth/register — creates a new user (rate-limited to 20/hour per IP). */
|
|
@@ -266,8 +310,8 @@ declare class AuthyonClient {
|
|
|
266
310
|
me: () => Promise<User>;
|
|
267
311
|
/** GET /auth/sessions — active refresh-token sessions with device/IP data. */
|
|
268
312
|
sessions: () => Promise<SessionInfo[]>;
|
|
269
|
-
/** GET /auth/me/activities — recent account activity for the current user. */
|
|
270
|
-
activities: (params?: PageParams) => Promise<Activity
|
|
313
|
+
/** GET /auth/me/activities — paginated recent account activity for the current user. */
|
|
314
|
+
activities: (params?: PageParams) => Promise<Page<Activity>>;
|
|
271
315
|
/**
|
|
272
316
|
* Revokes a single session by id (e.g. one entry from `sessions()`),
|
|
273
317
|
* signing that device out without affecting the current one.
|
|
@@ -400,4 +444,4 @@ declare function memoryStorage(): TokenStorage;
|
|
|
400
444
|
declare function localStorageAdapter(key?: string): TokenStorage;
|
|
401
445
|
declare function defaultStorage(): TokenStorage;
|
|
402
446
|
|
|
403
|
-
export { type Activity, type AuthEvent, type AuthStateListener, type AuthenticatorSetup, AuthyonClient, type AuthyonClientOptions, AuthyonError, type CreateOrganizationParams, ErrorCodes, type IntrospectResult, type InviteMemberParams, type LoginParams, type LoginResult, type Organization, type OrganizationMember, type PageParams, type RegisterParams, type Role, type Session, type SessionInfo, type SsoProvider, type TokenStorage, type TwoFactorChallenge, type TwoFactorMethod, type TwoFactorStatus, type TwoFactorVerifyParams, type User, type ValidateResult, type WebAuthnAssertion, type WebAuthnCeremonyStart, type WebAuthnCredential, createClient, defaultStorage, localStorageAdapter, memoryStorage };
|
|
447
|
+
export { type Activity, type AuthEvent, type AuthStateListener, type AuthenticatorSetup, AuthyonClient, type AuthyonClientOptions, AuthyonError, type CreateOrganizationParams, ErrorCodes, type IntrospectResult, type InviteMemberParams, type LoginParams, type LoginResult, type Organization, type OrganizationMember, type Page, type PageParams, type RegisterParams, type Role, type Session, type SessionInfo, type SsoProvider, type TokenStorage, type TwoFactorChallenge, type TwoFactorMethod, type TwoFactorStatus, type TwoFactorVerifyParams, type User, type ValidateResult, type WebAuthnAssertion, type WebAuthnCeremonyStart, type WebAuthnCredential, createClient, defaultStorage, localStorageAdapter, memoryStorage };
|
package/dist/index.d.ts
CHANGED
|
@@ -35,9 +35,17 @@ interface User {
|
|
|
35
35
|
id: string;
|
|
36
36
|
email: string;
|
|
37
37
|
username?: string;
|
|
38
|
+
emailConfirmed?: boolean;
|
|
39
|
+
firstName?: string | null;
|
|
40
|
+
lastName?: string | null;
|
|
41
|
+
roles?: string[];
|
|
42
|
+
permissions?: string[];
|
|
43
|
+
createdAt?: string;
|
|
44
|
+
lastLoginAt?: string;
|
|
38
45
|
organizations?: Organization[];
|
|
39
46
|
activeOrganization?: Organization | null;
|
|
40
|
-
|
|
47
|
+
/** Actions the user must complete before continuing (e.g. confirm e-mail). */
|
|
48
|
+
pendencies?: string[];
|
|
41
49
|
}
|
|
42
50
|
/** Token pair issued by login / refresh / tenant switch. */
|
|
43
51
|
interface Session {
|
|
@@ -89,9 +97,18 @@ interface TwoFactorVerifyParams {
|
|
|
89
97
|
/** Required when `method` is `"webauthn"`. */
|
|
90
98
|
webAuthnAssertion?: WebAuthnAssertion;
|
|
91
99
|
}
|
|
100
|
+
/** GET /auth/2fa/status — per-method enrolment flags, confirmed against the live API. */
|
|
92
101
|
interface TwoFactorStatus {
|
|
93
|
-
|
|
94
|
-
|
|
102
|
+
authenticatorEnabled: boolean;
|
|
103
|
+
authenticatorConfirmedAt?: string | null;
|
|
104
|
+
emailEnabled: boolean;
|
|
105
|
+
emailEnabledAt?: string | null;
|
|
106
|
+
/** Partially redacted (e.g. `"n**********@h***.com"`). */
|
|
107
|
+
emailHint?: string | null;
|
|
108
|
+
webAuthnEnabled: boolean;
|
|
109
|
+
webAuthnCredentialCount: number;
|
|
110
|
+
webAuthnCredentials: WebAuthnCredential[];
|
|
111
|
+
remainingRecoveryCodes: number;
|
|
95
112
|
}
|
|
96
113
|
interface AuthenticatorSetup {
|
|
97
114
|
secret: string;
|
|
@@ -123,12 +140,27 @@ interface SsoProvider {
|
|
|
123
140
|
/** URL to redirect the browser to in order to start this provider's flow. */
|
|
124
141
|
startUrl: string;
|
|
125
142
|
}
|
|
143
|
+
/** GET /auth/me/activities — one audit-trail entry, confirmed against the live API. */
|
|
126
144
|
interface Activity {
|
|
127
145
|
id: string;
|
|
128
|
-
|
|
129
|
-
|
|
146
|
+
eventType: string;
|
|
147
|
+
occurredAt: string;
|
|
148
|
+
environmentId?: string;
|
|
130
149
|
ip?: string;
|
|
131
|
-
|
|
150
|
+
userAgent?: string;
|
|
151
|
+
/** JSON-encoded string — `JSON.parse` it for the event-specific payload. */
|
|
152
|
+
payloadJson?: string;
|
|
153
|
+
}
|
|
154
|
+
/** Paginated list envelope returned by `user.activities()`. */
|
|
155
|
+
interface Page<T> {
|
|
156
|
+
data: T[];
|
|
157
|
+
/** Item count actually returned for this page. */
|
|
158
|
+
perPage?: number;
|
|
159
|
+
pageSize: number;
|
|
160
|
+
total: number;
|
|
161
|
+
pages: number;
|
|
162
|
+
hasNext: boolean;
|
|
163
|
+
hasPrev: boolean;
|
|
132
164
|
}
|
|
133
165
|
/** A role available within an organization (tenant). */
|
|
134
166
|
interface Role {
|
|
@@ -137,13 +169,17 @@ interface Role {
|
|
|
137
169
|
description?: string;
|
|
138
170
|
permissions?: string[];
|
|
139
171
|
}
|
|
172
|
+
/** GET /auth/sessions — confirmed against the live API. */
|
|
140
173
|
interface SessionInfo {
|
|
141
174
|
id: string;
|
|
142
|
-
createdAt
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
175
|
+
createdAt: string;
|
|
176
|
+
expiresAt: string;
|
|
177
|
+
revokedAt?: string | null;
|
|
178
|
+
createdFromIp?: string;
|
|
179
|
+
isActive: boolean;
|
|
180
|
+
userAgent?: string;
|
|
181
|
+
lastUsedAt?: string | null;
|
|
182
|
+
lastUsedFromIp?: string | null;
|
|
147
183
|
}
|
|
148
184
|
interface IntrospectResult {
|
|
149
185
|
active: boolean;
|
|
@@ -211,6 +247,14 @@ declare class AuthyonClient {
|
|
|
211
247
|
private emit;
|
|
212
248
|
private setSession;
|
|
213
249
|
private clearSession;
|
|
250
|
+
/**
|
|
251
|
+
* `/auth/login` and the other endpoints that mint a session don't return
|
|
252
|
+
* a `user` object on the wire — only `tokens` (plus `twoFactor`, when a
|
|
253
|
+
* challenge is required). Fetch the profile right after so callers get a
|
|
254
|
+
* fully-populated `session.user` without an extra manual round trip.
|
|
255
|
+
* Best-effort: keeps the session usable even if this fetch fails.
|
|
256
|
+
*/
|
|
257
|
+
private hydrateUser;
|
|
214
258
|
private request;
|
|
215
259
|
private toError;
|
|
216
260
|
/** POST /auth/register — creates a new user (rate-limited to 20/hour per IP). */
|
|
@@ -266,8 +310,8 @@ declare class AuthyonClient {
|
|
|
266
310
|
me: () => Promise<User>;
|
|
267
311
|
/** GET /auth/sessions — active refresh-token sessions with device/IP data. */
|
|
268
312
|
sessions: () => Promise<SessionInfo[]>;
|
|
269
|
-
/** GET /auth/me/activities — recent account activity for the current user. */
|
|
270
|
-
activities: (params?: PageParams) => Promise<Activity
|
|
313
|
+
/** GET /auth/me/activities — paginated recent account activity for the current user. */
|
|
314
|
+
activities: (params?: PageParams) => Promise<Page<Activity>>;
|
|
271
315
|
/**
|
|
272
316
|
* Revokes a single session by id (e.g. one entry from `sessions()`),
|
|
273
317
|
* signing that device out without affecting the current one.
|
|
@@ -400,4 +444,4 @@ declare function memoryStorage(): TokenStorage;
|
|
|
400
444
|
declare function localStorageAdapter(key?: string): TokenStorage;
|
|
401
445
|
declare function defaultStorage(): TokenStorage;
|
|
402
446
|
|
|
403
|
-
export { type Activity, type AuthEvent, type AuthStateListener, type AuthenticatorSetup, AuthyonClient, type AuthyonClientOptions, AuthyonError, type CreateOrganizationParams, ErrorCodes, type IntrospectResult, type InviteMemberParams, type LoginParams, type LoginResult, type Organization, type OrganizationMember, type PageParams, type RegisterParams, type Role, type Session, type SessionInfo, type SsoProvider, type TokenStorage, type TwoFactorChallenge, type TwoFactorMethod, type TwoFactorStatus, type TwoFactorVerifyParams, type User, type ValidateResult, type WebAuthnAssertion, type WebAuthnCeremonyStart, type WebAuthnCredential, createClient, defaultStorage, localStorageAdapter, memoryStorage };
|
|
447
|
+
export { type Activity, type AuthEvent, type AuthStateListener, type AuthenticatorSetup, AuthyonClient, type AuthyonClientOptions, AuthyonError, type CreateOrganizationParams, ErrorCodes, type IntrospectResult, type InviteMemberParams, type LoginParams, type LoginResult, type Organization, type OrganizationMember, type Page, type PageParams, type RegisterParams, type Role, type Session, type SessionInfo, type SsoProvider, type TokenStorage, type TwoFactorChallenge, type TwoFactorMethod, type TwoFactorStatus, type TwoFactorVerifyParams, type User, type ValidateResult, type WebAuthnAssertion, type WebAuthnCeremonyStart, type WebAuthnCredential, createClient, defaultStorage, localStorageAdapter, memoryStorage };
|
package/dist/index.js
CHANGED
|
@@ -81,7 +81,7 @@ var AuthyonClient = class {
|
|
|
81
81
|
loginFinish: (assertion) => this.request("/auth/webauthn/login/finish", {
|
|
82
82
|
method: "POST",
|
|
83
83
|
body: assertion
|
|
84
|
-
}).then((data) => this.setSession(data, "signed_in"))
|
|
84
|
+
}).then((data) => this.setSession({ tokens: data.tokens }, "signed_in")).then((session) => this.hydrateUser(session))
|
|
85
85
|
};
|
|
86
86
|
// ── Social sign-in (SSO) ─────────────────────────────────────────────────
|
|
87
87
|
this.sso = {
|
|
@@ -102,9 +102,7 @@ var AuthyonClient = class {
|
|
|
102
102
|
* POST /auth/sso/exchange — swaps the one-time code from the provider
|
|
103
103
|
* callback for tokens and stores the session.
|
|
104
104
|
*/
|
|
105
|
-
exchange: (code) => this.request("/auth/sso/exchange", { method: "POST", body: { code } }).then(
|
|
106
|
-
(data) => this.setSession(data, "signed_in")
|
|
107
|
-
)
|
|
105
|
+
exchange: (code) => this.request("/auth/sso/exchange", { method: "POST", body: { code } }).then((data) => this.setSession({ tokens: data.tokens }, "signed_in")).then((session) => this.hydrateUser(session))
|
|
108
106
|
};
|
|
109
107
|
// ── User ─────────────────────────────────────────────────────────────────
|
|
110
108
|
this.user = {
|
|
@@ -112,7 +110,7 @@ var AuthyonClient = class {
|
|
|
112
110
|
me: () => this.request("/auth/me", { bearer: true }).then(normalizeUser),
|
|
113
111
|
/** GET /auth/sessions — active refresh-token sessions with device/IP data. */
|
|
114
112
|
sessions: () => this.request("/auth/sessions", { bearer: true }),
|
|
115
|
-
/** GET /auth/me/activities — recent account activity for the current user. */
|
|
113
|
+
/** GET /auth/me/activities — paginated recent account activity for the current user. */
|
|
116
114
|
activities: (params = {}) => this.request(`/auth/me/activities?${toQuery(params)}`, { bearer: true }),
|
|
117
115
|
/**
|
|
118
116
|
* Revokes a single session by id (e.g. one entry from `sessions()`),
|
|
@@ -162,7 +160,7 @@ var AuthyonClient = class {
|
|
|
162
160
|
method: "POST",
|
|
163
161
|
bearer: true,
|
|
164
162
|
body: { tenantSlug: organizationSlug }
|
|
165
|
-
}).then((data) => this.setSession(data, "refreshed")),
|
|
163
|
+
}).then((data) => this.setSession({ tokens: data.tokens }, "refreshed")).then((session) => this.hydrateUser(session)),
|
|
166
164
|
/** The organization the current session is scoped to, from the cached session — no network call. */
|
|
167
165
|
current: () => this.getSession()?.user?.activeOrganization ?? null,
|
|
168
166
|
members: {
|
|
@@ -300,9 +298,9 @@ var AuthyonClient = class {
|
|
|
300
298
|
}
|
|
301
299
|
setSession(raw, event) {
|
|
302
300
|
const session = {
|
|
303
|
-
...raw,
|
|
301
|
+
...raw.tokens,
|
|
304
302
|
user: raw.user ? normalizeUser(raw.user) : void 0,
|
|
305
|
-
expiresAt: Date.now() + raw.expiresIn * 1e3
|
|
303
|
+
expiresAt: Date.now() + raw.tokens.expiresIn * 1e3
|
|
306
304
|
};
|
|
307
305
|
this.storage.set(session);
|
|
308
306
|
this.emit(event === "signed_out" ? { type: "signed_out" } : { type: event, session });
|
|
@@ -312,6 +310,23 @@ var AuthyonClient = class {
|
|
|
312
310
|
this.storage.clear();
|
|
313
311
|
this.emit({ type: "signed_out" });
|
|
314
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* `/auth/login` and the other endpoints that mint a session don't return
|
|
315
|
+
* a `user` object on the wire — only `tokens` (plus `twoFactor`, when a
|
|
316
|
+
* challenge is required). Fetch the profile right after so callers get a
|
|
317
|
+
* fully-populated `session.user` without an extra manual round trip.
|
|
318
|
+
* Best-effort: keeps the session usable even if this fetch fails.
|
|
319
|
+
*/
|
|
320
|
+
async hydrateUser(session) {
|
|
321
|
+
try {
|
|
322
|
+
const user = await this.user.me();
|
|
323
|
+
const hydrated = { ...session, user };
|
|
324
|
+
this.storage.set(hydrated);
|
|
325
|
+
return hydrated;
|
|
326
|
+
} catch {
|
|
327
|
+
return session;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
315
330
|
// ── HTTP core ────────────────────────────────────────────────────────────
|
|
316
331
|
async request(path, options = {}, isRetry = false) {
|
|
317
332
|
const headers = {
|
|
@@ -363,20 +378,20 @@ var AuthyonClient = class {
|
|
|
363
378
|
async login(params) {
|
|
364
379
|
const { organizationSlug, ...rest } = params;
|
|
365
380
|
const body = organizationSlug ? { ...rest, tenantSlug: organizationSlug } : rest;
|
|
366
|
-
const data = await this.request("/auth/login", {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
});
|
|
370
|
-
if (data.twoFactorRequired) {
|
|
371
|
-
return data;
|
|
381
|
+
const data = await this.request("/auth/login", { method: "POST", body });
|
|
382
|
+
if (data.twoFactor) {
|
|
383
|
+
return { twoFactorRequired: true, ...data.twoFactor };
|
|
372
384
|
}
|
|
373
|
-
const session = this.setSession(data, "signed_in");
|
|
385
|
+
const session = await this.hydrateUser(this.setSession({ tokens: data.tokens }, "signed_in"));
|
|
374
386
|
return { twoFactorRequired: false, session };
|
|
375
387
|
}
|
|
376
388
|
/** POST /auth/2fa/verify — redeems a 2FA challenge from `login()` and stores the session. */
|
|
377
389
|
async verifyTwoFactor(params) {
|
|
378
|
-
const data = await this.request("/auth/2fa/verify", {
|
|
379
|
-
|
|
390
|
+
const data = await this.request("/auth/2fa/verify", {
|
|
391
|
+
method: "POST",
|
|
392
|
+
body: params
|
|
393
|
+
});
|
|
394
|
+
return this.hydrateUser(this.setSession({ tokens: data.tokens }, "signed_in"));
|
|
380
395
|
}
|
|
381
396
|
/** POST /auth/refresh — rotates the single-use refresh token (single-flight). */
|
|
382
397
|
async refresh() {
|
|
@@ -388,7 +403,10 @@ var AuthyonClient = class {
|
|
|
388
403
|
method: "POST",
|
|
389
404
|
body: { refreshToken: current.refreshToken }
|
|
390
405
|
}).then(
|
|
391
|
-
(data) => this.setSession(
|
|
406
|
+
(data) => this.setSession(
|
|
407
|
+
{ tokens: data.tokens, user: current.user },
|
|
408
|
+
"refreshed"
|
|
409
|
+
)
|
|
392
410
|
).catch((error) => {
|
|
393
411
|
if (error instanceof AuthyonError && (error.status === 401 || error.status === 403)) {
|
|
394
412
|
this.clearSession();
|
|
@@ -440,11 +458,11 @@ var AuthyonClient = class {
|
|
|
440
458
|
}
|
|
441
459
|
};
|
|
442
460
|
function normalizeUser(raw) {
|
|
443
|
-
const { tenants, activeTenant, ...rest } = raw;
|
|
461
|
+
const { tenant, tenants, activeTenant, ...rest } = raw;
|
|
444
462
|
return {
|
|
445
463
|
...rest,
|
|
446
464
|
organizations: raw.organizations ?? tenants,
|
|
447
|
-
activeOrganization: raw.activeOrganization ?? activeTenant ?? null
|
|
465
|
+
activeOrganization: tenant ?? raw.activeOrganization ?? activeTenant ?? null
|
|
448
466
|
};
|
|
449
467
|
}
|
|
450
468
|
function createClient(options) {
|