@bagelink/auth 1.7.74 → 1.7.76

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 CHANGED
@@ -25,7 +25,7 @@ npm install @bagelink/auth
25
25
  ```typescript
26
26
  // main.ts
27
27
  import { createApp } from 'vue'
28
- import { createAuth, setAuthRouter, authGuard } from '@bagelink/auth'
28
+ import { createAuth } from '@bagelink/auth'
29
29
  import router from './router'
30
30
 
31
31
  const auth = createAuth({
@@ -37,11 +37,8 @@ const auth = createAuth({
37
37
  }
38
38
  })
39
39
 
40
- // Connect router for auto-redirect
41
- setAuthRouter(router)
42
-
43
- // Install auth guard
44
- router.beforeEach(authGuard())
40
+ // Connect router (automatically sets up auth guard)
41
+ auth.use(router)
45
42
 
46
43
  const app = createApp(App)
47
44
  app.use(router)
@@ -167,22 +164,78 @@ Returns auth composable with:
167
164
  }
168
165
  ```
169
166
 
170
- ### `authGuard()`
167
+ ### Custom Guard Composition
168
+
169
+ For multi-tenant apps or advanced permission logic, disable the auto-guard and compose your own:
171
170
 
172
- Navigation guard for protected routes:
171
+ #### Option 1: Using `composeGuards` utility (recommended)
173
172
 
174
173
  ```typescript
175
- router.beforeEach(authGuard()) // No parameters needed!
174
+ import { composeGuards } from '@bagelink/auth'
175
+
176
+ // Disable auto-guard
177
+ auth.use(router, { guard: false })
178
+
179
+ // Custom org access guard
180
+ const orgAccessGuard = () => async (to, from, next) => {
181
+ if (to.meta.requiresOrg) {
182
+ const hasAccess = await checkOrgAccess(to.params.orgId)
183
+ if (!hasAccess) return next('/no-access')
184
+ }
185
+ next()
186
+ }
187
+
188
+ // Compose all guards in sequence
189
+ router.beforeEach(composeGuards([
190
+ auth.routerGuard(), // Auth first
191
+ orgAccessGuard(), // Then org check
192
+ ]))
176
193
  ```
177
194
 
178
- Protects routes with `meta: { auth: true }` and handles redirects automatically.
195
+ #### Option 2: Multiple `beforeEach` calls
179
196
 
180
- ### `setAuthRouter(router)`
197
+ ```typescript
198
+ auth.use(router, { guard: false })
181
199
 
182
- Connect router for auto-redirect:
200
+ // Guards run in order they're registered
201
+ router.beforeEach(auth.routerGuard())
202
+
203
+ router.beforeEach(async (to, from, next) => {
204
+ // Custom org/tenant check
205
+ if (to.meta.requiresOrg && !await checkOrgAccess(to.params.orgId)) {
206
+ return next('/no-access')
207
+ }
208
+ next()
209
+ })
210
+ ```
211
+
212
+ #### Option 3: Manual composition
183
213
 
184
214
  ```typescript
185
- setAuthRouter(router) // Call after creating router
215
+ auth.use(router, { guard: false })
216
+
217
+ router.beforeEach(async (to, from, next) => {
218
+ // Run auth guard first
219
+ const authPassed = await new Promise<boolean>((resolve) => {
220
+ auth.routerGuard()(to, from, (result?: any) => {
221
+ if (result !== undefined) {
222
+ next(result)
223
+ resolve(false)
224
+ } else {
225
+ resolve(true)
226
+ }
227
+ })
228
+ })
229
+
230
+ if (!authPassed) return
231
+
232
+ // Your custom logic
233
+ if (to.meta.requiresOrg && !await checkOrgAccess(to.params.orgId)) {
234
+ return next('/no-access')
235
+ }
236
+
237
+ next()
238
+ })
186
239
  ```
187
240
 
188
241
  ## Components
package/dist/index.cjs CHANGED
@@ -1079,7 +1079,7 @@ const _sfc_main$4 = /* @__PURE__ */ vue.defineComponent({
1079
1079
  const authResponse = vue.ref(null);
1080
1080
  const { sso: sso2, user, accountInfo: accountInfo2 } = useAuth();
1081
1081
  const route = vueRouter.useRoute();
1082
- const router = vueRouter.useRouter();
1082
+ const router2 = vueRouter.useRouter();
1083
1083
  const providerInfo = vue.computed(() => {
1084
1084
  if (provider.value === null) return null;
1085
1085
  return providers[provider.value];
@@ -1089,7 +1089,7 @@ const _sfc_main$4 = /* @__PURE__ */ vue.defineComponent({
1089
1089
  try {
1090
1090
  await sso2.handleLinkCallback();
1091
1091
  success.value = true;
1092
- setTimeout(() => router.push("/"), timeout);
1092
+ setTimeout(() => router2.push("/"), timeout);
1093
1093
  } catch (err) {
1094
1094
  const errorMessage = err instanceof Error ? err.message : "Failed to link account";
1095
1095
  error.value = errorMessage;
@@ -1108,7 +1108,7 @@ const _sfc_main$4 = /* @__PURE__ */ vue.defineComponent({
1108
1108
  } else {
1109
1109
  authResponse.value = response;
1110
1110
  success.value = true;
1111
- setTimeout(() => router.push("/"), timeout);
1111
+ setTimeout(() => router2.push("/"), timeout);
1112
1112
  }
1113
1113
  } catch (err) {
1114
1114
  const errorMessage = err instanceof Error ? err.message : "Authentication failed";
@@ -1239,10 +1239,10 @@ const _sfc_main$3 = /* @__PURE__ */ vue.defineComponent({
1239
1239
  cardShadow: { type: Boolean, default: true }
1240
1240
  },
1241
1241
  setup(__props) {
1242
- const router = vueRouter.useRouter();
1242
+ const router2 = vueRouter.useRouter();
1243
1243
  function switchForm(form) {
1244
1244
  if (form === "login") {
1245
- router.push("/login");
1245
+ router2.push("/login");
1246
1246
  }
1247
1247
  }
1248
1248
  return (_ctx, _cache) => {
@@ -1283,12 +1283,12 @@ const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
1283
1283
  cardShadow: { type: Boolean, default: true }
1284
1284
  },
1285
1285
  setup(__props) {
1286
- const router = vueRouter.useRouter();
1286
+ const router2 = vueRouter.useRouter();
1287
1287
  function switchForm(form) {
1288
1288
  if (form === "signup") {
1289
- router.push("/signup");
1289
+ router2.push("/signup");
1290
1290
  } else if (form === "forgot-password") {
1291
- router.push("/forgot-password");
1291
+ router2.push("/forgot-password");
1292
1292
  }
1293
1293
  }
1294
1294
  return (_ctx, _cache) => {
@@ -1329,12 +1329,12 @@ const _sfc_main$1 = /* @__PURE__ */ vue.defineComponent({
1329
1329
  cardShadow: { type: Boolean, default: true }
1330
1330
  },
1331
1331
  setup(__props) {
1332
- const router = vueRouter.useRouter();
1332
+ const router2 = vueRouter.useRouter();
1333
1333
  const route = vueRouter.useRoute();
1334
1334
  const token = vue.computed(() => route.query.token);
1335
1335
  function switchForm(form) {
1336
1336
  if (form === "login") {
1337
- router.push("/login");
1337
+ router2.push("/login");
1338
1338
  }
1339
1339
  }
1340
1340
  return (_ctx, _cache) => {
@@ -1366,10 +1366,10 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
1366
1366
  cardShadow: { type: Boolean, default: true }
1367
1367
  },
1368
1368
  setup(__props) {
1369
- const router = vueRouter.useRouter();
1369
+ const router2 = vueRouter.useRouter();
1370
1370
  function switchForm(form) {
1371
1371
  if (form === "login") {
1372
- router.push("/login");
1372
+ router2.push("/login");
1373
1373
  }
1374
1374
  }
1375
1375
  return (_ctx, _cache) => {
@@ -1391,8 +1391,8 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
1391
1391
  };
1392
1392
  }
1393
1393
  });
1394
- function getRedirectUrl(router, config) {
1395
- const redirect2 = router.currentRoute.value.query[config.queryKey];
1394
+ function getRedirectUrl(router2, config) {
1395
+ const redirect2 = router2.currentRoute.value.query[config.queryKey];
1396
1396
  return redirect2 || config.fallback;
1397
1397
  }
1398
1398
  function isValidRedirect(redirectUrl, allowedPaths) {
@@ -1416,14 +1416,14 @@ function isValidRedirect(redirectUrl, allowedPaths) {
1416
1416
  }
1417
1417
  return true;
1418
1418
  }
1419
- async function performRedirect(router, config) {
1420
- const redirect2 = getRedirectUrl(router, config);
1419
+ async function performRedirect(router2, config) {
1420
+ const redirect2 = getRedirectUrl(router2, config);
1421
1421
  if (redirect2 !== config.fallback && !isValidRedirect(redirect2, config.allowedPaths)) {
1422
1422
  console.warn("[Auth] Invalid redirect URL detected, using fallback:", redirect2);
1423
- await router.push(config.fallback);
1423
+ await router2.push(config.fallback);
1424
1424
  return;
1425
1425
  }
1426
- await router.push(redirect2);
1426
+ await router2.push(redirect2);
1427
1427
  }
1428
1428
  function buildLoginQuery(currentPath, config) {
1429
1429
  if (!config.preserveRedirect) {
@@ -1908,10 +1908,8 @@ let authApi = null;
1908
1908
  let eventEmitter = null;
1909
1909
  let redirectConfig = null;
1910
1910
  let autoRedirectRouter = null;
1911
+ let cachedAuthGuard = null;
1911
1912
  const accountInfo = vue.ref(null);
1912
- function setAuthRouter(router) {
1913
- autoRedirectRouter = router;
1914
- }
1915
1913
  function getRedirectConfig() {
1916
1914
  if (!redirectConfig) {
1917
1915
  throw new Error("Redirect config not initialized. Did you call createAuth with redirect config?");
@@ -1948,6 +1946,69 @@ function createAuth(params) {
1948
1946
  eventEmitter.removeAllListeners(event);
1949
1947
  }
1950
1948
  },
1949
+ /**
1950
+ * Connect external dependencies like Vue Router
1951
+ * Automatically sets up router guard when router is provided
1952
+ * @param dependency - Vue Router instance or other plugins
1953
+ * @param options - Configuration options
1954
+ * @param options.guard - Whether to automatically set up auth guard (default: true)
1955
+ * @example
1956
+ * ```ts
1957
+ * // Auto setup (default)
1958
+ * auth.use(router)
1959
+ *
1960
+ * // Manual guard control (for custom composition)
1961
+ * auth.use(router, { guard: false })
1962
+ * router.beforeEach(async (to, from, next) => {
1963
+ * // Custom logic first
1964
+ * if (!hasOrgAccess(to)) return next('/no-access')
1965
+ * // Then run auth guard
1966
+ * return auth.routerGuard()(to, from, next)
1967
+ * })
1968
+ * ```
1969
+ */
1970
+ use(dependency, options = {}) {
1971
+ const { guard = true } = options;
1972
+ if (dependency && (dependency.beforeEach || dependency.push || dependency.currentRoute)) {
1973
+ autoRedirectRouter = dependency;
1974
+ if (guard) {
1975
+ dependency.beforeEach(authInstance.routerGuard());
1976
+ }
1977
+ }
1978
+ return authInstance;
1979
+ },
1980
+ /**
1981
+ * Create a Vue Router navigation guard for authentication
1982
+ * Protects routes requiring authentication and handles redirect logic
1983
+ * Note: Automatically called by auth.use(router), only use directly for custom setups
1984
+ * @example
1985
+ * ```ts
1986
+ * // Automatic (recommended)
1987
+ * auth.use(router)
1988
+ *
1989
+ * // Manual (for custom setups)
1990
+ * router.beforeEach(auth.routerGuard())
1991
+ * ```
1992
+ */
1993
+ routerGuard() {
1994
+ if (cachedAuthGuard === null) {
1995
+ cachedAuthGuard = async (to, from, next) => {
1996
+ const { authGuard: authGuard2 } = await Promise.resolve().then(() => router);
1997
+ const guard = authGuard2();
1998
+ cachedAuthGuard = guard;
1999
+ return guard(to, from, next);
2000
+ };
2001
+ }
2002
+ return cachedAuthGuard;
2003
+ },
2004
+ /**
2005
+ * Vue plugin install method
2006
+ * Makes auth available globally as $auth
2007
+ * @example
2008
+ * ```ts
2009
+ * app.use(auth)
2010
+ * ```
2011
+ */
1951
2012
  install(app) {
1952
2013
  app.config.globalProperties.$auth = useAuth();
1953
2014
  }
@@ -2218,7 +2279,6 @@ const useAuth$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePro
2218
2279
  __proto__: null,
2219
2280
  createAuth,
2220
2281
  getRedirectConfig,
2221
- setAuthRouter,
2222
2282
  useAuth
2223
2283
  }, Symbol.toStringTag, { value: "Module" }));
2224
2284
  let authInitialized = false;
@@ -2256,6 +2316,33 @@ function authGuard() {
2256
2316
  }
2257
2317
  };
2258
2318
  }
2319
+ function composeGuards(guards) {
2320
+ return async (to, from, next) => {
2321
+ let guardIndex = 0;
2322
+ const runNextGuard = async () => {
2323
+ if (guardIndex >= guards.length) {
2324
+ next();
2325
+ return;
2326
+ }
2327
+ const guard = guards[guardIndex];
2328
+ guardIndex++;
2329
+ await guard(to, from, (result) => {
2330
+ if (result !== void 0) {
2331
+ next(result);
2332
+ } else {
2333
+ runNextGuard();
2334
+ }
2335
+ });
2336
+ };
2337
+ await runNextGuard();
2338
+ };
2339
+ }
2340
+ const router = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2341
+ __proto__: null,
2342
+ authGuard,
2343
+ composeGuards,
2344
+ resetAuthState
2345
+ }, Symbol.toStringTag, { value: "Module" }));
2259
2346
  function createAuthRoutes(config = {}) {
2260
2347
  const {
2261
2348
  basePath = "",
@@ -2342,6 +2429,7 @@ exports.StateMismatchError = StateMismatchError;
2342
2429
  exports.accountToUser = accountToUser;
2343
2430
  exports.authGuard = authGuard;
2344
2431
  exports.buildLoginQuery = buildLoginQuery;
2432
+ exports.composeGuards = composeGuards;
2345
2433
  exports.createAuth = createAuth;
2346
2434
  exports.createAuthGuard = createAuthGuard;
2347
2435
  exports.createAuthRoutes = createAuthRoutes;
@@ -2356,7 +2444,6 @@ exports.performRedirect = performRedirect;
2356
2444
  exports.providers = providers;
2357
2445
  exports.resetAuthState = resetAuthState;
2358
2446
  exports.setAuthContext = setAuthContext;
2359
- exports.setAuthRouter = setAuthRouter;
2360
2447
  exports.sso = sso;
2361
2448
  exports.ssoProvidersList = ssoProvidersList;
2362
2449
  exports.useAuth = useAuth;
package/dist/index.mjs CHANGED
@@ -1077,7 +1077,7 @@ const _sfc_main$4 = /* @__PURE__ */ defineComponent({
1077
1077
  const authResponse = ref(null);
1078
1078
  const { sso: sso2, user, accountInfo: accountInfo2 } = useAuth();
1079
1079
  const route = useRoute();
1080
- const router = useRouter();
1080
+ const router2 = useRouter();
1081
1081
  const providerInfo = computed(() => {
1082
1082
  if (provider.value === null) return null;
1083
1083
  return providers[provider.value];
@@ -1087,7 +1087,7 @@ const _sfc_main$4 = /* @__PURE__ */ defineComponent({
1087
1087
  try {
1088
1088
  await sso2.handleLinkCallback();
1089
1089
  success.value = true;
1090
- setTimeout(() => router.push("/"), timeout);
1090
+ setTimeout(() => router2.push("/"), timeout);
1091
1091
  } catch (err) {
1092
1092
  const errorMessage = err instanceof Error ? err.message : "Failed to link account";
1093
1093
  error.value = errorMessage;
@@ -1106,7 +1106,7 @@ const _sfc_main$4 = /* @__PURE__ */ defineComponent({
1106
1106
  } else {
1107
1107
  authResponse.value = response;
1108
1108
  success.value = true;
1109
- setTimeout(() => router.push("/"), timeout);
1109
+ setTimeout(() => router2.push("/"), timeout);
1110
1110
  }
1111
1111
  } catch (err) {
1112
1112
  const errorMessage = err instanceof Error ? err.message : "Authentication failed";
@@ -1237,10 +1237,10 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({
1237
1237
  cardShadow: { type: Boolean, default: true }
1238
1238
  },
1239
1239
  setup(__props) {
1240
- const router = useRouter();
1240
+ const router2 = useRouter();
1241
1241
  function switchForm(form) {
1242
1242
  if (form === "login") {
1243
- router.push("/login");
1243
+ router2.push("/login");
1244
1244
  }
1245
1245
  }
1246
1246
  return (_ctx, _cache) => {
@@ -1281,12 +1281,12 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
1281
1281
  cardShadow: { type: Boolean, default: true }
1282
1282
  },
1283
1283
  setup(__props) {
1284
- const router = useRouter();
1284
+ const router2 = useRouter();
1285
1285
  function switchForm(form) {
1286
1286
  if (form === "signup") {
1287
- router.push("/signup");
1287
+ router2.push("/signup");
1288
1288
  } else if (form === "forgot-password") {
1289
- router.push("/forgot-password");
1289
+ router2.push("/forgot-password");
1290
1290
  }
1291
1291
  }
1292
1292
  return (_ctx, _cache) => {
@@ -1327,12 +1327,12 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
1327
1327
  cardShadow: { type: Boolean, default: true }
1328
1328
  },
1329
1329
  setup(__props) {
1330
- const router = useRouter();
1330
+ const router2 = useRouter();
1331
1331
  const route = useRoute();
1332
1332
  const token = computed(() => route.query.token);
1333
1333
  function switchForm(form) {
1334
1334
  if (form === "login") {
1335
- router.push("/login");
1335
+ router2.push("/login");
1336
1336
  }
1337
1337
  }
1338
1338
  return (_ctx, _cache) => {
@@ -1364,10 +1364,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1364
1364
  cardShadow: { type: Boolean, default: true }
1365
1365
  },
1366
1366
  setup(__props) {
1367
- const router = useRouter();
1367
+ const router2 = useRouter();
1368
1368
  function switchForm(form) {
1369
1369
  if (form === "login") {
1370
- router.push("/login");
1370
+ router2.push("/login");
1371
1371
  }
1372
1372
  }
1373
1373
  return (_ctx, _cache) => {
@@ -1389,8 +1389,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1389
1389
  };
1390
1390
  }
1391
1391
  });
1392
- function getRedirectUrl(router, config) {
1393
- const redirect2 = router.currentRoute.value.query[config.queryKey];
1392
+ function getRedirectUrl(router2, config) {
1393
+ const redirect2 = router2.currentRoute.value.query[config.queryKey];
1394
1394
  return redirect2 || config.fallback;
1395
1395
  }
1396
1396
  function isValidRedirect(redirectUrl, allowedPaths) {
@@ -1414,14 +1414,14 @@ function isValidRedirect(redirectUrl, allowedPaths) {
1414
1414
  }
1415
1415
  return true;
1416
1416
  }
1417
- async function performRedirect(router, config) {
1418
- const redirect2 = getRedirectUrl(router, config);
1417
+ async function performRedirect(router2, config) {
1418
+ const redirect2 = getRedirectUrl(router2, config);
1419
1419
  if (redirect2 !== config.fallback && !isValidRedirect(redirect2, config.allowedPaths)) {
1420
1420
  console.warn("[Auth] Invalid redirect URL detected, using fallback:", redirect2);
1421
- await router.push(config.fallback);
1421
+ await router2.push(config.fallback);
1422
1422
  return;
1423
1423
  }
1424
- await router.push(redirect2);
1424
+ await router2.push(redirect2);
1425
1425
  }
1426
1426
  function buildLoginQuery(currentPath, config) {
1427
1427
  if (!config.preserveRedirect) {
@@ -1906,10 +1906,8 @@ let authApi = null;
1906
1906
  let eventEmitter = null;
1907
1907
  let redirectConfig = null;
1908
1908
  let autoRedirectRouter = null;
1909
+ let cachedAuthGuard = null;
1909
1910
  const accountInfo = ref(null);
1910
- function setAuthRouter(router) {
1911
- autoRedirectRouter = router;
1912
- }
1913
1911
  function getRedirectConfig() {
1914
1912
  if (!redirectConfig) {
1915
1913
  throw new Error("Redirect config not initialized. Did you call createAuth with redirect config?");
@@ -1946,6 +1944,69 @@ function createAuth(params) {
1946
1944
  eventEmitter.removeAllListeners(event);
1947
1945
  }
1948
1946
  },
1947
+ /**
1948
+ * Connect external dependencies like Vue Router
1949
+ * Automatically sets up router guard when router is provided
1950
+ * @param dependency - Vue Router instance or other plugins
1951
+ * @param options - Configuration options
1952
+ * @param options.guard - Whether to automatically set up auth guard (default: true)
1953
+ * @example
1954
+ * ```ts
1955
+ * // Auto setup (default)
1956
+ * auth.use(router)
1957
+ *
1958
+ * // Manual guard control (for custom composition)
1959
+ * auth.use(router, { guard: false })
1960
+ * router.beforeEach(async (to, from, next) => {
1961
+ * // Custom logic first
1962
+ * if (!hasOrgAccess(to)) return next('/no-access')
1963
+ * // Then run auth guard
1964
+ * return auth.routerGuard()(to, from, next)
1965
+ * })
1966
+ * ```
1967
+ */
1968
+ use(dependency, options = {}) {
1969
+ const { guard = true } = options;
1970
+ if (dependency && (dependency.beforeEach || dependency.push || dependency.currentRoute)) {
1971
+ autoRedirectRouter = dependency;
1972
+ if (guard) {
1973
+ dependency.beforeEach(authInstance.routerGuard());
1974
+ }
1975
+ }
1976
+ return authInstance;
1977
+ },
1978
+ /**
1979
+ * Create a Vue Router navigation guard for authentication
1980
+ * Protects routes requiring authentication and handles redirect logic
1981
+ * Note: Automatically called by auth.use(router), only use directly for custom setups
1982
+ * @example
1983
+ * ```ts
1984
+ * // Automatic (recommended)
1985
+ * auth.use(router)
1986
+ *
1987
+ * // Manual (for custom setups)
1988
+ * router.beforeEach(auth.routerGuard())
1989
+ * ```
1990
+ */
1991
+ routerGuard() {
1992
+ if (cachedAuthGuard === null) {
1993
+ cachedAuthGuard = async (to, from, next) => {
1994
+ const { authGuard: authGuard2 } = await Promise.resolve().then(() => router);
1995
+ const guard = authGuard2();
1996
+ cachedAuthGuard = guard;
1997
+ return guard(to, from, next);
1998
+ };
1999
+ }
2000
+ return cachedAuthGuard;
2001
+ },
2002
+ /**
2003
+ * Vue plugin install method
2004
+ * Makes auth available globally as $auth
2005
+ * @example
2006
+ * ```ts
2007
+ * app.use(auth)
2008
+ * ```
2009
+ */
1949
2010
  install(app) {
1950
2011
  app.config.globalProperties.$auth = useAuth();
1951
2012
  }
@@ -2216,7 +2277,6 @@ const useAuth$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePro
2216
2277
  __proto__: null,
2217
2278
  createAuth,
2218
2279
  getRedirectConfig,
2219
- setAuthRouter,
2220
2280
  useAuth
2221
2281
  }, Symbol.toStringTag, { value: "Module" }));
2222
2282
  let authInitialized = false;
@@ -2254,6 +2314,33 @@ function authGuard() {
2254
2314
  }
2255
2315
  };
2256
2316
  }
2317
+ function composeGuards(guards) {
2318
+ return async (to, from, next) => {
2319
+ let guardIndex = 0;
2320
+ const runNextGuard = async () => {
2321
+ if (guardIndex >= guards.length) {
2322
+ next();
2323
+ return;
2324
+ }
2325
+ const guard = guards[guardIndex];
2326
+ guardIndex++;
2327
+ await guard(to, from, (result) => {
2328
+ if (result !== void 0) {
2329
+ next(result);
2330
+ } else {
2331
+ runNextGuard();
2332
+ }
2333
+ });
2334
+ };
2335
+ await runNextGuard();
2336
+ };
2337
+ }
2338
+ const router = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2339
+ __proto__: null,
2340
+ authGuard,
2341
+ composeGuards,
2342
+ resetAuthState
2343
+ }, Symbol.toStringTag, { value: "Module" }));
2257
2344
  function createAuthRoutes(config = {}) {
2258
2345
  const {
2259
2346
  basePath = "",
@@ -2341,6 +2428,7 @@ export {
2341
2428
  accountToUser,
2342
2429
  authGuard,
2343
2430
  buildLoginQuery,
2431
+ composeGuards,
2344
2432
  createAuth,
2345
2433
  createAuthGuard,
2346
2434
  createAuthRoutes,
@@ -2355,7 +2443,6 @@ export {
2355
2443
  providers,
2356
2444
  resetAuthState,
2357
2445
  setAuthContext,
2358
- setAuthRouter,
2359
2446
  sso,
2360
2447
  ssoProvidersList,
2361
2448
  useAuth
package/dist/router.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { NavigationGuardNext, RouteLocationNormalized } from 'vue-router';
1
+ import { NavigationGuard, NavigationGuardNext, RouteLocationNormalized } from 'vue-router';
2
2
  /**
3
3
  * Reset auth initialization state
4
4
  * Useful for testing or app reload scenarios
@@ -20,3 +20,17 @@ export declare function resetAuthState(): void;
20
20
  * ```
21
21
  */
22
22
  export declare function authGuard(): (to: RouteLocationNormalized, _from: RouteLocationNormalized, next: NavigationGuardNext) => Promise<void>;
23
+ /**
24
+ * Compose multiple navigation guards into one
25
+ * Guards are executed in order, stopping at the first one that calls next with a value
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * router.beforeEach(composeGuards([
30
+ * authGuard(),
31
+ * orgAccessGuard(),
32
+ * featureFlagGuard(),
33
+ * ]))
34
+ * ```
35
+ */
36
+ export declare function composeGuards(guards: NavigationGuard[]): NavigationGuard;
package/dist/useAuth.d.ts CHANGED
@@ -9,18 +9,6 @@ interface InitParams {
9
9
  */
10
10
  redirect?: RedirectConfig;
11
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
12
  /**
25
13
  * Get the current redirect configuration
26
14
  * Used internally by router guard
@@ -30,6 +18,52 @@ export declare function createAuth(params: InitParams): {
30
18
  on<K extends AuthState>(event: K, handler: AuthEventMap[K]): void;
31
19
  off<K extends AuthState>(event: K, handler: AuthEventMap[K]): void;
32
20
  removeAllListeners<K extends AuthState>(event?: K): void;
21
+ /**
22
+ * Connect external dependencies like Vue Router
23
+ * Automatically sets up router guard when router is provided
24
+ * @param dependency - Vue Router instance or other plugins
25
+ * @param options - Configuration options
26
+ * @param options.guard - Whether to automatically set up auth guard (default: true)
27
+ * @example
28
+ * ```ts
29
+ * // Auto setup (default)
30
+ * auth.use(router)
31
+ *
32
+ * // Manual guard control (for custom composition)
33
+ * auth.use(router, { guard: false })
34
+ * router.beforeEach(async (to, from, next) => {
35
+ * // Custom logic first
36
+ * if (!hasOrgAccess(to)) return next('/no-access')
37
+ * // Then run auth guard
38
+ * return auth.routerGuard()(to, from, next)
39
+ * })
40
+ * ```
41
+ */
42
+ use(dependency: any, options?: {
43
+ guard?: boolean;
44
+ }): /*elided*/ any;
45
+ /**
46
+ * Create a Vue Router navigation guard for authentication
47
+ * Protects routes requiring authentication and handles redirect logic
48
+ * Note: Automatically called by auth.use(router), only use directly for custom setups
49
+ * @example
50
+ * ```ts
51
+ * // Automatic (recommended)
52
+ * auth.use(router)
53
+ *
54
+ * // Manual (for custom setups)
55
+ * router.beforeEach(auth.routerGuard())
56
+ * ```
57
+ */
58
+ routerGuard(): any;
59
+ /**
60
+ * Vue plugin install method
61
+ * Makes auth available globally as $auth
62
+ * @example
63
+ * ```ts
64
+ * app.use(auth)
65
+ * ```
66
+ */
33
67
  install(app: App): void;
34
68
  };
35
69
  export declare function useAuth(): {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/auth",
3
3
  "type": "module",
4
- "version": "1.7.74",
4
+ "version": "1.7.76",
5
5
  "description": "Bagelink auth package",
6
6
  "author": {
7
7
  "name": "Bagel Studio",
package/src/router.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { NavigationGuardNext, RouteLocationNormalized } from 'vue-router'
1
+ import type { NavigationGuard, NavigationGuardNext, RouteLocationNormalized } from 'vue-router'
2
2
  import { buildLoginQuery } from './redirect'
3
3
  import { useAuth, getRedirectConfig } from './useAuth'
4
4
 
@@ -84,3 +84,46 @@ export function authGuard() {
84
84
  }
85
85
  }
86
86
  }
87
+
88
+ /**
89
+ * Compose multiple navigation guards into one
90
+ * Guards are executed in order, stopping at the first one that calls next with a value
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * router.beforeEach(composeGuards([
95
+ * authGuard(),
96
+ * orgAccessGuard(),
97
+ * featureFlagGuard(),
98
+ * ]))
99
+ * ```
100
+ */
101
+ export function composeGuards(guards: NavigationGuard[]): NavigationGuard {
102
+ return async (to, from, next) => {
103
+ let guardIndex = 0
104
+
105
+ const runNextGuard = async (): Promise<void> => {
106
+ if (guardIndex >= guards.length) {
107
+ // All guards passed, allow navigation
108
+ next()
109
+ return
110
+ }
111
+
112
+ const guard = guards[guardIndex]
113
+ guardIndex++
114
+
115
+ // Run the current guard
116
+ await guard(to, from, (result?: any) => {
117
+ if (result !== undefined) {
118
+ // Guard blocked or redirected, stop here
119
+ next(result)
120
+ } else {
121
+ // Guard passed, run next guard
122
+ runNextGuard()
123
+ }
124
+ })
125
+ }
126
+
127
+ await runNextGuard()
128
+ }
129
+ }
package/src/useAuth.ts CHANGED
@@ -24,6 +24,7 @@ let authApi: AuthApi | null = null
24
24
  let eventEmitter: EventEmitter | null = null
25
25
  let redirectConfig: NormalizedRedirectConfig | null = null
26
26
  let autoRedirectRouter: any = null // Router instance for auto-redirect
27
+ let cachedAuthGuard: any = null // Cached router guard
27
28
  const accountInfo = ref<AccountInfo | null>(null)
28
29
 
29
30
  interface InitParams {
@@ -35,21 +36,6 @@ interface InitParams {
35
36
  redirect?: RedirectConfig
36
37
  }
37
38
 
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
39
  /**
54
40
  * Get the current redirect configuration
55
41
  * Used internally by router guard
@@ -101,6 +87,76 @@ export function createAuth(params: InitParams) {
101
87
  }
102
88
  },
103
89
 
90
+ /**
91
+ * Connect external dependencies like Vue Router
92
+ * Automatically sets up router guard when router is provided
93
+ * @param dependency - Vue Router instance or other plugins
94
+ * @param options - Configuration options
95
+ * @param options.guard - Whether to automatically set up auth guard (default: true)
96
+ * @example
97
+ * ```ts
98
+ * // Auto setup (default)
99
+ * auth.use(router)
100
+ *
101
+ * // Manual guard control (for custom composition)
102
+ * auth.use(router, { guard: false })
103
+ * router.beforeEach(async (to, from, next) => {
104
+ * // Custom logic first
105
+ * if (!hasOrgAccess(to)) return next('/no-access')
106
+ * // Then run auth guard
107
+ * return auth.routerGuard()(to, from, next)
108
+ * })
109
+ * ```
110
+ */
111
+ use(dependency: any, options: { guard?: boolean } = {}) {
112
+ const { guard = true } = options
113
+
114
+ // Detect if it's a router by checking for common router properties
115
+ if (dependency && (dependency.beforeEach || dependency.push || dependency.currentRoute)) {
116
+ autoRedirectRouter = dependency
117
+ // Automatically set up the auth guard unless disabled
118
+ if (guard) {
119
+ dependency.beforeEach(authInstance.routerGuard())
120
+ }
121
+ }
122
+ return authInstance
123
+ },
124
+
125
+ /**
126
+ * Create a Vue Router navigation guard for authentication
127
+ * Protects routes requiring authentication and handles redirect logic
128
+ * Note: Automatically called by auth.use(router), only use directly for custom setups
129
+ * @example
130
+ * ```ts
131
+ * // Automatic (recommended)
132
+ * auth.use(router)
133
+ *
134
+ * // Manual (for custom setups)
135
+ * router.beforeEach(auth.routerGuard())
136
+ * ```
137
+ */
138
+ routerGuard() {
139
+ // Return factory that lazily loads authGuard to avoid circular dependency
140
+ if (cachedAuthGuard === null) {
141
+ cachedAuthGuard = async (to: any, from: any, next: any) => {
142
+ const { authGuard } = await import('./router')
143
+ const guard = authGuard()
144
+ // Cache the actual guard for next time
145
+ cachedAuthGuard = guard
146
+ return guard(to, from, next)
147
+ }
148
+ }
149
+ return cachedAuthGuard
150
+ },
151
+
152
+ /**
153
+ * Vue plugin install method
154
+ * Makes auth available globally as $auth
155
+ * @example
156
+ * ```ts
157
+ * app.use(auth)
158
+ * ```
159
+ */
104
160
  install(app: App) {
105
161
  // Make auth available globally
106
162
  app.config.globalProperties.$auth = useAuth()