@meistrari/auth-nuxt 2.3.0 → 2.4.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 CHANGED
@@ -8,6 +8,13 @@ A Nuxt module that provides comprehensive authentication, organization managemen
8
8
  npm install @meistrari/auth-nuxt
9
9
  ```
10
10
 
11
+ ## Prerequisites
12
+
13
+ Before setting up the SDK, make sure the following are configured in the Auth API:
14
+
15
+ 1. **Allowed Origins**: Your application URL (e.g., `https://your-app.com`) must be added to the allowed origins list in the Auth API. This ensures the Auth API accepts requests from your application.
16
+ 2. **Allowed Email Domains**: The email domains of users who will access the application must be added to the allowed email domains list in the Auth API. For example, if your users sign in with `@company.com` emails, that domain must be whitelisted.
17
+
11
18
  ## Setup
12
19
 
13
20
  Add the module to your `nuxt.config.ts`:
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/auth-nuxt",
3
3
  "configKey": "telaAuth",
4
- "version": "2.3.0",
4
+ "version": "2.4.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -66,6 +66,11 @@ const module$1 = defineNuxtModule({
66
66
  handler: resolver.resolve("./runtime/server/routes/auth/switch-organization"),
67
67
  method: "post"
68
68
  });
69
+ addServerHandler({
70
+ route: "/auth/whoami",
71
+ handler: resolver.resolve("./runtime/server/routes/auth/whoami"),
72
+ method: "get"
73
+ });
69
74
  addPlugin(resolver.resolve("./runtime/plugins/application-token-refresh"));
70
75
  addPlugin(resolver.resolve("./runtime/plugins/auth-guard"));
71
76
  addPlugin(resolver.resolve("./runtime/plugins/directives"));
@@ -40,6 +40,7 @@ export declare function useTelaApplicationAuth(): {
40
40
  getAvailableOrganizations: () => Promise<FullOrganization[]>;
41
41
  switchOrganization: (organizationId: string) => Promise<void>;
42
42
  refreshToken: () => Promise<void>;
43
+ getToken: () => Promise<string | null | undefined>;
43
44
  user: import("vue").Ref<{
44
45
  id: string;
45
46
  createdAt: Date;
@@ -1,6 +1,7 @@
1
1
  import { navigateTo, useCookie, useRuntimeConfig } from "#app";
2
2
  import { AuthorizationFlowError, isTokenExpired, RefreshTokenExpiredError, UserNotLoggedInError } from "@meistrari/auth-core";
3
3
  import { useApplicationSessionState } from "./state.js";
4
+ import { willTokenExpireIn } from "../helpers/token.js";
4
5
  const FIFTEEN_MINUTES = 60 * 15;
5
6
  const ONE_MINUTE = 60 * 1e3;
6
7
  export function useTelaApplicationAuth() {
@@ -87,6 +88,13 @@ export function useTelaApplicationAuth() {
87
88
  throw error;
88
89
  }
89
90
  }
91
+ async function getToken() {
92
+ const shouldRefresh = accessTokenCookie.value ? willTokenExpireIn(accessTokenCookie.value, ONE_MINUTE * 2) : true;
93
+ if (shouldRefresh) {
94
+ await refreshToken();
95
+ }
96
+ return accessTokenCookie.value;
97
+ }
90
98
  return {
91
99
  ...state,
92
100
  login,
@@ -94,6 +102,7 @@ export function useTelaApplicationAuth() {
94
102
  initSession,
95
103
  getAvailableOrganizations,
96
104
  switchOrganization,
97
- refreshToken
105
+ refreshToken,
106
+ getToken
98
107
  };
99
108
  }
@@ -105,30 +105,26 @@ export declare function useOrganizationState(): {
105
105
  role: "org:admin" | "org:member" | "org:reviewer";
106
106
  createdAt: Date;
107
107
  userId: string;
108
- teamId?: string | undefined | undefined;
108
+ teamId?: string | undefined | undefined | undefined;
109
109
  user: {
110
110
  id: string;
111
111
  email: string;
112
112
  name: string;
113
113
  image?: string | undefined;
114
114
  };
115
- deletedAt?: Date | undefined;
116
- lastActiveAt?: Date | undefined;
117
115
  } | null, {
118
116
  id: string;
119
117
  organizationId: string;
120
118
  role: "org:admin" | "org:member" | "org:reviewer";
121
119
  createdAt: Date;
122
120
  userId: string;
123
- teamId?: string | undefined | undefined;
121
+ teamId?: string | undefined | undefined | undefined;
124
122
  user: {
125
123
  id: string;
126
124
  email: string;
127
125
  name: string;
128
126
  image?: string | undefined;
129
127
  };
130
- deletedAt?: Date | undefined;
131
- lastActiveAt?: Date | undefined;
132
128
  } | null>;
133
129
  };
134
130
  export declare function useApplicationSessionState(): {
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Parses a JWT token to extract its expiration time
3
+ *
4
+ * @param token - The JWT token string
5
+ * @returns The expiration timestamp in milliseconds, or null if parsing fails
6
+ */
7
+ export declare function parseTokenExpiry(token: string): number | null;
8
+ /**
9
+ * Checks if a JWT token will expire within a certain time window, or if it is already expired
10
+ *
11
+ * @param token - The JWT token string
12
+ * @param timeWindow - The time window in milliseconds
13
+ * @returns True if the token will expire within the time window, false otherwise
14
+ */
15
+ export declare function willTokenExpireIn(token: string, timeWindow: number): boolean | 0;
@@ -0,0 +1,19 @@
1
+ import { decodeJwt } from "jose";
2
+ export function parseTokenExpiry(token) {
3
+ try {
4
+ const payload = decodeJwt(token);
5
+ if (!payload.exp)
6
+ return null;
7
+ return payload.exp * 1e3;
8
+ } catch {
9
+ return null;
10
+ }
11
+ }
12
+ export function willTokenExpireIn(token, timeWindow) {
13
+ const now = Date.now();
14
+ const expiry = parseTokenExpiry(token);
15
+ if (expiry === null) {
16
+ return true;
17
+ }
18
+ return expiry && expiry - timeWindow <= now;
19
+ }
@@ -1,32 +1,12 @@
1
1
  import { defineNuxtPlugin, useCookie, useRuntimeConfig } from "#app";
2
2
  import { isTokenExpired } from "@meistrari/auth-core";
3
- import { decodeJwt } from "jose";
4
3
  import { useTelaApplicationAuth } from "../composables/application-auth.js";
5
4
  import { useApplicationSessionState } from "../composables/state.js";
6
5
  import { createNuxtAuthClient } from "../shared.js";
6
+ import { parseTokenExpiry } from "../helpers/token.js";
7
7
  const SEVEN_DAYS = 60 * 60 * 24 * 7;
8
8
  const FIFTEEN_MINUTES = 60 * 15;
9
9
  const TWO_MINUTES = 2 * 60 * 1e3;
10
- function parseTokenExpiry(token) {
11
- try {
12
- const payload = decodeJwt(token);
13
- if (!payload.exp)
14
- return null;
15
- return payload.exp * 1e3;
16
- } catch {
17
- return null;
18
- }
19
- }
20
- function mapOrganization(organization) {
21
- return {
22
- name: organization.title,
23
- id: organization.id,
24
- createdAt: organization.createdAt,
25
- logo: organization.avatarUrl,
26
- metadata: organization.metadata,
27
- slug: organization.slug ?? ""
28
- };
29
- }
30
10
  export default defineNuxtPlugin({
31
11
  name: "tela-application-token-refresh",
32
12
  enforce: "post",
@@ -62,14 +42,14 @@ export default defineNuxtPlugin({
62
42
  accessTokenCookie.value = accessToken;
63
43
  refreshTokenCookie.value = refreshToken2;
64
44
  state.user.value = user2;
65
- state.activeOrganization.value = mapOrganization(organization2);
45
+ state.activeOrganization.value = organization2;
66
46
  return;
67
47
  }
68
48
  const { user, organization } = await $fetch("/auth/refresh", {
69
49
  method: "POST"
70
50
  });
71
51
  state.user.value = user;
72
- state.activeOrganization.value = mapOrganization(organization);
52
+ state.activeOrganization.value = organization;
73
53
  } catch {
74
54
  await sdkLogout();
75
55
  if (import.meta.client) {
@@ -107,7 +87,7 @@ export default defineNuxtPlugin({
107
87
  try {
108
88
  const data = await authClient.application.whoAmI(accessTokenCookie.value);
109
89
  state.user.value = data.user;
110
- state.activeOrganization.value = mapOrganization(data.organization);
90
+ state.activeOrganization.value = data.organization;
111
91
  } catch (error) {
112
92
  console.error("[Tela Auth SDK] Failed to get user and organization:", error.message);
113
93
  if (!refreshTokenCookie.value) {
@@ -135,6 +115,17 @@ export default defineNuxtPlugin({
135
115
  return;
136
116
  }
137
117
  if (import.meta.client) {
118
+ if (!state.user.value && accessTokenCookie.value) {
119
+ try {
120
+ const { user, organization } = await $fetch("/auth/whoami", {
121
+ method: "GET"
122
+ });
123
+ state.user.value = user;
124
+ state.activeOrganization.value = organization;
125
+ } catch (error) {
126
+ console.error("[Tela Auth SDK] Failed to load user info on client startup:", error);
127
+ }
128
+ }
138
129
  void scheduleTokenRefresh();
139
130
  }
140
131
  }
@@ -0,0 +1,20 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
2
+ success: boolean;
3
+ user: {
4
+ id: string;
5
+ createdAt: Date;
6
+ updatedAt: Date;
7
+ email: string;
8
+ emailVerified: boolean;
9
+ name: string;
10
+ image?: string | null | undefined;
11
+ twoFactorEnabled: boolean | null | undefined;
12
+ banned: boolean | null | undefined;
13
+ role?: string | null | undefined;
14
+ banReason?: string | null | undefined;
15
+ banExpires?: Date | null | undefined;
16
+ lastActiveAt?: Date | null | undefined;
17
+ };
18
+ organization: import("@meistrari/auth-core").FullOrganization;
19
+ }>>;
20
+ export default _default;
@@ -0,0 +1,31 @@
1
+ import { createError, useRuntimeConfig } from "#imports";
2
+ import { defineEventHandler, getCookie } from "h3";
3
+ import { createNuxtAuthClient } from "../../../shared.js";
4
+ export default defineEventHandler(async (event) => {
5
+ const config = useRuntimeConfig();
6
+ const authConfig = config.public.telaAuth;
7
+ const authClient = createNuxtAuthClient(authConfig.apiUrl, () => null, () => null);
8
+ const accessToken = getCookie(event, "tela-access-token");
9
+ if (!accessToken) {
10
+ throw createError({
11
+ statusCode: 401,
12
+ statusMessage: "Unauthorized",
13
+ message: "No access token found"
14
+ });
15
+ }
16
+ try {
17
+ const { user, organization } = await authClient.application.whoAmI(accessToken);
18
+ return {
19
+ success: true,
20
+ user,
21
+ organization
22
+ };
23
+ } catch (error) {
24
+ console.error("[Auth WhoAmI] Failed to get user and organization:", error);
25
+ throw createError({
26
+ statusCode: 401,
27
+ statusMessage: "Unauthorized",
28
+ message: "Failed to get user and organization"
29
+ });
30
+ }
31
+ });
@@ -3,7 +3,14 @@ import { createRemoteJWKSet, jwtVerify } from "jose";
3
3
  import { useRuntimeConfig } from "nitropack/runtime";
4
4
  export function requireAuth(handler, options) {
5
5
  return defineEventHandler(async (event) => {
6
+ const moduleOptions = useRuntimeConfig(event).public.telaAuth;
7
+ if (!moduleOptions.skipServerMiddleware && !options?.roles && import.meta.dev) {
8
+ console.warn("You have enabled the global server middleware, meaning you only need to use requireAuth() on routes that require specific roles.", `Triggered at ${event.path}`);
9
+ }
6
10
  if (event.context.auth?.user && event.context.auth?.token) {
11
+ if (import.meta.dev) {
12
+ console.debug("Using existing auth context from global server middleware");
13
+ }
7
14
  const user = event.context.auth.user;
8
15
  if (options?.roles && !options.roles.includes(user.role ?? "")) {
9
16
  throw createError({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/auth-nuxt",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -31,7 +31,7 @@
31
31
  "build": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt-module-build build"
32
32
  },
33
33
  "dependencies": {
34
- "@meistrari/auth-core": "1.11.0",
34
+ "@meistrari/auth-core": "1.11.2",
35
35
  "jose": "6.1.3"
36
36
  },
37
37
  "peerDependencies": {