@aurabase/auth 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -2
- package/dist/index.cjs +138 -17
- package/dist/index.d.cts +14 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +139 -18
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -39,8 +39,13 @@ await aura.auth.signOut()
|
|
|
39
39
|
### Session
|
|
40
40
|
|
|
41
41
|
```ts
|
|
42
|
-
// getSession()
|
|
43
|
-
const {
|
|
42
|
+
// getSession() lit le store local (synchrone en pratique) :
|
|
43
|
+
const { data: { session } } = await aura.auth.getSession()
|
|
44
|
+
if (session) {
|
|
45
|
+
console.log('Session ID:', session.id)
|
|
46
|
+
console.log('Access token:', session.access_token)
|
|
47
|
+
console.log('User ID:', session.user.id)
|
|
48
|
+
}
|
|
44
49
|
|
|
45
50
|
const refreshed = await aura.auth.refreshSession()
|
|
46
51
|
|
|
@@ -55,6 +60,32 @@ if (first) await aura.auth.revokeSession(first.id)
|
|
|
55
60
|
await aura.auth.revokeAllSessions()
|
|
56
61
|
```
|
|
57
62
|
|
|
63
|
+
### Configuration du stockage (SSR & Node.js)
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { createClient, LocalAuthStore, BaseAuthStore } from '@aurabase/aurabase-js'
|
|
67
|
+
|
|
68
|
+
// Par défaut, le client choisit automatiquement :
|
|
69
|
+
// - Navigateur : `LocalAuthStore` avec clé scopée par projet (`aura-<slug>-auth`)
|
|
70
|
+
// - Node.js / SSR : `BaseAuthStore` en mémoire avec exportToCookie() / loadFromCookie()
|
|
71
|
+
|
|
72
|
+
// Pour personnaliser le stockage :
|
|
73
|
+
const aura = createClient('https://mon-projet.aurabase.cloud', 'aura_anon_xxx', {
|
|
74
|
+
auth: {
|
|
75
|
+
storage: new LocalAuthStore('custom-auth-key'),
|
|
76
|
+
autoRefreshToken: true,
|
|
77
|
+
persistSession: true,
|
|
78
|
+
},
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// Sérialisation cookie (SSR) :
|
|
82
|
+
const setCookieHeader = aura.authStore.exportToCookie({
|
|
83
|
+
httpOnly: true,
|
|
84
|
+
secure: true,
|
|
85
|
+
sameSite: 'lax',
|
|
86
|
+
})
|
|
87
|
+
```
|
|
88
|
+
|
|
58
89
|
### OAuth (implicite ou PKCE)
|
|
59
90
|
|
|
60
91
|
`signInWithOAuth` ne redirige plus « en dur » : il renvoie toujours `{ url }` (et redirige en plus via `window.location.href` si un `window` existe).
|
package/dist/index.cjs
CHANGED
|
@@ -52,9 +52,69 @@ var AuthAdminService = class extends core.BaseService {
|
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
54
|
* Génère un lien à usage unique pour un utilisateur (`'magic'`, `'recovery'` ou `'invite'`) et renvoie `{ link, token }`.
|
|
55
|
+
* Accepte `generateLink(userId, type)` ou `generateLink({ userId, type })` ou `generateLink({ email, type })`.
|
|
55
56
|
*/
|
|
56
|
-
async generateLink(
|
|
57
|
-
|
|
57
|
+
async generateLink(userIdOrParams, type) {
|
|
58
|
+
let resolvedUserId;
|
|
59
|
+
let resolvedType;
|
|
60
|
+
let resolvedRedirectTo;
|
|
61
|
+
if (typeof userIdOrParams === "object" && userIdOrParams !== null) {
|
|
62
|
+
resolvedUserId = userIdOrParams.userId ?? userIdOrParams.user_id;
|
|
63
|
+
resolvedType = userIdOrParams.type ?? type;
|
|
64
|
+
resolvedRedirectTo = userIdOrParams.redirectTo ?? userIdOrParams.redirect_to;
|
|
65
|
+
if (!resolvedUserId && userIdOrParams.email) {
|
|
66
|
+
const email = userIdOrParams.email.trim().toLowerCase();
|
|
67
|
+
const searchRes = await this.listUsers({ search: email, limit: 1 });
|
|
68
|
+
const usersList = Array.isArray(searchRes.data) ? searchRes.data : [];
|
|
69
|
+
if (usersList[0]?.id) {
|
|
70
|
+
resolvedUserId = usersList[0].id;
|
|
71
|
+
} else {
|
|
72
|
+
return {
|
|
73
|
+
data: null,
|
|
74
|
+
error: new core.AuraError(404, {
|
|
75
|
+
error: {
|
|
76
|
+
code: "USER_NOT_FOUND",
|
|
77
|
+
message: `No user found with email "${email}".`
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} else if (typeof userIdOrParams === "string") {
|
|
84
|
+
resolvedUserId = userIdOrParams;
|
|
85
|
+
resolvedType = type;
|
|
86
|
+
}
|
|
87
|
+
if (!resolvedUserId || typeof resolvedUserId !== "string" || resolvedUserId.trim() === "" || resolvedUserId.includes("[object")) {
|
|
88
|
+
return {
|
|
89
|
+
data: null,
|
|
90
|
+
error: new core.AuraError(400, {
|
|
91
|
+
error: {
|
|
92
|
+
code: "INVALID_ARGUMENT",
|
|
93
|
+
message: "userId or email is required. Usage: generateLink({ email, type }) or generateLink(userId, type)."
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (!resolvedType) {
|
|
99
|
+
return {
|
|
100
|
+
data: null,
|
|
101
|
+
error: new core.AuraError(400, {
|
|
102
|
+
error: {
|
|
103
|
+
code: "INVALID_ARGUMENT",
|
|
104
|
+
message: 'type is required for generateLink ("magic" | "recovery" | "invite").'
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const body = { type: resolvedType };
|
|
110
|
+
if (resolvedRedirectTo) {
|
|
111
|
+
body.redirect_to = resolvedRedirectTo;
|
|
112
|
+
}
|
|
113
|
+
return this.request(
|
|
114
|
+
"POST",
|
|
115
|
+
`${this.prefix}/users/${encodeURIComponent(resolvedUserId)}/generate-link`,
|
|
116
|
+
body
|
|
117
|
+
);
|
|
58
118
|
}
|
|
59
119
|
/**
|
|
60
120
|
* Impersonation (AUTH-037) : jeton COURT (10 minutes au plus) pour agir en tant que l'utilisateur,
|
|
@@ -536,6 +596,15 @@ var SessionManager = class {
|
|
|
536
596
|
};
|
|
537
597
|
|
|
538
598
|
// src/AuthService.ts
|
|
599
|
+
function extractSessionId(token) {
|
|
600
|
+
if (!token) return void 0;
|
|
601
|
+
try {
|
|
602
|
+
const payload = decodeJwt(token).payload;
|
|
603
|
+
return payload?.session_id ?? payload?.sid ?? void 0;
|
|
604
|
+
} catch {
|
|
605
|
+
return void 0;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
539
608
|
function isMfaChallenge(v) {
|
|
540
609
|
return typeof v === "object" && v !== null && v.mfa_required === true;
|
|
541
610
|
}
|
|
@@ -814,6 +883,7 @@ var AuthService = class extends core.BaseService {
|
|
|
814
883
|
queueMicrotask(() => {
|
|
815
884
|
if (this._stateListeners.has(callback)) {
|
|
816
885
|
const session = store.token && store.user ? {
|
|
886
|
+
id: extractSessionId(store.token),
|
|
817
887
|
access_token: store.token,
|
|
818
888
|
refresh_token: store.refreshToken ?? "",
|
|
819
889
|
token_type: "Bearer",
|
|
@@ -828,6 +898,7 @@ var AuthService = class extends core.BaseService {
|
|
|
828
898
|
_emitEvent(event, user, accessToken, extra) {
|
|
829
899
|
if (event === "SIGNED_OUT") this._signedOutSeq = this._clearSeq;
|
|
830
900
|
const session = accessToken && user ? {
|
|
901
|
+
id: extractSessionId(accessToken),
|
|
831
902
|
access_token: accessToken,
|
|
832
903
|
refresh_token: this.client.authStore.refreshToken ?? "",
|
|
833
904
|
token_type: "Bearer",
|
|
@@ -859,6 +930,7 @@ var AuthService = class extends core.BaseService {
|
|
|
859
930
|
}
|
|
860
931
|
if (req.options?.data) payload.data = req.options.data;
|
|
861
932
|
if (req.options?.emailRedirectTo) payload.email_redirect_to = req.options.emailRedirectTo;
|
|
933
|
+
if (req.options?.preventEmailEnumeration) payload.prevent_email_enumeration = true;
|
|
862
934
|
const res = await this.request("POST", `${this.prefix}/register`, payload);
|
|
863
935
|
if (res.data && !isMfaChallenge(res.data) && !res.data.access_token) {
|
|
864
936
|
res.data.session = null;
|
|
@@ -867,6 +939,7 @@ var AuthService = class extends core.BaseService {
|
|
|
867
939
|
if (res.data && !isMfaChallenge(res.data)) {
|
|
868
940
|
const tp = res.data;
|
|
869
941
|
tp.session = {
|
|
942
|
+
id: extractSessionId(tp.access_token),
|
|
870
943
|
access_token: tp.access_token,
|
|
871
944
|
refresh_token: tp.refresh_token,
|
|
872
945
|
token_type: tp.token_type,
|
|
@@ -902,6 +975,7 @@ var AuthService = class extends core.BaseService {
|
|
|
902
975
|
if (res.data && !isMfaChallenge(res.data)) {
|
|
903
976
|
const tp = res.data;
|
|
904
977
|
tp.session = {
|
|
978
|
+
id: extractSessionId(tp.access_token),
|
|
905
979
|
access_token: tp.access_token,
|
|
906
980
|
refresh_token: tp.refresh_token,
|
|
907
981
|
token_type: tp.token_type,
|
|
@@ -1069,6 +1143,7 @@ var AuthService = class extends core.BaseService {
|
|
|
1069
1143
|
const token = this.client.authStore.token;
|
|
1070
1144
|
const user = this.client.authStore.user;
|
|
1071
1145
|
const session = token && user ? {
|
|
1146
|
+
id: extractSessionId(token),
|
|
1072
1147
|
access_token: token,
|
|
1073
1148
|
refresh_token: this.client.authStore.refreshToken ?? "",
|
|
1074
1149
|
token_type: "Bearer",
|
|
@@ -1460,14 +1535,28 @@ var AuthService = class extends core.BaseService {
|
|
|
1460
1535
|
const code = typeof codeOrParams === "string" ? codeOrParams : codeOrParams.code;
|
|
1461
1536
|
return this.regenerateRecoveryCodes(code);
|
|
1462
1537
|
},
|
|
1463
|
-
factors: (mfaToken) => this.request("POST", `${this.prefix}/mfa/factors`, {
|
|
1538
|
+
factors: (mfaToken) => this.request("POST", `${this.prefix}/mfa/factors`, {
|
|
1539
|
+
mfa_token: mfaToken
|
|
1540
|
+
}),
|
|
1464
1541
|
sms: {
|
|
1465
1542
|
setup: (phone) => this.request("POST", `${this.prefix}/mfa/sms/setup`, { phone }),
|
|
1466
|
-
verifySetup: (code) => this.request(
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1543
|
+
verifySetup: (code) => this.request(
|
|
1544
|
+
"POST",
|
|
1545
|
+
`${this.prefix}/mfa/sms/setup/verify`,
|
|
1546
|
+
{ code }
|
|
1547
|
+
),
|
|
1548
|
+
send: (mfaToken) => this.request(
|
|
1549
|
+
"POST",
|
|
1550
|
+
`${this.prefix}/mfa/sms/send`,
|
|
1551
|
+
{
|
|
1552
|
+
mfa_token: mfaToken
|
|
1553
|
+
}
|
|
1554
|
+
),
|
|
1555
|
+
remove: (password) => this.request(
|
|
1556
|
+
"DELETE",
|
|
1557
|
+
`${this.prefix}/mfa/sms`,
|
|
1558
|
+
{ password }
|
|
1559
|
+
)
|
|
1471
1560
|
},
|
|
1472
1561
|
webauthn: {
|
|
1473
1562
|
register: (params) => this.webauthnRegister(params),
|
|
@@ -1481,6 +1570,13 @@ var AuthService = class extends core.BaseService {
|
|
|
1481
1570
|
}
|
|
1482
1571
|
};
|
|
1483
1572
|
}
|
|
1573
|
+
/**
|
|
1574
|
+
* API d'administration de l'authentification (alias vers `client.authAdmin`).
|
|
1575
|
+
* Exige une clé `service_role` (côté serveur uniquement).
|
|
1576
|
+
*/
|
|
1577
|
+
get admin() {
|
|
1578
|
+
return this.client.authAdmin;
|
|
1579
|
+
}
|
|
1484
1580
|
webauthnIndisponible() {
|
|
1485
1581
|
return {
|
|
1486
1582
|
data: null,
|
|
@@ -1505,7 +1601,7 @@ var AuthService = class extends core.BaseService {
|
|
|
1505
1601
|
`${this.prefix}/mfa/webauthn/register/start`,
|
|
1506
1602
|
{ friendly_name: params?.friendlyName }
|
|
1507
1603
|
);
|
|
1508
|
-
if (!debut.data) return debut;
|
|
1604
|
+
if (!debut.data) return { data: null, error: debut.error };
|
|
1509
1605
|
let cred;
|
|
1510
1606
|
try {
|
|
1511
1607
|
cred = await conteneur.create(creationOptionsFromJson(debut.data.options));
|
|
@@ -1513,10 +1609,14 @@ var AuthService = class extends core.BaseService {
|
|
|
1513
1609
|
return this.webauthnAnnule(e);
|
|
1514
1610
|
}
|
|
1515
1611
|
if (!cred) return this.webauthnAnnule(new Error("no credential returned"));
|
|
1516
|
-
return this.request(
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1612
|
+
return this.request(
|
|
1613
|
+
"POST",
|
|
1614
|
+
`${this.prefix}/mfa/webauthn/register/finish`,
|
|
1615
|
+
{
|
|
1616
|
+
credential: attestationToJson(cred),
|
|
1617
|
+
friendly_name: params?.friendlyName
|
|
1618
|
+
}
|
|
1619
|
+
);
|
|
1520
1620
|
}
|
|
1521
1621
|
/**
|
|
1522
1622
|
* Relève un défi MFA (`mfaToken`, reçu d'un `signIn`) avec une passkey : options du serveur,
|
|
@@ -1530,7 +1630,7 @@ var AuthService = class extends core.BaseService {
|
|
|
1530
1630
|
`${this.prefix}/mfa/webauthn/verify/start`,
|
|
1531
1631
|
{ mfa_token: params.mfaToken }
|
|
1532
1632
|
);
|
|
1533
|
-
if (!debut.data) return debut;
|
|
1633
|
+
if (!debut.data) return { data: null, error: debut.error };
|
|
1534
1634
|
let cred;
|
|
1535
1635
|
try {
|
|
1536
1636
|
cred = await conteneur.get(requestOptionsFromJson(debut.data.options));
|
|
@@ -1572,10 +1672,31 @@ var AuthService = class extends core.BaseService {
|
|
|
1572
1672
|
* MFA : termine une connexion qui a renvoyé un défi (`mfa_token` et code TOTP). Enregistre la session et émet `SIGNED_IN`.
|
|
1573
1673
|
*/
|
|
1574
1674
|
async mfaVerify(req) {
|
|
1575
|
-
const
|
|
1675
|
+
const raw = req;
|
|
1676
|
+
const mfa_token = raw.mfa_token ?? raw.mfaToken ?? raw.challenge_id ?? raw.challengeId;
|
|
1677
|
+
const code = raw.code;
|
|
1678
|
+
const recovery_code = raw.recovery_code ?? raw.recoveryCode;
|
|
1679
|
+
const sms_code = raw.sms_code ?? raw.smsCode;
|
|
1680
|
+
const body = {};
|
|
1681
|
+
if (mfa_token) body.mfa_token = mfa_token;
|
|
1682
|
+
if (code !== void 0) body.code = code;
|
|
1683
|
+
if (recovery_code !== void 0) body.recovery_code = recovery_code;
|
|
1684
|
+
if (sms_code !== void 0) body.sms_code = sms_code;
|
|
1685
|
+
const res = await this.request("POST", `${this.prefix}/mfa/verify-login`, body);
|
|
1576
1686
|
if (res.data) {
|
|
1577
|
-
|
|
1578
|
-
|
|
1687
|
+
const tp = res.data;
|
|
1688
|
+
tp.session = {
|
|
1689
|
+
id: extractSessionId(tp.access_token),
|
|
1690
|
+
access_token: tp.access_token,
|
|
1691
|
+
refresh_token: tp.refresh_token,
|
|
1692
|
+
token_type: tp.token_type,
|
|
1693
|
+
expires_in: tp.expires_in,
|
|
1694
|
+
user: tp.user,
|
|
1695
|
+
provider_token: tp.provider_token,
|
|
1696
|
+
provider_refresh_token: tp.provider_refresh_token
|
|
1697
|
+
};
|
|
1698
|
+
this.client.authStore.save(tp.access_token, tp.refresh_token, tp.user);
|
|
1699
|
+
this._emitEvent("SIGNED_IN", tp.user, tp.access_token);
|
|
1579
1700
|
}
|
|
1580
1701
|
return res;
|
|
1581
1702
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -72,8 +72,16 @@ declare class AuthAdminService extends BaseService {
|
|
|
72
72
|
unbanUser(userId: string): Promise<AuraResponse<null>>;
|
|
73
73
|
/**
|
|
74
74
|
* Génère un lien à usage unique pour un utilisateur (`'magic'`, `'recovery'` ou `'invite'`) et renvoie `{ link, token }`.
|
|
75
|
+
* Accepte `generateLink(userId, type)` ou `generateLink({ userId, type })` ou `generateLink({ email, type })`.
|
|
75
76
|
*/
|
|
76
|
-
generateLink(
|
|
77
|
+
generateLink(userIdOrParams: string | {
|
|
78
|
+
userId?: string;
|
|
79
|
+
user_id?: string;
|
|
80
|
+
email?: string;
|
|
81
|
+
type: GenerateLinkType;
|
|
82
|
+
redirectTo?: string;
|
|
83
|
+
redirect_to?: string;
|
|
84
|
+
}, type?: GenerateLinkType): Promise<AuraResponse<GenerateLinkResult>>;
|
|
77
85
|
/**
|
|
78
86
|
* Impersonation (AUTH-037) : jeton COURT (10 minutes au plus) pour agir en tant que l'utilisateur,
|
|
79
87
|
* avec le claim `act: { sub }`. Pas de refresh token, journalisé (`admin_impersonation`).
|
|
@@ -568,6 +576,11 @@ declare class AuthService extends BaseService {
|
|
|
568
576
|
}>>;
|
|
569
577
|
};
|
|
570
578
|
};
|
|
579
|
+
/**
|
|
580
|
+
* API d'administration de l'authentification (alias vers `client.authAdmin`).
|
|
581
|
+
* Exige une clé `service_role` (côté serveur uniquement).
|
|
582
|
+
*/
|
|
583
|
+
get admin(): AuthAdminService;
|
|
571
584
|
private webauthnIndisponible;
|
|
572
585
|
/**
|
|
573
586
|
* Enrôle une passkey WebAuthn pour l'utilisateur connecté (cérémonie complète : options du serveur,
|
package/dist/index.d.ts
CHANGED
|
@@ -72,8 +72,16 @@ declare class AuthAdminService extends BaseService {
|
|
|
72
72
|
unbanUser(userId: string): Promise<AuraResponse<null>>;
|
|
73
73
|
/**
|
|
74
74
|
* Génère un lien à usage unique pour un utilisateur (`'magic'`, `'recovery'` ou `'invite'`) et renvoie `{ link, token }`.
|
|
75
|
+
* Accepte `generateLink(userId, type)` ou `generateLink({ userId, type })` ou `generateLink({ email, type })`.
|
|
75
76
|
*/
|
|
76
|
-
generateLink(
|
|
77
|
+
generateLink(userIdOrParams: string | {
|
|
78
|
+
userId?: string;
|
|
79
|
+
user_id?: string;
|
|
80
|
+
email?: string;
|
|
81
|
+
type: GenerateLinkType;
|
|
82
|
+
redirectTo?: string;
|
|
83
|
+
redirect_to?: string;
|
|
84
|
+
}, type?: GenerateLinkType): Promise<AuraResponse<GenerateLinkResult>>;
|
|
77
85
|
/**
|
|
78
86
|
* Impersonation (AUTH-037) : jeton COURT (10 minutes au plus) pour agir en tant que l'utilisateur,
|
|
79
87
|
* avec le claim `act: { sub }`. Pas de refresh token, journalisé (`admin_impersonation`).
|
|
@@ -568,6 +576,11 @@ declare class AuthService extends BaseService {
|
|
|
568
576
|
}>>;
|
|
569
577
|
};
|
|
570
578
|
};
|
|
579
|
+
/**
|
|
580
|
+
* API d'administration de l'authentification (alias vers `client.authAdmin`).
|
|
581
|
+
* Exige une clé `service_role` (côté serveur uniquement).
|
|
582
|
+
*/
|
|
583
|
+
get admin(): AuthAdminService;
|
|
571
584
|
private webauthnIndisponible;
|
|
572
585
|
/**
|
|
573
586
|
* Enrôle une passkey WebAuthn pour l'utilisateur connecté (cérémonie complète : options du serveur,
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseService, isTokenExpired, clientScope,
|
|
1
|
+
import { BaseService, AuraError, isTokenExpired, clientScope, refreshOnce, DEFAULT_REQUEST_TIMEOUT_MS, emitAuraEvent } from '@aurabase/core';
|
|
2
2
|
|
|
3
3
|
// src/AuthAdminService.ts
|
|
4
4
|
var AuthAdminService = class extends BaseService {
|
|
@@ -50,9 +50,69 @@ var AuthAdminService = class extends BaseService {
|
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
52
|
* Génère un lien à usage unique pour un utilisateur (`'magic'`, `'recovery'` ou `'invite'`) et renvoie `{ link, token }`.
|
|
53
|
+
* Accepte `generateLink(userId, type)` ou `generateLink({ userId, type })` ou `generateLink({ email, type })`.
|
|
53
54
|
*/
|
|
54
|
-
async generateLink(
|
|
55
|
-
|
|
55
|
+
async generateLink(userIdOrParams, type) {
|
|
56
|
+
let resolvedUserId;
|
|
57
|
+
let resolvedType;
|
|
58
|
+
let resolvedRedirectTo;
|
|
59
|
+
if (typeof userIdOrParams === "object" && userIdOrParams !== null) {
|
|
60
|
+
resolvedUserId = userIdOrParams.userId ?? userIdOrParams.user_id;
|
|
61
|
+
resolvedType = userIdOrParams.type ?? type;
|
|
62
|
+
resolvedRedirectTo = userIdOrParams.redirectTo ?? userIdOrParams.redirect_to;
|
|
63
|
+
if (!resolvedUserId && userIdOrParams.email) {
|
|
64
|
+
const email = userIdOrParams.email.trim().toLowerCase();
|
|
65
|
+
const searchRes = await this.listUsers({ search: email, limit: 1 });
|
|
66
|
+
const usersList = Array.isArray(searchRes.data) ? searchRes.data : [];
|
|
67
|
+
if (usersList[0]?.id) {
|
|
68
|
+
resolvedUserId = usersList[0].id;
|
|
69
|
+
} else {
|
|
70
|
+
return {
|
|
71
|
+
data: null,
|
|
72
|
+
error: new AuraError(404, {
|
|
73
|
+
error: {
|
|
74
|
+
code: "USER_NOT_FOUND",
|
|
75
|
+
message: `No user found with email "${email}".`
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} else if (typeof userIdOrParams === "string") {
|
|
82
|
+
resolvedUserId = userIdOrParams;
|
|
83
|
+
resolvedType = type;
|
|
84
|
+
}
|
|
85
|
+
if (!resolvedUserId || typeof resolvedUserId !== "string" || resolvedUserId.trim() === "" || resolvedUserId.includes("[object")) {
|
|
86
|
+
return {
|
|
87
|
+
data: null,
|
|
88
|
+
error: new AuraError(400, {
|
|
89
|
+
error: {
|
|
90
|
+
code: "INVALID_ARGUMENT",
|
|
91
|
+
message: "userId or email is required. Usage: generateLink({ email, type }) or generateLink(userId, type)."
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (!resolvedType) {
|
|
97
|
+
return {
|
|
98
|
+
data: null,
|
|
99
|
+
error: new AuraError(400, {
|
|
100
|
+
error: {
|
|
101
|
+
code: "INVALID_ARGUMENT",
|
|
102
|
+
message: 'type is required for generateLink ("magic" | "recovery" | "invite").'
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const body = { type: resolvedType };
|
|
108
|
+
if (resolvedRedirectTo) {
|
|
109
|
+
body.redirect_to = resolvedRedirectTo;
|
|
110
|
+
}
|
|
111
|
+
return this.request(
|
|
112
|
+
"POST",
|
|
113
|
+
`${this.prefix}/users/${encodeURIComponent(resolvedUserId)}/generate-link`,
|
|
114
|
+
body
|
|
115
|
+
);
|
|
56
116
|
}
|
|
57
117
|
/**
|
|
58
118
|
* Impersonation (AUTH-037) : jeton COURT (10 minutes au plus) pour agir en tant que l'utilisateur,
|
|
@@ -534,6 +594,15 @@ var SessionManager = class {
|
|
|
534
594
|
};
|
|
535
595
|
|
|
536
596
|
// src/AuthService.ts
|
|
597
|
+
function extractSessionId(token) {
|
|
598
|
+
if (!token) return void 0;
|
|
599
|
+
try {
|
|
600
|
+
const payload = decodeJwt(token).payload;
|
|
601
|
+
return payload?.session_id ?? payload?.sid ?? void 0;
|
|
602
|
+
} catch {
|
|
603
|
+
return void 0;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
537
606
|
function isMfaChallenge(v) {
|
|
538
607
|
return typeof v === "object" && v !== null && v.mfa_required === true;
|
|
539
608
|
}
|
|
@@ -812,6 +881,7 @@ var AuthService = class extends BaseService {
|
|
|
812
881
|
queueMicrotask(() => {
|
|
813
882
|
if (this._stateListeners.has(callback)) {
|
|
814
883
|
const session = store.token && store.user ? {
|
|
884
|
+
id: extractSessionId(store.token),
|
|
815
885
|
access_token: store.token,
|
|
816
886
|
refresh_token: store.refreshToken ?? "",
|
|
817
887
|
token_type: "Bearer",
|
|
@@ -826,6 +896,7 @@ var AuthService = class extends BaseService {
|
|
|
826
896
|
_emitEvent(event, user, accessToken, extra) {
|
|
827
897
|
if (event === "SIGNED_OUT") this._signedOutSeq = this._clearSeq;
|
|
828
898
|
const session = accessToken && user ? {
|
|
899
|
+
id: extractSessionId(accessToken),
|
|
829
900
|
access_token: accessToken,
|
|
830
901
|
refresh_token: this.client.authStore.refreshToken ?? "",
|
|
831
902
|
token_type: "Bearer",
|
|
@@ -857,6 +928,7 @@ var AuthService = class extends BaseService {
|
|
|
857
928
|
}
|
|
858
929
|
if (req.options?.data) payload.data = req.options.data;
|
|
859
930
|
if (req.options?.emailRedirectTo) payload.email_redirect_to = req.options.emailRedirectTo;
|
|
931
|
+
if (req.options?.preventEmailEnumeration) payload.prevent_email_enumeration = true;
|
|
860
932
|
const res = await this.request("POST", `${this.prefix}/register`, payload);
|
|
861
933
|
if (res.data && !isMfaChallenge(res.data) && !res.data.access_token) {
|
|
862
934
|
res.data.session = null;
|
|
@@ -865,6 +937,7 @@ var AuthService = class extends BaseService {
|
|
|
865
937
|
if (res.data && !isMfaChallenge(res.data)) {
|
|
866
938
|
const tp = res.data;
|
|
867
939
|
tp.session = {
|
|
940
|
+
id: extractSessionId(tp.access_token),
|
|
868
941
|
access_token: tp.access_token,
|
|
869
942
|
refresh_token: tp.refresh_token,
|
|
870
943
|
token_type: tp.token_type,
|
|
@@ -900,6 +973,7 @@ var AuthService = class extends BaseService {
|
|
|
900
973
|
if (res.data && !isMfaChallenge(res.data)) {
|
|
901
974
|
const tp = res.data;
|
|
902
975
|
tp.session = {
|
|
976
|
+
id: extractSessionId(tp.access_token),
|
|
903
977
|
access_token: tp.access_token,
|
|
904
978
|
refresh_token: tp.refresh_token,
|
|
905
979
|
token_type: tp.token_type,
|
|
@@ -1067,6 +1141,7 @@ var AuthService = class extends BaseService {
|
|
|
1067
1141
|
const token = this.client.authStore.token;
|
|
1068
1142
|
const user = this.client.authStore.user;
|
|
1069
1143
|
const session = token && user ? {
|
|
1144
|
+
id: extractSessionId(token),
|
|
1070
1145
|
access_token: token,
|
|
1071
1146
|
refresh_token: this.client.authStore.refreshToken ?? "",
|
|
1072
1147
|
token_type: "Bearer",
|
|
@@ -1458,14 +1533,28 @@ var AuthService = class extends BaseService {
|
|
|
1458
1533
|
const code = typeof codeOrParams === "string" ? codeOrParams : codeOrParams.code;
|
|
1459
1534
|
return this.regenerateRecoveryCodes(code);
|
|
1460
1535
|
},
|
|
1461
|
-
factors: (mfaToken) => this.request("POST", `${this.prefix}/mfa/factors`, {
|
|
1536
|
+
factors: (mfaToken) => this.request("POST", `${this.prefix}/mfa/factors`, {
|
|
1537
|
+
mfa_token: mfaToken
|
|
1538
|
+
}),
|
|
1462
1539
|
sms: {
|
|
1463
1540
|
setup: (phone) => this.request("POST", `${this.prefix}/mfa/sms/setup`, { phone }),
|
|
1464
|
-
verifySetup: (code) => this.request(
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1541
|
+
verifySetup: (code) => this.request(
|
|
1542
|
+
"POST",
|
|
1543
|
+
`${this.prefix}/mfa/sms/setup/verify`,
|
|
1544
|
+
{ code }
|
|
1545
|
+
),
|
|
1546
|
+
send: (mfaToken) => this.request(
|
|
1547
|
+
"POST",
|
|
1548
|
+
`${this.prefix}/mfa/sms/send`,
|
|
1549
|
+
{
|
|
1550
|
+
mfa_token: mfaToken
|
|
1551
|
+
}
|
|
1552
|
+
),
|
|
1553
|
+
remove: (password) => this.request(
|
|
1554
|
+
"DELETE",
|
|
1555
|
+
`${this.prefix}/mfa/sms`,
|
|
1556
|
+
{ password }
|
|
1557
|
+
)
|
|
1469
1558
|
},
|
|
1470
1559
|
webauthn: {
|
|
1471
1560
|
register: (params) => this.webauthnRegister(params),
|
|
@@ -1479,6 +1568,13 @@ var AuthService = class extends BaseService {
|
|
|
1479
1568
|
}
|
|
1480
1569
|
};
|
|
1481
1570
|
}
|
|
1571
|
+
/**
|
|
1572
|
+
* API d'administration de l'authentification (alias vers `client.authAdmin`).
|
|
1573
|
+
* Exige une clé `service_role` (côté serveur uniquement).
|
|
1574
|
+
*/
|
|
1575
|
+
get admin() {
|
|
1576
|
+
return this.client.authAdmin;
|
|
1577
|
+
}
|
|
1482
1578
|
webauthnIndisponible() {
|
|
1483
1579
|
return {
|
|
1484
1580
|
data: null,
|
|
@@ -1503,7 +1599,7 @@ var AuthService = class extends BaseService {
|
|
|
1503
1599
|
`${this.prefix}/mfa/webauthn/register/start`,
|
|
1504
1600
|
{ friendly_name: params?.friendlyName }
|
|
1505
1601
|
);
|
|
1506
|
-
if (!debut.data) return debut;
|
|
1602
|
+
if (!debut.data) return { data: null, error: debut.error };
|
|
1507
1603
|
let cred;
|
|
1508
1604
|
try {
|
|
1509
1605
|
cred = await conteneur.create(creationOptionsFromJson(debut.data.options));
|
|
@@ -1511,10 +1607,14 @@ var AuthService = class extends BaseService {
|
|
|
1511
1607
|
return this.webauthnAnnule(e);
|
|
1512
1608
|
}
|
|
1513
1609
|
if (!cred) return this.webauthnAnnule(new Error("no credential returned"));
|
|
1514
|
-
return this.request(
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1610
|
+
return this.request(
|
|
1611
|
+
"POST",
|
|
1612
|
+
`${this.prefix}/mfa/webauthn/register/finish`,
|
|
1613
|
+
{
|
|
1614
|
+
credential: attestationToJson(cred),
|
|
1615
|
+
friendly_name: params?.friendlyName
|
|
1616
|
+
}
|
|
1617
|
+
);
|
|
1518
1618
|
}
|
|
1519
1619
|
/**
|
|
1520
1620
|
* Relève un défi MFA (`mfaToken`, reçu d'un `signIn`) avec une passkey : options du serveur,
|
|
@@ -1528,7 +1628,7 @@ var AuthService = class extends BaseService {
|
|
|
1528
1628
|
`${this.prefix}/mfa/webauthn/verify/start`,
|
|
1529
1629
|
{ mfa_token: params.mfaToken }
|
|
1530
1630
|
);
|
|
1531
|
-
if (!debut.data) return debut;
|
|
1631
|
+
if (!debut.data) return { data: null, error: debut.error };
|
|
1532
1632
|
let cred;
|
|
1533
1633
|
try {
|
|
1534
1634
|
cred = await conteneur.get(requestOptionsFromJson(debut.data.options));
|
|
@@ -1570,10 +1670,31 @@ var AuthService = class extends BaseService {
|
|
|
1570
1670
|
* MFA : termine une connexion qui a renvoyé un défi (`mfa_token` et code TOTP). Enregistre la session et émet `SIGNED_IN`.
|
|
1571
1671
|
*/
|
|
1572
1672
|
async mfaVerify(req) {
|
|
1573
|
-
const
|
|
1673
|
+
const raw = req;
|
|
1674
|
+
const mfa_token = raw.mfa_token ?? raw.mfaToken ?? raw.challenge_id ?? raw.challengeId;
|
|
1675
|
+
const code = raw.code;
|
|
1676
|
+
const recovery_code = raw.recovery_code ?? raw.recoveryCode;
|
|
1677
|
+
const sms_code = raw.sms_code ?? raw.smsCode;
|
|
1678
|
+
const body = {};
|
|
1679
|
+
if (mfa_token) body.mfa_token = mfa_token;
|
|
1680
|
+
if (code !== void 0) body.code = code;
|
|
1681
|
+
if (recovery_code !== void 0) body.recovery_code = recovery_code;
|
|
1682
|
+
if (sms_code !== void 0) body.sms_code = sms_code;
|
|
1683
|
+
const res = await this.request("POST", `${this.prefix}/mfa/verify-login`, body);
|
|
1574
1684
|
if (res.data) {
|
|
1575
|
-
|
|
1576
|
-
|
|
1685
|
+
const tp = res.data;
|
|
1686
|
+
tp.session = {
|
|
1687
|
+
id: extractSessionId(tp.access_token),
|
|
1688
|
+
access_token: tp.access_token,
|
|
1689
|
+
refresh_token: tp.refresh_token,
|
|
1690
|
+
token_type: tp.token_type,
|
|
1691
|
+
expires_in: tp.expires_in,
|
|
1692
|
+
user: tp.user,
|
|
1693
|
+
provider_token: tp.provider_token,
|
|
1694
|
+
provider_refresh_token: tp.provider_refresh_token
|
|
1695
|
+
};
|
|
1696
|
+
this.client.authStore.save(tp.access_token, tp.refresh_token, tp.user);
|
|
1697
|
+
this._emitEvent("SIGNED_IN", tp.user, tp.access_token);
|
|
1577
1698
|
}
|
|
1578
1699
|
return res;
|
|
1579
1700
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aurabase/auth",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Service d'authentification du SDK Aurabase (sessions, OAuth/PKCE, MFA, magic link, OTP, admin)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"authentication"
|
|
47
47
|
],
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@aurabase/core": "0.7.
|
|
49
|
+
"@aurabase/core": "0.7.2"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/node": "20.14.15",
|