@bagelink/auth 1.7.72 → 1.7.74

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.
@@ -0,0 +1,22 @@
1
+ import { NavigationGuardNext, RouteLocationNormalized } from 'vue-router';
2
+ /**
3
+ * Reset auth initialization state
4
+ * Useful for testing or app reload scenarios
5
+ */
6
+ export declare function resetAuthState(): void;
7
+ /**
8
+ * Auth guard for Vue Router
9
+ *
10
+ * Protects routes requiring authentication and handles redirect logic.
11
+ * Reads configuration from the auth instance created via createAuth().
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const auth = createAuth({
16
+ * baseURL: 'https://api.example.com',
17
+ * redirect: { ... }
18
+ * })
19
+ * router.beforeEach(authGuard())
20
+ * ```
21
+ */
22
+ export declare function authGuard(): (to: RouteLocationNormalized, _from: RouteLocationNormalized, next: NavigationGuardNext) => Promise<void>;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Redirect configuration types for auth library
3
+ */
4
+ export interface RedirectConfig {
5
+ /**
6
+ * Query parameter key used to store the redirect URL
7
+ * After login, read this param to redirect users back to their intended destination
8
+ * @default 'redirect'
9
+ */
10
+ queryKey?: string;
11
+ /**
12
+ * Default fallback URL when no redirect is specified
13
+ * @default '/'
14
+ */
15
+ fallback?: string;
16
+ /**
17
+ * Routes that require NO authentication (login, signup, forgot password, etc)
18
+ * Authenticated users will be automatically redirected away from these pages
19
+ * @default ['Login', 'Signup', 'ForgotPassword', 'ResetPassword', 'Callback']
20
+ */
21
+ noAuthRoutes?: string[];
22
+ /**
23
+ * Route name to redirect to when authenticated users try to access auth-only pages
24
+ * @default '/'
25
+ */
26
+ authenticatedRedirect?: string;
27
+ /**
28
+ * Route name to redirect to when unauthenticated users try to access protected pages
29
+ * @default 'Login'
30
+ */
31
+ loginRoute?: string;
32
+ /**
33
+ * Meta key used to check if a route requires authentication
34
+ * @default 'auth'
35
+ * @example In your route: `meta: { auth: true }`
36
+ */
37
+ authMetaKey?: string;
38
+ /**
39
+ * Enable automatic redirect handling after login
40
+ * When true, the library automatically redirects users after successful login
41
+ * @default true
42
+ */
43
+ autoRedirect?: boolean;
44
+ /**
45
+ * Enable redirect preservation for protected routes
46
+ * When enabled, the original URL is preserved as a query param after redirecting to login
47
+ * @default true
48
+ */
49
+ preserveRedirect?: boolean;
50
+ /**
51
+ * Optional allowed redirect path patterns for security validation
52
+ * If specified, only URLs matching these patterns will be allowed
53
+ * @default undefined (allows all internal paths)
54
+ */
55
+ allowedPaths?: RegExp[];
56
+ }
57
+ /**
58
+ * Normalized redirect configuration with all defaults applied
59
+ */
60
+ export interface NormalizedRedirectConfig extends Required<Omit<RedirectConfig, 'allowedPaths'>> {
61
+ allowedPaths?: RegExp[];
62
+ }
63
+ /**
64
+ * Default redirect configuration
65
+ */
66
+ export declare const DEFAULT_REDIRECT_CONFIG: NormalizedRedirectConfig;
67
+ /**
68
+ * Normalize redirect configuration by applying defaults
69
+ */
70
+ export declare function normalizeRedirectConfig(config?: RedirectConfig): NormalizedRedirectConfig;
package/dist/useAuth.d.ts CHANGED
@@ -1,8 +1,31 @@
1
1
  import { App } from 'vue';
2
2
  import { AccountInfo, User, NewUser, UpdatePasswordForm, UpdateAccountRequest, AuthEventMap, SSOProvider, SSOInitiateRequest, SSOCallbackRequest, SSOLinkRequest, AuthState } from './types';
3
+ import { RedirectConfig, NormalizedRedirectConfig } from './types/redirect';
3
4
  interface InitParams {
4
5
  baseURL: string;
6
+ /**
7
+ * Redirect configuration for authentication flows
8
+ * @see RedirectConfig
9
+ */
10
+ redirect?: RedirectConfig;
5
11
  }
12
+ /**
13
+ * Set the router instance for auto-redirect functionality
14
+ * Call this in your app setup after creating the router
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const auth = createAuth({ ... })
19
+ * const router = createRouter({ ... })
20
+ * setAuthRouter(router)
21
+ * ```
22
+ */
23
+ export declare function setAuthRouter(router: any): void;
24
+ /**
25
+ * Get the current redirect configuration
26
+ * Used internally by router guard
27
+ */
28
+ export declare function getRedirectConfig(): NormalizedRedirectConfig;
6
29
  export declare function createAuth(params: InitParams): {
7
30
  on<K extends AuthState>(event: K, handler: AuthEventMap[K]): void;
8
31
  off<K extends AuthState>(event: K, handler: AuthEventMap[K]): void;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/auth",
3
3
  "type": "module",
4
- "version": "1.7.72",
4
+ "version": "1.7.74",
5
5
  "description": "Bagelink auth package",
6
6
  "author": {
7
7
  "name": "Bagel Studio",
package/src/index.ts CHANGED
@@ -14,17 +14,25 @@ export { default as ForgotPasswordPage } from './pages/ForgotPasswordPage.vue'
14
14
  export { default as LoginPage } from './pages/LoginPage.vue'
15
15
  export { default as ResetPasswordPage } from './pages/ResetPasswordPage.vue'
16
16
  export { default as SignupPage } from './pages/SignupPage.vue'
17
+
18
+ // Redirect utilities
19
+ export * from './redirect'
20
+
21
+ // Router integration
22
+ export * from './router'
23
+
17
24
  // Routes
18
25
  export * from './routes'
19
26
  export * from './sso'
20
27
 
21
- // Types (re-export from types file and module)
28
+ // Types ( e-export fro types file and modu e)
22
29
  export * from './types'
23
-
24
30
  export type {
25
31
  ForgotPasswordTexts,
26
32
  LoginTexts,
27
33
  ResetPasswordTexts,
28
34
  SignupTexts,
29
35
  } from './types/'
36
+
37
+ export * from './types/redirect'
30
38
  export * from './useAuth'
@@ -0,0 +1,95 @@
1
+ import type { Router } from 'vue-router'
2
+ import type { NormalizedRedirectConfig } from './types/redirect'
3
+
4
+ /**
5
+ * Redirect utilities for handling post-authentication navigation
6
+ */
7
+
8
+ /**
9
+ * Get redirect URL from current route query params
10
+ */
11
+ export function getRedirectUrl(
12
+ router: Router,
13
+ config: NormalizedRedirectConfig,
14
+ ): string {
15
+ const redirect = router.currentRoute.value.query[config.queryKey] as string
16
+ return redirect || config.fallback
17
+ }
18
+
19
+ /**
20
+ * Validate redirect URL is safe (prevents open redirect attacks)
21
+ */
22
+ export function isValidRedirect(
23
+ redirectUrl: string,
24
+ allowedPaths?: RegExp[],
25
+ ): boolean {
26
+ // Null or empty check
27
+ if (!redirectUrl) {
28
+ return false
29
+ }
30
+
31
+ // Prevent redirects to external URLs (absolute URLs with protocol)
32
+ if (redirectUrl.startsWith('http://') || redirectUrl.startsWith('https://')) {
33
+ return false
34
+ }
35
+
36
+ // Prevent protocol-relative URLs
37
+ if (redirectUrl.startsWith('//')) {
38
+ return false
39
+ }
40
+
41
+ // Prevent javascript: and data: URLs
42
+ if (redirectUrl.startsWith('javascript:') || redirectUrl.startsWith('data:')) {
43
+ return false
44
+ }
45
+
46
+ // Ensure it starts with /
47
+ if (!redirectUrl.startsWith('/')) {
48
+ return false
49
+ }
50
+
51
+ // If allowed paths are specified, check against them
52
+ if (allowedPaths && allowedPaths.length > 0) {
53
+ return allowedPaths.some(pattern => pattern.test(redirectUrl))
54
+ }
55
+
56
+ return true
57
+ }
58
+
59
+ /**
60
+ * Perform redirect after login with security validation
61
+ */
62
+ export async function performRedirect(
63
+ router: Router,
64
+ config: NormalizedRedirectConfig,
65
+ ): Promise<void> {
66
+ const redirect = getRedirectUrl(router, config)
67
+
68
+ // Always validate redirect URL for security
69
+ if (redirect !== config.fallback && !isValidRedirect(redirect, config.allowedPaths)) {
70
+ console.warn('[Auth] Invalid redirect URL detected, using fallback:', redirect)
71
+ await router.push(config.fallback)
72
+ return
73
+ }
74
+
75
+ await router.push(redirect)
76
+ }
77
+
78
+ /**
79
+ * Build query params for redirect to login
80
+ */
81
+ export function buildLoginQuery(
82
+ currentPath: string,
83
+ config: NormalizedRedirectConfig,
84
+ ): Record<string, string> {
85
+ if (!config.preserveRedirect) {
86
+ return {}
87
+ }
88
+
89
+ // Validate the current path before storing it
90
+ if (isValidRedirect(currentPath, config.allowedPaths)) {
91
+ return { [config.queryKey]: currentPath }
92
+ }
93
+
94
+ return {}
95
+ }
package/src/router.ts ADDED
@@ -0,0 +1,86 @@
1
+ import type { NavigationGuardNext, RouteLocationNormalized } from 'vue-router'
2
+ import { buildLoginQuery } from './redirect'
3
+ import { useAuth, getRedirectConfig } from './useAuth'
4
+
5
+ /**
6
+ * Auth router integration for Vue Router
7
+ */
8
+
9
+ // Track if auth has been initialized (for performance)
10
+ let authInitialized = false
11
+
12
+ /**
13
+ * Reset auth initialization state
14
+ * Useful for testing or app reload scenarios
15
+ */
16
+ export function resetAuthState() {
17
+ authInitialized = false
18
+ }
19
+
20
+ /**
21
+ * Auth guard for Vue Router
22
+ *
23
+ * Protects routes requiring authentication and handles redirect logic.
24
+ * Reads configuration from the auth instance created via createAuth().
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * const auth = createAuth({
29
+ * baseURL: 'https://api.example.com',
30
+ * redirect: { ... }
31
+ * })
32
+ * router.beforeEach(authGuard())
33
+ * ```
34
+ */
35
+ export function authGuard() {
36
+ return async function guard(
37
+
38
+ to: RouteLocationNormalized,
39
+ _from: RouteLocationNormalized,
40
+ next: NavigationGuardNext,
41
+ ): Promise<void> {
42
+ const auth = useAuth()
43
+ const config = getRedirectConfig()
44
+
45
+ // Check if route requires authentication
46
+ const requiresAuth = to.meta[config.authMetaKey]
47
+
48
+ // Check if route is an auth-only page (login, signup, etc)
49
+ const requiresNoAuth = config.noAuthRoutes.includes(to.name as string)
50
+
51
+ try {
52
+ // Only check auth once on first navigation for performance
53
+ if (!authInitialized) {
54
+ await auth.checkAuth()
55
+ authInitialized = true
56
+ }
57
+
58
+ // Use cached auth state from reactive user ref
59
+ const isAuthenticated = !!auth.user.value
60
+
61
+ // Redirect authenticated users away from auth pages
62
+ if (isAuthenticated && requiresNoAuth) {
63
+ next(config.authenticatedRedirect)
64
+ return
65
+ }
66
+
67
+ // Redirect unauthenticated users to login for protected pages
68
+ if (!isAuthenticated && requiresAuth) {
69
+ const query = buildLoginQuery(to.fullPath, config)
70
+
71
+ next({
72
+ name: config.loginRoute,
73
+ query,
74
+ })
75
+ return
76
+ }
77
+
78
+ // Allow navigation
79
+ next()
80
+ } catch (error) {
81
+ console.error('[Auth Guard] Error:', error)
82
+ // On error, allow navigation but log the issue
83
+ next()
84
+ }
85
+ }
86
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Redirect configuration types for auth library
3
+ */
4
+
5
+ export interface RedirectConfig {
6
+ /**
7
+ * Query parameter key used to store the redirect URL
8
+ * After login, read this param to redirect users back to their intended destination
9
+ * @default 'redirect'
10
+ */
11
+ queryKey?: string
12
+
13
+ /**
14
+ * Default fallback URL when no redirect is specified
15
+ * @default '/'
16
+ */
17
+ fallback?: string
18
+
19
+ /**
20
+ * Routes that require NO authentication (login, signup, forgot password, etc)
21
+ * Authenticated users will be automatically redirected away from these pages
22
+ * @default ['Login', 'Signup', 'ForgotPassword', 'ResetPassword', 'Callback']
23
+ */
24
+ noAuthRoutes?: string[]
25
+
26
+ /**
27
+ * Route name to redirect to when authenticated users try to access auth-only pages
28
+ * @default '/'
29
+ */
30
+ authenticatedRedirect?: string
31
+
32
+ /**
33
+ * Route name to redirect to when unauthenticated users try to access protected pages
34
+ * @default 'Login'
35
+ */
36
+ loginRoute?: string
37
+
38
+ /**
39
+ * Meta key used to check if a route requires authentication
40
+ * @default 'auth'
41
+ * @example In your route: `meta: { auth: true }`
42
+ */
43
+ authMetaKey?: string
44
+
45
+ /**
46
+ * Enable automatic redirect handling after login
47
+ * When true, the library automatically redirects users after successful login
48
+ * @default true
49
+ */
50
+ autoRedirect?: boolean
51
+
52
+ /**
53
+ * Enable redirect preservation for protected routes
54
+ * When enabled, the original URL is preserved as a query param after redirecting to login
55
+ * @default true
56
+ */
57
+ preserveRedirect?: boolean
58
+
59
+ /**
60
+ * Optional allowed redirect path patterns for security validation
61
+ * If specified, only URLs matching these patterns will be allowed
62
+ * @default undefined (allows all internal paths)
63
+ */
64
+ allowedPaths?: RegExp[]
65
+ }
66
+
67
+ /**
68
+ * Normalized redirect configuration with all defaults applied
69
+ */
70
+ export interface NormalizedRedirectConfig extends Required<Omit<RedirectConfig, 'allowedPaths'>> {
71
+ allowedPaths?: RegExp[]
72
+ }
73
+
74
+ /**
75
+ * Default redirect configuration
76
+ */
77
+ export const DEFAULT_REDIRECT_CONFIG: NormalizedRedirectConfig = {
78
+ queryKey: 'redirect',
79
+ fallback: '/',
80
+ noAuthRoutes: ['Login', 'Signup', 'ForgotPassword', 'ResetPassword', 'Callback'],
81
+ authenticatedRedirect: '/',
82
+ loginRoute: 'Login',
83
+ authMetaKey: 'auth',
84
+ autoRedirect: true,
85
+ preserveRedirect: true,
86
+ }
87
+
88
+ /**
89
+ * Normalize redirect configuration by applying defaults
90
+ */
91
+ export function normalizeRedirectConfig(config?: RedirectConfig): NormalizedRedirectConfig {
92
+ return {
93
+ ...DEFAULT_REDIRECT_CONFIG,
94
+ ...config,
95
+ }
96
+ }
package/src/useAuth.ts CHANGED
@@ -11,19 +11,54 @@ import type {
11
11
  SSOCallbackRequest,
12
12
  SSOLinkRequest,
13
13
  } from './types'
14
+ import type { RedirectConfig, NormalizedRedirectConfig } from './types/redirect'
14
15
  import { ref, computed } from 'vue'
15
16
  import { AuthApi } from './api'
16
17
  import { setAuthContext, sso } from './sso'
17
18
  import { AuthState, accountToUser } from './types'
19
+ import { normalizeRedirectConfig } from './types/redirect'
18
20
  import { EventEmitter } from './utils'
19
21
 
20
22
  // Global state
21
23
  let authApi: AuthApi | null = null
22
24
  let eventEmitter: EventEmitter | null = null
25
+ let redirectConfig: NormalizedRedirectConfig | null = null
26
+ let autoRedirectRouter: any = null // Router instance for auto-redirect
23
27
  const accountInfo = ref<AccountInfo | null>(null)
24
28
 
25
29
  interface InitParams {
26
30
  baseURL: string
31
+ /**
32
+ * Redirect configuration for authentication flows
33
+ * @see RedirectConfig
34
+ */
35
+ redirect?: RedirectConfig
36
+ }
37
+
38
+ /**
39
+ * Set the router instance for auto-redirect functionality
40
+ * Call this in your app setup after creating the router
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * const auth = createAuth({ ... })
45
+ * const router = createRouter({ ... })
46
+ * setAuthRouter(router)
47
+ * ```
48
+ */
49
+ export function setAuthRouter(router: any) {
50
+ autoRedirectRouter = router
51
+ }
52
+
53
+ /**
54
+ * Get the current redirect configuration
55
+ * Used internally by router guard
56
+ */
57
+ export function getRedirectConfig(): NormalizedRedirectConfig {
58
+ if (!redirectConfig) {
59
+ throw new Error('Redirect config not initialized. Did you call createAuth with redirect config?')
60
+ }
61
+ return redirectConfig
27
62
  }
28
63
 
29
64
  // Initialize auth
@@ -36,7 +71,17 @@ export function createAuth(params: InitParams) {
36
71
  eventEmitter = new EventEmitter()
37
72
  }
38
73
 
39
- return {
74
+ // Store redirect configuration
75
+ if (params.redirect) {
76
+ redirectConfig = normalizeRedirectConfig(params.redirect)
77
+ }
78
+
79
+ // Setup auto-redirect on login if enabled
80
+ if (redirectConfig?.autoRedirect) {
81
+ setupAutoRedirect()
82
+ }
83
+
84
+ const authInstance = {
40
85
  // Event listener methods
41
86
  on<K extends AuthState>(event: K, handler: AuthEventMap[K]): void {
42
87
  if (eventEmitter) {
@@ -61,6 +106,32 @@ export function createAuth(params: InitParams) {
61
106
  app.config.globalProperties.$auth = useAuth()
62
107
  },
63
108
  }
109
+
110
+ return authInstance
111
+ }
112
+
113
+ /**
114
+ * Setup automatic redirect after successful login
115
+ */
116
+ function setupAutoRedirect() {
117
+ if (!eventEmitter || !redirectConfig) return
118
+
119
+ eventEmitter.on(AuthState.LOGIN, async () => {
120
+ // Only auto-redirect if router is available
121
+ if (!autoRedirectRouter) {
122
+ console.warn('[Auth] Auto-redirect enabled but router not set. Call setAuthRouter(router) in your app setup.')
123
+ return
124
+ }
125
+
126
+ // Dynamic import to avoid circular dependency
127
+ const { performRedirect } = await import('./redirect')
128
+
129
+ try {
130
+ await performRedirect(autoRedirectRouter, redirectConfig!)
131
+ } catch (error) {
132
+ console.error('[Auth] Auto-redirect error:', error)
133
+ }
134
+ })
64
135
  }
65
136
 
66
137
  // Composable