@douvery/auth 0.2.0 → 0.3.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/README.md +641 -93
- package/dist/index.d.ts +107 -1
- package/dist/index.js +187 -0
- package/dist/index.js.map +1 -1
- package/dist/qwik/index.d.ts +135 -1
- package/dist/qwik/index.js +296 -24
- package/dist/qwik/index.js.map +1 -1
- package/dist/react/index.d.ts +144 -1
- package/dist/react/index.js +373 -45
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -207,6 +207,64 @@ interface LogoutOptions {
|
|
|
207
207
|
/** Only clear local session, don't redirect @default false */
|
|
208
208
|
localOnly?: boolean;
|
|
209
209
|
}
|
|
210
|
+
/** Base options for all auth navigation redirects */
|
|
211
|
+
interface AuthNavigationOptions {
|
|
212
|
+
/** URL to return to after the action completes */
|
|
213
|
+
returnTo?: string;
|
|
214
|
+
/** OAuth client_id for branded experiences */
|
|
215
|
+
clientId?: string;
|
|
216
|
+
/** Open in a new tab/window instead of redirecting */
|
|
217
|
+
openInNewTab?: boolean;
|
|
218
|
+
}
|
|
219
|
+
interface SelectAccountOptions extends AuthNavigationOptions {
|
|
220
|
+
/** Pre-select a specific email/account */
|
|
221
|
+
loginHint?: string;
|
|
222
|
+
}
|
|
223
|
+
interface RegisterOptions extends AuthNavigationOptions {
|
|
224
|
+
/** Pre-fill email on register form */
|
|
225
|
+
email?: string;
|
|
226
|
+
/** Pre-fill first name */
|
|
227
|
+
firstName?: string;
|
|
228
|
+
/** Pre-fill last name */
|
|
229
|
+
lastName?: string;
|
|
230
|
+
/** UI locale preference */
|
|
231
|
+
uiLocales?: string;
|
|
232
|
+
}
|
|
233
|
+
interface RecoverAccountOptions extends AuthNavigationOptions {
|
|
234
|
+
/** Pre-fill email for recovery */
|
|
235
|
+
email?: string;
|
|
236
|
+
}
|
|
237
|
+
interface VerifyAccountOptions extends AuthNavigationOptions {
|
|
238
|
+
/** Email to verify */
|
|
239
|
+
email?: string;
|
|
240
|
+
}
|
|
241
|
+
interface UpgradeAccountOptions extends AuthNavigationOptions {
|
|
242
|
+
/** Scopes to request on upgrade */
|
|
243
|
+
scopes?: string[];
|
|
244
|
+
}
|
|
245
|
+
interface SetupPasskeyOptions extends AuthNavigationOptions {
|
|
246
|
+
}
|
|
247
|
+
interface SetupAddressOptions extends AuthNavigationOptions {
|
|
248
|
+
}
|
|
249
|
+
interface AddAccountOptions extends AuthNavigationOptions {
|
|
250
|
+
/** Pre-fill email hint for the new account */
|
|
251
|
+
loginHint?: string;
|
|
252
|
+
}
|
|
253
|
+
interface RevokeTokenOptions {
|
|
254
|
+
/** Specific token to revoke. If not provided, revokes current access token */
|
|
255
|
+
token?: string;
|
|
256
|
+
/** Token type hint: "access_token" or "refresh_token" */
|
|
257
|
+
tokenTypeHint?: "access_token" | "refresh_token";
|
|
258
|
+
}
|
|
259
|
+
/** Result of URL builder methods (non-redirecting) */
|
|
260
|
+
interface AuthUrl {
|
|
261
|
+
/** The full URL */
|
|
262
|
+
url: string;
|
|
263
|
+
/** Open the URL via redirect */
|
|
264
|
+
redirect: () => void;
|
|
265
|
+
/** Open the URL in a new tab */
|
|
266
|
+
open: () => Window | null;
|
|
267
|
+
}
|
|
210
268
|
|
|
211
269
|
/**
|
|
212
270
|
* @douvery/auth - Auth Client
|
|
@@ -236,7 +294,55 @@ declare class DouveryAuthClient {
|
|
|
236
294
|
refreshTokens(): Promise<TokenInfo>;
|
|
237
295
|
/** Get current access token (auto-refreshes if needed) */
|
|
238
296
|
getAccessToken(): Promise<string | null>;
|
|
297
|
+
/** Redirect to select/switch account */
|
|
298
|
+
selectAccount(options?: SelectAccountOptions): void;
|
|
299
|
+
/** Build select-account URL without redirecting */
|
|
300
|
+
buildSelectAccountUrl(options?: SelectAccountOptions): AuthUrl;
|
|
301
|
+
/** Redirect to add another account (multi-session) */
|
|
302
|
+
addAccount(options?: AddAccountOptions): void;
|
|
303
|
+
/** Build add-account URL without redirecting */
|
|
304
|
+
buildAddAccountUrl(options?: AddAccountOptions): AuthUrl;
|
|
305
|
+
/** Redirect to register a new account */
|
|
306
|
+
register(options?: RegisterOptions): void;
|
|
307
|
+
/** Build register URL without redirecting */
|
|
308
|
+
buildRegisterUrl(options?: RegisterOptions): AuthUrl;
|
|
309
|
+
/** Redirect to recover account (forgot password) */
|
|
310
|
+
recoverAccount(options?: RecoverAccountOptions): void;
|
|
311
|
+
/** Build recover-account URL without redirecting */
|
|
312
|
+
buildRecoverAccountUrl(options?: RecoverAccountOptions): AuthUrl;
|
|
313
|
+
/** Redirect to verify account (email verification) */
|
|
314
|
+
verifyAccount(options?: VerifyAccountOptions): void;
|
|
315
|
+
/** Build verify-account URL without redirecting */
|
|
316
|
+
buildVerifyAccountUrl(options?: VerifyAccountOptions): AuthUrl;
|
|
317
|
+
/** Redirect to upgrade account (guest → full account) */
|
|
318
|
+
upgradeAccount(options?: UpgradeAccountOptions): void;
|
|
319
|
+
/** Build upgrade-account URL without redirecting */
|
|
320
|
+
buildUpgradeAccountUrl(options?: UpgradeAccountOptions): AuthUrl;
|
|
321
|
+
/** Redirect to passkey setup */
|
|
322
|
+
setupPasskey(options?: SetupPasskeyOptions): void;
|
|
323
|
+
/** Build setup-passkey URL without redirecting */
|
|
324
|
+
buildSetupPasskeyUrl(options?: SetupPasskeyOptions): AuthUrl;
|
|
325
|
+
/** Redirect to address setup */
|
|
326
|
+
setupAddress(options?: SetupAddressOptions): void;
|
|
327
|
+
/** Build setup-address URL without redirecting */
|
|
328
|
+
buildSetupAddressUrl(options?: SetupAddressOptions): AuthUrl;
|
|
329
|
+
/** Build a login URL without redirecting (useful for links/buttons) */
|
|
330
|
+
buildLoginUrl(options?: LoginOptions): AuthUrl;
|
|
331
|
+
/** Build a logout URL without redirecting */
|
|
332
|
+
buildLogoutUrl(options?: LogoutOptions): AuthUrl;
|
|
333
|
+
/** Revoke a token (access or refresh) */
|
|
334
|
+
revokeToken(options?: RevokeTokenOptions): Promise<void>;
|
|
335
|
+
/** Check if the user's session is expired */
|
|
336
|
+
isSessionExpired(): boolean;
|
|
337
|
+
/** Check if user needs email verification */
|
|
338
|
+
needsEmailVerification(): boolean;
|
|
339
|
+
/** Check if user is a guest account */
|
|
340
|
+
isGuestAccount(): boolean;
|
|
239
341
|
private tokenSetToInfo;
|
|
342
|
+
/** Build an AuthUrl object for a given path and params */
|
|
343
|
+
private createAuthUrl;
|
|
344
|
+
/** Navigate to an auth URL, respecting openInNewTab option */
|
|
345
|
+
private navigate;
|
|
240
346
|
private fetchUser;
|
|
241
347
|
private extractUserFromIdToken;
|
|
242
348
|
private normalizeUser;
|
|
@@ -349,4 +455,4 @@ declare class TokenManager {
|
|
|
349
455
|
clearAll(): Promise<void>;
|
|
350
456
|
}
|
|
351
457
|
|
|
352
|
-
export { AuthError, type AuthErrorCode, type AuthEvent, type AuthEventHandler, type AuthState, type AuthStatus, type CallbackResult, CookieStorage, type DecodedIdToken, DouveryAuthClient, type DouveryAuthConfig, LocalStorage, type LoginOptions, type LogoutOptions, MemoryStorage, type OIDCDiscovery, type PKCEPair, STORAGE_KEYS, SessionStorage, type StorageKeys, type TokenInfo, TokenManager, type TokenSet, type TokenStorage, type User, base64UrlDecode, base64UrlEncode, createDouveryAuth, createStorage, decodeJWT, generateCodeChallenge, generateCodeVerifier, generateNonce, generatePKCEPair, generateState, getTokenExpiration, isTokenExpired, verifyCodeChallenge };
|
|
458
|
+
export { type AddAccountOptions, AuthError, type AuthErrorCode, type AuthEvent, type AuthEventHandler, type AuthNavigationOptions, type AuthState, type AuthStatus, type AuthUrl, type CallbackResult, CookieStorage, type DecodedIdToken, DouveryAuthClient, type DouveryAuthConfig, LocalStorage, type LoginOptions, type LogoutOptions, MemoryStorage, type OIDCDiscovery, type PKCEPair, type RecoverAccountOptions, type RegisterOptions, type RevokeTokenOptions, STORAGE_KEYS, type SelectAccountOptions, SessionStorage, type SetupAddressOptions, type SetupPasskeyOptions, type StorageKeys, type TokenInfo, TokenManager, type TokenSet, type TokenStorage, type UpgradeAccountOptions, type User, type VerifyAccountOptions, base64UrlDecode, base64UrlEncode, createDouveryAuth, createStorage, decodeJWT, generateCodeChallenge, generateCodeVerifier, generateNonce, generatePKCEPair, generateState, getTokenExpiration, isTokenExpired, verifyCodeChallenge };
|
package/dist/index.js
CHANGED
|
@@ -636,6 +636,171 @@ var DouveryAuthClient = class {
|
|
|
636
636
|
}
|
|
637
637
|
return tokens.accessToken;
|
|
638
638
|
}
|
|
639
|
+
// ============================================
|
|
640
|
+
// Navigation Methods
|
|
641
|
+
// ============================================
|
|
642
|
+
/** Redirect to select/switch account */
|
|
643
|
+
selectAccount(options = {}) {
|
|
644
|
+
const url = this.buildSelectAccountUrl(options);
|
|
645
|
+
this.navigate(url, options);
|
|
646
|
+
}
|
|
647
|
+
/** Build select-account URL without redirecting */
|
|
648
|
+
buildSelectAccountUrl(options = {}) {
|
|
649
|
+
const params = new URLSearchParams();
|
|
650
|
+
if (options.returnTo) params.set("continue", options.returnTo);
|
|
651
|
+
if (options.clientId) params.set("client_id", options.clientId);
|
|
652
|
+
if (options.loginHint) params.set("login_hint", options.loginHint);
|
|
653
|
+
return this.createAuthUrl("/select-account", params);
|
|
654
|
+
}
|
|
655
|
+
/** Redirect to add another account (multi-session) */
|
|
656
|
+
addAccount(options = {}) {
|
|
657
|
+
const url = this.buildAddAccountUrl(options);
|
|
658
|
+
this.navigate(url, options);
|
|
659
|
+
}
|
|
660
|
+
/** Build add-account URL without redirecting */
|
|
661
|
+
buildAddAccountUrl(options = {}) {
|
|
662
|
+
const params = new URLSearchParams({ add_account: "true" });
|
|
663
|
+
if (options.returnTo) params.set("continue", options.returnTo);
|
|
664
|
+
if (options.clientId) params.set("client_id", options.clientId);
|
|
665
|
+
if (options.loginHint) params.set("login_hint", options.loginHint);
|
|
666
|
+
return this.createAuthUrl("/login", params);
|
|
667
|
+
}
|
|
668
|
+
/** Redirect to register a new account */
|
|
669
|
+
register(options = {}) {
|
|
670
|
+
const url = this.buildRegisterUrl(options);
|
|
671
|
+
this.navigate(url, options);
|
|
672
|
+
}
|
|
673
|
+
/** Build register URL without redirecting */
|
|
674
|
+
buildRegisterUrl(options = {}) {
|
|
675
|
+
const params = new URLSearchParams();
|
|
676
|
+
if (options.returnTo) params.set("continue", options.returnTo);
|
|
677
|
+
if (options.clientId) params.set("client_id", options.clientId);
|
|
678
|
+
if (options.email) params.set("email", options.email);
|
|
679
|
+
if (options.firstName) params.set("first_name", options.firstName);
|
|
680
|
+
if (options.lastName) params.set("last_name", options.lastName);
|
|
681
|
+
if (options.uiLocales) params.set("ui_locales", options.uiLocales);
|
|
682
|
+
return this.createAuthUrl("/register", params);
|
|
683
|
+
}
|
|
684
|
+
/** Redirect to recover account (forgot password) */
|
|
685
|
+
recoverAccount(options = {}) {
|
|
686
|
+
const url = this.buildRecoverAccountUrl(options);
|
|
687
|
+
this.navigate(url, options);
|
|
688
|
+
}
|
|
689
|
+
/** Build recover-account URL without redirecting */
|
|
690
|
+
buildRecoverAccountUrl(options = {}) {
|
|
691
|
+
const params = new URLSearchParams();
|
|
692
|
+
if (options.returnTo) params.set("continue", options.returnTo);
|
|
693
|
+
if (options.clientId) params.set("client_id", options.clientId);
|
|
694
|
+
if (options.email) params.set("email", options.email);
|
|
695
|
+
return this.createAuthUrl("/recover-account", params);
|
|
696
|
+
}
|
|
697
|
+
/** Redirect to verify account (email verification) */
|
|
698
|
+
verifyAccount(options = {}) {
|
|
699
|
+
const url = this.buildVerifyAccountUrl(options);
|
|
700
|
+
this.navigate(url, options);
|
|
701
|
+
}
|
|
702
|
+
/** Build verify-account URL without redirecting */
|
|
703
|
+
buildVerifyAccountUrl(options = {}) {
|
|
704
|
+
const params = new URLSearchParams();
|
|
705
|
+
if (options.returnTo) params.set("continue", options.returnTo);
|
|
706
|
+
if (options.clientId) params.set("client_id", options.clientId);
|
|
707
|
+
if (options.email) params.set("email", options.email);
|
|
708
|
+
return this.createAuthUrl("/verify-account", params);
|
|
709
|
+
}
|
|
710
|
+
/** Redirect to upgrade account (guest → full account) */
|
|
711
|
+
upgradeAccount(options = {}) {
|
|
712
|
+
const url = this.buildUpgradeAccountUrl(options);
|
|
713
|
+
this.navigate(url, options);
|
|
714
|
+
}
|
|
715
|
+
/** Build upgrade-account URL without redirecting */
|
|
716
|
+
buildUpgradeAccountUrl(options = {}) {
|
|
717
|
+
const params = new URLSearchParams();
|
|
718
|
+
if (options.returnTo) params.set("continue", options.returnTo);
|
|
719
|
+
if (options.clientId) params.set("client_id", options.clientId);
|
|
720
|
+
if (options.scopes?.length) params.set("scope", options.scopes.join(" "));
|
|
721
|
+
return this.createAuthUrl("/upgrade-account", params);
|
|
722
|
+
}
|
|
723
|
+
/** Redirect to passkey setup */
|
|
724
|
+
setupPasskey(options = {}) {
|
|
725
|
+
const url = this.buildSetupPasskeyUrl(options);
|
|
726
|
+
this.navigate(url, options);
|
|
727
|
+
}
|
|
728
|
+
/** Build setup-passkey URL without redirecting */
|
|
729
|
+
buildSetupPasskeyUrl(options = {}) {
|
|
730
|
+
const params = new URLSearchParams();
|
|
731
|
+
if (options.returnTo) params.set("continue", options.returnTo);
|
|
732
|
+
if (options.clientId) params.set("client_id", options.clientId);
|
|
733
|
+
return this.createAuthUrl("/setup-passkey", params);
|
|
734
|
+
}
|
|
735
|
+
/** Redirect to address setup */
|
|
736
|
+
setupAddress(options = {}) {
|
|
737
|
+
const url = this.buildSetupAddressUrl(options);
|
|
738
|
+
this.navigate(url, options);
|
|
739
|
+
}
|
|
740
|
+
/** Build setup-address URL without redirecting */
|
|
741
|
+
buildSetupAddressUrl(options = {}) {
|
|
742
|
+
const params = new URLSearchParams();
|
|
743
|
+
if (options.returnTo) params.set("continue", options.returnTo);
|
|
744
|
+
if (options.clientId) params.set("client_id", options.clientId);
|
|
745
|
+
return this.createAuthUrl("/setup-address", params);
|
|
746
|
+
}
|
|
747
|
+
/** Build a login URL without redirecting (useful for links/buttons) */
|
|
748
|
+
buildLoginUrl(options = {}) {
|
|
749
|
+
const params = new URLSearchParams();
|
|
750
|
+
if (options.returnTo) params.set("continue", options.returnTo);
|
|
751
|
+
if (options.loginHint) params.set("email", options.loginHint);
|
|
752
|
+
if (options.prompt) params.set("prompt", options.prompt);
|
|
753
|
+
if (options.uiLocales) params.set("ui_locales", options.uiLocales);
|
|
754
|
+
return this.createAuthUrl("/login", params);
|
|
755
|
+
}
|
|
756
|
+
/** Build a logout URL without redirecting */
|
|
757
|
+
buildLogoutUrl(options = {}) {
|
|
758
|
+
const params = new URLSearchParams();
|
|
759
|
+
if (options.returnTo) params.set("continue", options.returnTo);
|
|
760
|
+
return this.createAuthUrl("/logout", params);
|
|
761
|
+
}
|
|
762
|
+
/** Revoke a token (access or refresh) */
|
|
763
|
+
async revokeToken(options = {}) {
|
|
764
|
+
this.log("Revoking token...");
|
|
765
|
+
const token = options.token ?? (await this.tokenManager.getTokens())?.accessToken;
|
|
766
|
+
if (!token) {
|
|
767
|
+
throw new AuthError("invalid_token", "No token to revoke");
|
|
768
|
+
}
|
|
769
|
+
const revokeUrl = `${this.config.issuer}/oauth/revoke`;
|
|
770
|
+
const body = new URLSearchParams({
|
|
771
|
+
token,
|
|
772
|
+
client_id: this.config.clientId
|
|
773
|
+
});
|
|
774
|
+
if (options.tokenTypeHint) {
|
|
775
|
+
body.set("token_type_hint", options.tokenTypeHint);
|
|
776
|
+
}
|
|
777
|
+
const response = await fetch(revokeUrl, {
|
|
778
|
+
method: "POST",
|
|
779
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
780
|
+
body
|
|
781
|
+
});
|
|
782
|
+
if (!response.ok) {
|
|
783
|
+
const error = await response.json().catch(() => ({}));
|
|
784
|
+
throw new AuthError(
|
|
785
|
+
error.error ?? "server_error",
|
|
786
|
+
error.error_description ?? "Token revocation failed"
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
this.log("Token revoked successfully");
|
|
790
|
+
}
|
|
791
|
+
/** Check if the user's session is expired */
|
|
792
|
+
isSessionExpired() {
|
|
793
|
+
if (!this.state.tokens) return true;
|
|
794
|
+
return isTokenExpired(this.state.tokens.accessToken);
|
|
795
|
+
}
|
|
796
|
+
/** Check if user needs email verification */
|
|
797
|
+
needsEmailVerification() {
|
|
798
|
+
return this.state.user?.emailVerified === false;
|
|
799
|
+
}
|
|
800
|
+
/** Check if user is a guest account */
|
|
801
|
+
isGuestAccount() {
|
|
802
|
+
return this.state.user?.accountType === "guest";
|
|
803
|
+
}
|
|
639
804
|
tokenSetToInfo(tokenSet) {
|
|
640
805
|
return {
|
|
641
806
|
accessToken: tokenSet.access_token,
|
|
@@ -646,6 +811,28 @@ var DouveryAuthClient = class {
|
|
|
646
811
|
scope: tokenSet.scope?.split(" ") ?? []
|
|
647
812
|
};
|
|
648
813
|
}
|
|
814
|
+
/** Build an AuthUrl object for a given path and params */
|
|
815
|
+
createAuthUrl(path, params) {
|
|
816
|
+
const query = params.toString();
|
|
817
|
+
const url = `${this.config.issuer}${path}${query ? `?${query}` : ""}`;
|
|
818
|
+
return {
|
|
819
|
+
url,
|
|
820
|
+
redirect: () => {
|
|
821
|
+
window.location.href = url;
|
|
822
|
+
},
|
|
823
|
+
open: () => {
|
|
824
|
+
return window.open(url, "_blank");
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
/** Navigate to an auth URL, respecting openInNewTab option */
|
|
829
|
+
navigate(authUrl, options) {
|
|
830
|
+
if (options.openInNewTab) {
|
|
831
|
+
authUrl.open();
|
|
832
|
+
} else {
|
|
833
|
+
authUrl.redirect();
|
|
834
|
+
}
|
|
835
|
+
}
|
|
649
836
|
async fetchUser(accessToken) {
|
|
650
837
|
const discovery = await this.getDiscovery();
|
|
651
838
|
const response = await fetch(discovery.userinfo_endpoint, {
|