@sentroy-co/client-sdk 2.13.8 → 2.13.9
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/auth/admin/index.d.ts +111 -10
- package/dist/auth/admin/index.d.ts.map +1 -1
- package/dist/auth/admin/index.js +125 -20
- package/dist/auth/admin/index.js.map +1 -1
- package/dist/auth/client.d.ts +127 -1
- package/dist/auth/client.d.ts.map +1 -1
- package/dist/auth/client.js +361 -3
- package/dist/auth/client.js.map +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/react/index.d.ts +63 -4
- package/dist/auth/react/index.d.ts.map +1 -1
- package/dist/auth/react/index.js +180 -1
- package/dist/auth/react/index.js.map +1 -1
- package/dist/auth/types.d.ts +55 -0
- package/dist/auth/types.d.ts.map +1 -1
- package/package.json +5 -1
- package/src/auth/admin/index.ts +227 -31
- package/src/auth/client.ts +438 -4
- package/src/auth/index.ts +9 -0
- package/src/auth/react/index.tsx +255 -4
- package/src/auth/types.ts +66 -0
package/src/auth/client.ts
CHANGED
|
@@ -3,6 +3,14 @@ import {
|
|
|
3
3
|
type SignupResponse,
|
|
4
4
|
type LoginResponse,
|
|
5
5
|
type AuthTokensResponse,
|
|
6
|
+
type LoginOutcome,
|
|
7
|
+
type SessionSummary,
|
|
8
|
+
type ActivityEntry,
|
|
9
|
+
type MfaStatus,
|
|
10
|
+
type MfaEnrollResponse,
|
|
11
|
+
type MfaVerifyEnrollmentResponse,
|
|
12
|
+
type PasskeySummary,
|
|
13
|
+
type SocialProvider,
|
|
6
14
|
} from "./types"
|
|
7
15
|
import { AuthHttp, type AuthHttpOptions } from "./http"
|
|
8
16
|
|
|
@@ -61,6 +69,37 @@ function decodeBase64Url(s: string): string {
|
|
|
61
69
|
throw new Error("No base64 decoder available")
|
|
62
70
|
}
|
|
63
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Optional `@simplewebauthn/browser` import — RP webauthn flow kullanmak
|
|
74
|
+
* isterse kendi devDependencies'ine ekler; lazy import ile bundle'a
|
|
75
|
+
* sızmaz.
|
|
76
|
+
*/
|
|
77
|
+
async function loadSimpleWebAuthnBrowser(): Promise<{
|
|
78
|
+
startRegistration: (opts: {
|
|
79
|
+
optionsJSON: Record<string, unknown>
|
|
80
|
+
}) => Promise<unknown>
|
|
81
|
+
startAuthentication: (opts: {
|
|
82
|
+
optionsJSON: Record<string, unknown>
|
|
83
|
+
}) => Promise<unknown>
|
|
84
|
+
}> {
|
|
85
|
+
try {
|
|
86
|
+
// String-concat hides the specifier from TS resolver — `@simplewebauthn/browser`
|
|
87
|
+
// is an optional peer; SDK ships without it bundled. RP'nin npm install'unda
|
|
88
|
+
// varsa runtime'da çözülür, yoksa catch'e düşüp kullanıcıya net hata verir.
|
|
89
|
+
const specifier = "@simplewebauthn/" + "browser"
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
91
|
+
const mod: any = await import(/* @vite-ignore */ specifier)
|
|
92
|
+
return {
|
|
93
|
+
startRegistration: mod.startRegistration,
|
|
94
|
+
startAuthentication: mod.startAuthentication,
|
|
95
|
+
}
|
|
96
|
+
} catch {
|
|
97
|
+
throw new Error(
|
|
98
|
+
"Passkey support requires `@simplewebauthn/browser` — install it as a peer dependency.",
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
64
103
|
function localStorageAdapter(projectSlug: string): AuthStorageAdapter {
|
|
65
104
|
if (typeof window === "undefined" || !window.localStorage) {
|
|
66
105
|
return memoryStorageAdapter()
|
|
@@ -186,14 +225,45 @@ export class SentroyAuth {
|
|
|
186
225
|
return res
|
|
187
226
|
}
|
|
188
227
|
|
|
228
|
+
/**
|
|
229
|
+
* Sign in with email/password. MFA enrolled user'lar için response
|
|
230
|
+
* discriminated union: `kind: "mfa"` → caller `verifyMfa()` çağırır.
|
|
231
|
+
* `kind: "tokens"` → session kuruldu.
|
|
232
|
+
*/
|
|
189
233
|
async signIn(input: {
|
|
190
234
|
email: string
|
|
191
235
|
password: string
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
236
|
+
rememberMe?: boolean
|
|
237
|
+
}): Promise<LoginOutcome> {
|
|
238
|
+
const res = await this.http.request<
|
|
239
|
+
LoginResponse | { mfaRequired: true; mfaToken: string; factorType: "totp" }
|
|
240
|
+
>("/login", { method: "POST", json: input })
|
|
241
|
+
if ("mfaRequired" in res && res.mfaRequired) {
|
|
242
|
+
return { kind: "mfa", data: res }
|
|
243
|
+
}
|
|
244
|
+
const tokens = res as LoginResponse
|
|
245
|
+
this.persist({
|
|
246
|
+
accessToken: tokens.accessToken,
|
|
247
|
+
refreshToken: tokens.refreshToken,
|
|
248
|
+
user: tokens.user,
|
|
196
249
|
})
|
|
250
|
+
return { kind: "tokens", data: tokens }
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* MFA verify — `signIn` ile `kind: "mfa"` döndüyse, kullanıcıdan code
|
|
255
|
+
* (veya recovery code) alıp bu method'u çağır. Başarılıysa session
|
|
256
|
+
* kurulur ve login tamamlanır.
|
|
257
|
+
*/
|
|
258
|
+
async verifyMfa(input: {
|
|
259
|
+
mfaToken: string
|
|
260
|
+
code?: string
|
|
261
|
+
recoveryCode?: string
|
|
262
|
+
}): Promise<LoginResponse> {
|
|
263
|
+
const res = await this.http.request<LoginResponse>(
|
|
264
|
+
"/login/mfa/verify",
|
|
265
|
+
{ method: "POST", json: input },
|
|
266
|
+
)
|
|
197
267
|
this.persist({
|
|
198
268
|
accessToken: res.accessToken,
|
|
199
269
|
refreshToken: res.refreshToken,
|
|
@@ -224,6 +294,21 @@ export class SentroyAuth {
|
|
|
224
294
|
})
|
|
225
295
|
}
|
|
226
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Reset password using token from email. `newPassword` policy +
|
|
299
|
+
* HaveIBeenPwned breach check yapılır.
|
|
300
|
+
*/
|
|
301
|
+
async confirmPasswordReset(input: {
|
|
302
|
+
token: string
|
|
303
|
+
newPassword: string
|
|
304
|
+
}): Promise<SentroyAuthUser> {
|
|
305
|
+
const res = await this.http.request<{ user: SentroyAuthUser }>(
|
|
306
|
+
"/password-reset/confirm",
|
|
307
|
+
{ method: "POST", json: input },
|
|
308
|
+
)
|
|
309
|
+
return res.user
|
|
310
|
+
}
|
|
311
|
+
|
|
227
312
|
async verifyEmail(token: string): Promise<SentroyAuthUser> {
|
|
228
313
|
const res = await this.http.request<{ user: SentroyAuthUser }>(
|
|
229
314
|
"/verify-email",
|
|
@@ -241,6 +326,355 @@ export class SentroyAuth {
|
|
|
241
326
|
return res.user
|
|
242
327
|
}
|
|
243
328
|
|
|
329
|
+
// ─── Magic link ──────────────────────────────────────────────────────────
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Email magic-link request. Project'in `magicLinkEnabled` true
|
|
333
|
+
* olması gerekir. Uniform 200 response — email yoksa da hata vermez.
|
|
334
|
+
*/
|
|
335
|
+
async sendMagicLink(input: { email: string; redirectUri?: string }): Promise<void> {
|
|
336
|
+
await this.http.request("/magic-link/request", {
|
|
337
|
+
method: "POST",
|
|
338
|
+
json: input,
|
|
339
|
+
})
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Magic link mail'inden gelen token ile login. Session kurulur.
|
|
344
|
+
*/
|
|
345
|
+
async consumeMagicLink(token: string): Promise<LoginResponse> {
|
|
346
|
+
const res = await this.http.request<LoginResponse & { redirectUri?: string | null }>(
|
|
347
|
+
"/magic-link/consume",
|
|
348
|
+
{ method: "POST", json: { token } },
|
|
349
|
+
)
|
|
350
|
+
this.persist({
|
|
351
|
+
accessToken: res.accessToken,
|
|
352
|
+
refreshToken: res.refreshToken,
|
|
353
|
+
user: res.user,
|
|
354
|
+
})
|
|
355
|
+
return res
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// ─── Invitation ──────────────────────────────────────────────────────────
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Accept admin invitation. Token mail'den gelir, kullanıcı password
|
|
362
|
+
* + optional displayName girer; hesap create + session kurulur.
|
|
363
|
+
*/
|
|
364
|
+
async acceptInvitation(input: {
|
|
365
|
+
token: string
|
|
366
|
+
password: string
|
|
367
|
+
displayName?: string
|
|
368
|
+
}): Promise<LoginResponse> {
|
|
369
|
+
const res = await this.http.request<LoginResponse>(
|
|
370
|
+
"/invitation/accept",
|
|
371
|
+
{ method: "POST", json: input },
|
|
372
|
+
)
|
|
373
|
+
this.persist({
|
|
374
|
+
accessToken: res.accessToken,
|
|
375
|
+
refreshToken: res.refreshToken,
|
|
376
|
+
user: res.user,
|
|
377
|
+
})
|
|
378
|
+
return res
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// ─── Social federation ───────────────────────────────────────────────────
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Provider authorize URL üret. `window.location.assign(url)` ile
|
|
385
|
+
* RP'nin sayfasından redirect — callback'te Sentroy session kurulur,
|
|
386
|
+
* redirectUri fragment'ında token'lar döner.
|
|
387
|
+
*/
|
|
388
|
+
socialAuthorizeUrl(
|
|
389
|
+
provider: SocialProvider,
|
|
390
|
+
opts: { redirectUri?: string; rememberMe?: boolean } = {},
|
|
391
|
+
): string {
|
|
392
|
+
const params = new URLSearchParams()
|
|
393
|
+
if (opts.redirectUri) params.set("redirectUri", opts.redirectUri)
|
|
394
|
+
if (opts.rememberMe) params.set("rememberMe", "1")
|
|
395
|
+
const qs = params.toString()
|
|
396
|
+
return `${this.http.baseUrl}/api/v1/auth/${this.http.projectSlug}/social/${provider}/authorize${qs ? `?${qs}` : ""}`
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* `window.location.hash`tan social login redirect sonrası gelen
|
|
401
|
+
* `#access_token=...&refresh_token=...&token_type=Bearer` parse +
|
|
402
|
+
* session kur. RP sayfasına redirectUri varsayılan akış kullanıldıysa
|
|
403
|
+
* çağırın. Başarılıysa user döner, fail'da null.
|
|
404
|
+
*/
|
|
405
|
+
async consumeRedirectFragment(): Promise<SentroyAuthUser | null> {
|
|
406
|
+
if (typeof window === "undefined") return null
|
|
407
|
+
const hash = window.location.hash.replace(/^#/, "")
|
|
408
|
+
if (!hash) return null
|
|
409
|
+
const params = new URLSearchParams(hash)
|
|
410
|
+
const accessToken = params.get("access_token")
|
|
411
|
+
const refreshToken = params.get("refresh_token")
|
|
412
|
+
if (!accessToken || !refreshToken) return null
|
|
413
|
+
// Fragment'ı URL'den temizle (history clean)
|
|
414
|
+
window.history.replaceState(
|
|
415
|
+
null,
|
|
416
|
+
"",
|
|
417
|
+
window.location.pathname + window.location.search,
|
|
418
|
+
)
|
|
419
|
+
const user = await this.fetchMe(accessToken)
|
|
420
|
+
if (!user) return null
|
|
421
|
+
this.persist({ accessToken, refreshToken, user })
|
|
422
|
+
return user
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// ─── /me (current user info) ─────────────────────────────────────────────
|
|
426
|
+
|
|
427
|
+
async getCurrentUser(): Promise<SentroyAuthUser | null> {
|
|
428
|
+
const restored = this.storage.read()
|
|
429
|
+
if (!restored) return null
|
|
430
|
+
const user = await this.fetchMe(restored.accessToken)
|
|
431
|
+
if (user) {
|
|
432
|
+
this.persist({ ...restored, user })
|
|
433
|
+
}
|
|
434
|
+
return user
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
private async fetchMe(accessToken: string): Promise<SentroyAuthUser | null> {
|
|
438
|
+
try {
|
|
439
|
+
return await this.http.request<SentroyAuthUser>("/me", {
|
|
440
|
+
method: "GET",
|
|
441
|
+
bearer: accessToken,
|
|
442
|
+
})
|
|
443
|
+
} catch {
|
|
444
|
+
return null
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// ─── /me/sessions ────────────────────────────────────────────────────────
|
|
449
|
+
|
|
450
|
+
async listSessions(): Promise<SessionSummary[]> {
|
|
451
|
+
return this.http.request<SessionSummary[]>("/me/sessions", {
|
|
452
|
+
method: "GET",
|
|
453
|
+
bearer: this.requireToken(),
|
|
454
|
+
})
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
async revokeSession(id: string): Promise<void> {
|
|
458
|
+
await this.http.request(`/me/sessions/${encodeURIComponent(id)}`, {
|
|
459
|
+
method: "DELETE",
|
|
460
|
+
bearer: this.requireToken(),
|
|
461
|
+
})
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// ─── /me/password ────────────────────────────────────────────────────────
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Change password. Backend tüm session'ları revoke eder; SDK local
|
|
468
|
+
* session'ı temizler — caller `signIn` ile tekrar oturum açar.
|
|
469
|
+
*/
|
|
470
|
+
async changePassword(input: {
|
|
471
|
+
currentPassword: string
|
|
472
|
+
newPassword: string
|
|
473
|
+
}): Promise<void> {
|
|
474
|
+
await this.http.request("/me/password", {
|
|
475
|
+
method: "POST",
|
|
476
|
+
json: input,
|
|
477
|
+
bearer: this.requireToken(),
|
|
478
|
+
})
|
|
479
|
+
this.clearSession()
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// ─── /me/email/change ────────────────────────────────────────────────────
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Request email change — confirmation mail yeni adrese gönderilir.
|
|
486
|
+
* Kullanıcı `confirmEmailChange(token)` ile finalize eder.
|
|
487
|
+
*/
|
|
488
|
+
async requestEmailChange(input: {
|
|
489
|
+
newEmail: string
|
|
490
|
+
currentPassword: string
|
|
491
|
+
}): Promise<void> {
|
|
492
|
+
await this.http.request("/me/email/change-request", {
|
|
493
|
+
method: "POST",
|
|
494
|
+
json: input,
|
|
495
|
+
bearer: this.requireToken(),
|
|
496
|
+
})
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/** Token-based confirm (mail link'inden gelir). */
|
|
500
|
+
async confirmEmailChange(token: string): Promise<SentroyAuthUser> {
|
|
501
|
+
const user = await this.http.request<SentroyAuthUser>(
|
|
502
|
+
"/me/email/change-confirm",
|
|
503
|
+
{ method: "POST", json: { token } },
|
|
504
|
+
)
|
|
505
|
+
// Email changed — tüm sessions revoke edildi, local clear
|
|
506
|
+
this.clearSession()
|
|
507
|
+
return user
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// ─── /me/account (delete) ────────────────────────────────────────────────
|
|
511
|
+
|
|
512
|
+
async requestAccountDeletion(currentPassword: string): Promise<void> {
|
|
513
|
+
await this.http.request("/me/account/delete-request", {
|
|
514
|
+
method: "POST",
|
|
515
|
+
json: { currentPassword },
|
|
516
|
+
bearer: this.requireToken(),
|
|
517
|
+
})
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
async confirmAccountDeletion(token: string): Promise<void> {
|
|
521
|
+
await this.http.request("/me/account/delete-confirm", {
|
|
522
|
+
method: "POST",
|
|
523
|
+
json: { token },
|
|
524
|
+
})
|
|
525
|
+
this.clearSession()
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// ─── /me/activity ────────────────────────────────────────────────────────
|
|
529
|
+
|
|
530
|
+
async getActivity(): Promise<ActivityEntry[]> {
|
|
531
|
+
return this.http.request<ActivityEntry[]>("/me/activity", {
|
|
532
|
+
method: "GET",
|
|
533
|
+
bearer: this.requireToken(),
|
|
534
|
+
})
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// ─── /me/mfa ─────────────────────────────────────────────────────────────
|
|
538
|
+
|
|
539
|
+
readonly mfa = {
|
|
540
|
+
getStatus: async (): Promise<MfaStatus> =>
|
|
541
|
+
this.http.request<MfaStatus>("/me/mfa", {
|
|
542
|
+
method: "GET",
|
|
543
|
+
bearer: this.requireToken(),
|
|
544
|
+
}),
|
|
545
|
+
enrollTotp: async (): Promise<MfaEnrollResponse> =>
|
|
546
|
+
this.http.request<MfaEnrollResponse>("/me/mfa/totp/enroll", {
|
|
547
|
+
method: "POST",
|
|
548
|
+
bearer: this.requireToken(),
|
|
549
|
+
}),
|
|
550
|
+
verifyTotpEnrollment: async (
|
|
551
|
+
code: string,
|
|
552
|
+
): Promise<MfaVerifyEnrollmentResponse> =>
|
|
553
|
+
this.http.request<MfaVerifyEnrollmentResponse>(
|
|
554
|
+
"/me/mfa/totp/verify-enrollment",
|
|
555
|
+
{ method: "POST", json: { code }, bearer: this.requireToken() },
|
|
556
|
+
),
|
|
557
|
+
disableTotp: async (currentPassword: string): Promise<void> => {
|
|
558
|
+
await this.http.request("/me/mfa/totp/disable", {
|
|
559
|
+
method: "POST",
|
|
560
|
+
json: { currentPassword },
|
|
561
|
+
bearer: this.requireToken(),
|
|
562
|
+
})
|
|
563
|
+
},
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// ─── /me/passkey + /passkey/auth ─────────────────────────────────────────
|
|
567
|
+
|
|
568
|
+
readonly passkey = {
|
|
569
|
+
list: async (): Promise<PasskeySummary[]> =>
|
|
570
|
+
this.http.request<PasskeySummary[]>("/me/passkey", {
|
|
571
|
+
method: "GET",
|
|
572
|
+
bearer: this.requireToken(),
|
|
573
|
+
}),
|
|
574
|
+
delete: async (id: string): Promise<void> => {
|
|
575
|
+
await this.http.request(`/me/passkey/${encodeURIComponent(id)}`, {
|
|
576
|
+
method: "DELETE",
|
|
577
|
+
bearer: this.requireToken(),
|
|
578
|
+
})
|
|
579
|
+
},
|
|
580
|
+
/**
|
|
581
|
+
* Register a new passkey on this device.
|
|
582
|
+
*
|
|
583
|
+
* Browser-only: dynamically imports `@simplewebauthn/browser`. RP
|
|
584
|
+
* SDK kullanıyor ama webauthn/browser bağımlılığı yoksa caller
|
|
585
|
+
* `peerDependencies` aracılığıyla manuel ekler.
|
|
586
|
+
*/
|
|
587
|
+
register: async (deviceName?: string): Promise<void> => {
|
|
588
|
+
const begin = await this.http.request<{
|
|
589
|
+
options: unknown
|
|
590
|
+
challengeToken: string
|
|
591
|
+
}>("/me/passkey/register/begin", {
|
|
592
|
+
method: "POST",
|
|
593
|
+
bearer: this.requireToken(),
|
|
594
|
+
})
|
|
595
|
+
const sw = await loadSimpleWebAuthnBrowser()
|
|
596
|
+
const attestation = await sw.startRegistration({
|
|
597
|
+
optionsJSON: begin.options as Parameters<typeof sw.startRegistration>[0]["optionsJSON"],
|
|
598
|
+
})
|
|
599
|
+
await this.http.request("/me/passkey/register/complete", {
|
|
600
|
+
method: "POST",
|
|
601
|
+
json: {
|
|
602
|
+
challengeToken: begin.challengeToken,
|
|
603
|
+
response: attestation,
|
|
604
|
+
deviceName:
|
|
605
|
+
deviceName ??
|
|
606
|
+
(typeof navigator !== "undefined"
|
|
607
|
+
? navigator.userAgent.slice(0, 80)
|
|
608
|
+
: null),
|
|
609
|
+
},
|
|
610
|
+
bearer: this.requireToken(),
|
|
611
|
+
})
|
|
612
|
+
},
|
|
613
|
+
/**
|
|
614
|
+
* Sign in with passkey. Email opsiyonel — verilirse server o
|
|
615
|
+
* user'ın passkey'lerini allowList yapar, yoksa "usernameless".
|
|
616
|
+
* Session kurulur.
|
|
617
|
+
*/
|
|
618
|
+
authenticate: async (
|
|
619
|
+
opts: { email?: string; rememberMe?: boolean } = {},
|
|
620
|
+
): Promise<LoginResponse> => {
|
|
621
|
+
const begin = await this.http.request<{
|
|
622
|
+
options: unknown
|
|
623
|
+
challengeToken: string
|
|
624
|
+
}>("/passkey/authenticate/begin", {
|
|
625
|
+
method: "POST",
|
|
626
|
+
json: { email: opts.email },
|
|
627
|
+
})
|
|
628
|
+
const sw = await loadSimpleWebAuthnBrowser()
|
|
629
|
+
const assertion = await sw.startAuthentication({
|
|
630
|
+
optionsJSON: begin.options as Parameters<typeof sw.startAuthentication>[0]["optionsJSON"],
|
|
631
|
+
})
|
|
632
|
+
const res = await this.http.request<LoginResponse>(
|
|
633
|
+
"/passkey/authenticate/complete",
|
|
634
|
+
{
|
|
635
|
+
method: "POST",
|
|
636
|
+
json: {
|
|
637
|
+
challengeToken: begin.challengeToken,
|
|
638
|
+
response: assertion,
|
|
639
|
+
rememberMe: opts.rememberMe,
|
|
640
|
+
},
|
|
641
|
+
},
|
|
642
|
+
)
|
|
643
|
+
this.persist({
|
|
644
|
+
accessToken: res.accessToken,
|
|
645
|
+
refreshToken: res.refreshToken,
|
|
646
|
+
user: res.user,
|
|
647
|
+
})
|
|
648
|
+
return res
|
|
649
|
+
},
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Force refresh now — caller'ın sürdüğü access token süresi dolmuş
|
|
654
|
+
* olabilir; bu method yeni token'ları persist eder.
|
|
655
|
+
*/
|
|
656
|
+
async refreshNow(): Promise<void> {
|
|
657
|
+
await this.refresh()
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/** Manual session injection — fragment / cookie redirect dışında tokens
|
|
661
|
+
* başka bir kanaldan elde edildiyse (örn. RP custom auth callback). */
|
|
662
|
+
setSession(input: {
|
|
663
|
+
accessToken: string
|
|
664
|
+
refreshToken: string
|
|
665
|
+
user: SentroyAuthUser
|
|
666
|
+
}): void {
|
|
667
|
+
this.persist(input)
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
private requireToken(): string {
|
|
671
|
+
const restored = this.storage.read()
|
|
672
|
+
if (!restored?.accessToken) {
|
|
673
|
+
throw new Error("Not signed in — accessToken missing.")
|
|
674
|
+
}
|
|
675
|
+
return restored.accessToken
|
|
676
|
+
}
|
|
677
|
+
|
|
244
678
|
/**
|
|
245
679
|
* Subscription pattern — Firebase Auth uyumlu. Caller'ın hemen mevcut
|
|
246
680
|
* state'i alabilmesi için constructor'da restore edilen user
|
package/src/auth/index.ts
CHANGED
|
@@ -23,4 +23,13 @@ export {
|
|
|
23
23
|
type SignupResponse,
|
|
24
24
|
type LoginResponse,
|
|
25
25
|
type AuthTokensResponse,
|
|
26
|
+
type LoginOutcome,
|
|
27
|
+
type MfaChallengeResponse,
|
|
28
|
+
type SessionSummary,
|
|
29
|
+
type ActivityEntry,
|
|
30
|
+
type MfaStatus,
|
|
31
|
+
type MfaEnrollResponse,
|
|
32
|
+
type MfaVerifyEnrollmentResponse,
|
|
33
|
+
type PasskeySummary,
|
|
34
|
+
type SocialProvider,
|
|
26
35
|
} from "./types"
|