@meistrari/auth-nuxt 1.0.5 → 1.1.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
@@ -99,7 +99,7 @@ await signInWithEmailAndPassword({
99
99
  ```typescript
100
100
  await signInWithSocialProvider({
101
101
  provider: 'google', // or 'microsoft'
102
- callbackURL: '/dashboard',
102
+ callbackURL: '/',
103
103
  errorCallbackURL: '/login?error=true'
104
104
  })
105
105
  ```
@@ -108,7 +108,7 @@ await signInWithSocialProvider({
108
108
  ```typescript
109
109
  await signInWithSaml({
110
110
  email: 'user@example.com',
111
- callbackURL: '/dashboard',
111
+ callbackURL: '/',
112
112
  errorCallbackURL: '/login?error=true'
113
113
  })
114
114
  ```
package/dist/module.d.mts CHANGED
@@ -7,6 +7,16 @@ interface ModuleOptions {
7
7
  jwtCookieName: string;
8
8
  /** Skip default server middleware */
9
9
  skipServerMiddleware: boolean;
10
+ application?: {
11
+ /** Whether to enable application authentication */
12
+ enabled: boolean;
13
+ /** Auth Dashboard URL */
14
+ dashboardUrl: string;
15
+ /** The ID of the application to authenticate with */
16
+ applicationId: string;
17
+ /** The redirect URI to redirect to after authentication. Must be registered in the application's settings. */
18
+ redirectUri: string;
19
+ };
10
20
  }
11
21
  declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
12
22
 
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/auth-nuxt",
3
3
  "configKey": "telaAuth",
4
- "version": "1.0.5",
4
+ "version": "1.1.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { defineNuxtModule, createResolver, addServerHandler, addImports, addPlugin } from '@nuxt/kit';
1
+ import { defineNuxtModule, createResolver, addImports, addPlugin, addServerHandler } from '@nuxt/kit';
2
2
 
3
3
  const module$1 = defineNuxtModule({
4
4
  meta: {
@@ -12,6 +12,15 @@ const module$1 = defineNuxtModule({
12
12
  setup(options, nuxt) {
13
13
  const resolver = createResolver(import.meta.url);
14
14
  nuxt.options.runtimeConfig.public.telaAuth = options;
15
+ if (options.application?.enabled) {
16
+ addImports({
17
+ name: "useTelaApplicationAuth",
18
+ as: "useTelaApplicationAuth",
19
+ from: resolver.resolve("runtime/composables/application-auth")
20
+ });
21
+ addPlugin(resolver.resolve("./runtime/plugins/application-token-refresh"));
22
+ return;
23
+ }
15
24
  if (!options.skipServerMiddleware) {
16
25
  addServerHandler({
17
26
  route: "",
@@ -33,7 +42,7 @@ const module$1 = defineNuxtModule({
33
42
  as: "useTelaApiKey",
34
43
  from: resolver.resolve("runtime/composables/api-key")
35
44
  });
36
- addPlugin(resolver.resolve("./runtime/plugin"));
45
+ addPlugin(resolver.resolve("./runtime/plugins/handshake"));
37
46
  }
38
47
  });
39
48
 
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Composable for managing Tela application authentication with OAuth 2.0 PKCE flow
3
+ *
4
+ * Provides methods to:
5
+ * - Initiate OAuth login flow with PKCE
6
+ * - Exchange authorization codes for tokens
7
+ * - Automatically refresh access tokens
8
+ * - Manage user session state
9
+ * - Handle logout
10
+ *
11
+ * @returns An object containing authentication state and methods
12
+ * @throws {Error} If auth dashboard URL is not configured
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const auth = useTelaApplicationAuth()
17
+ *
18
+ * // Initiate login
19
+ * await auth.login({
20
+ * applicationId: 'app-123',
21
+ * redirectUri: 'https://example.com/callback'
22
+ * })
23
+ *
24
+ * // Initialize session (handles OAuth callback and token refresh)
25
+ * await auth.initSession()
26
+ *
27
+ * // Access user state
28
+ * console.log(auth.user.value)
29
+ * console.log(auth.activeOrganization.value)
30
+ *
31
+ * // Logout
32
+ * await auth.logout()
33
+ * ```
34
+ */
35
+ export declare function useTelaApplicationAuth(): {
36
+ login: () => Promise<void>;
37
+ logout: () => Promise<void>;
38
+ initSession: () => Promise<void>;
39
+ user: import("vue").Ref<{
40
+ id: string;
41
+ createdAt: Date;
42
+ updatedAt: Date;
43
+ email: string;
44
+ emailVerified: boolean;
45
+ name: string;
46
+ image?: string | null | undefined;
47
+ twoFactorEnabled: boolean | null | undefined;
48
+ banned: boolean | null | undefined;
49
+ role?: string | null | undefined;
50
+ banReason?: string | null | undefined;
51
+ banExpires?: Date | null | undefined;
52
+ lastActiveAt?: Date | null | undefined;
53
+ } | null, {
54
+ id: string;
55
+ createdAt: Date;
56
+ updatedAt: Date;
57
+ email: string;
58
+ emailVerified: boolean;
59
+ name: string;
60
+ image?: string | null | undefined;
61
+ twoFactorEnabled: boolean | null | undefined;
62
+ banned: boolean | null | undefined;
63
+ role?: string | null | undefined;
64
+ banReason?: string | null | undefined;
65
+ banExpires?: Date | null | undefined;
66
+ lastActiveAt?: Date | null | undefined;
67
+ } | null>;
68
+ activeOrganization: import("vue").Ref<Omit<import("@meistrari/auth-core").FullOrganization, "members" | "invitations" | "teams"> | null, Omit<import("@meistrari/auth-core").FullOrganization, "members" | "invitations" | "teams"> | null>;
69
+ };
@@ -0,0 +1,171 @@
1
+ import { navigateTo, useCookie, useRoute, useRuntimeConfig } from "#app";
2
+ import { createNuxtAuthClient } from "../shared.js";
3
+ import { useApplicationSessionState } from "./state.js";
4
+ import { AuthorizationFlowError, isTokenExpired, RefreshTokenExpiredError } from "@meistrari/auth-core";
5
+ const SEVEN_DAYS = 60 * 60 * 24 * 7;
6
+ const FIFTEEN_MINUTES = 60 * 15;
7
+ const ONE_MINUTE = 60 * 1e3;
8
+ function verifier(stateKey) {
9
+ return `code_verifier_${stateKey}`;
10
+ }
11
+ function hexEncode(bytes) {
12
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
13
+ }
14
+ function generateStateKey(query) {
15
+ if (query.state && typeof query.state === "string") {
16
+ return query.state;
17
+ }
18
+ const array = new Uint8Array(8);
19
+ crypto.getRandomValues(array);
20
+ return hexEncode(array);
21
+ }
22
+ function generateCodeVerifier() {
23
+ const array = new Uint8Array(32);
24
+ crypto.getRandomValues(array);
25
+ return base64UrlEncode(array);
26
+ }
27
+ async function generateCodeChallenge(verifier2) {
28
+ const encoder = new TextEncoder();
29
+ const data = encoder.encode(verifier2);
30
+ const digest = await crypto.subtle.digest("SHA-256", data);
31
+ return base64UrlEncode(new Uint8Array(digest));
32
+ }
33
+ function base64UrlEncode(bytes) {
34
+ return btoa(String.fromCharCode(...bytes)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
35
+ }
36
+ function mapOrganization(organization) {
37
+ return {
38
+ name: organization.title,
39
+ id: organization.id,
40
+ createdAt: organization.createdAt,
41
+ logo: organization.avatarUrl,
42
+ metadata: organization.metadata,
43
+ slug: organization.slug ?? ""
44
+ };
45
+ }
46
+ export function useTelaApplicationAuth() {
47
+ const appConfig = useRuntimeConfig().public.telaAuth;
48
+ const query = useRoute().query;
49
+ const accessTokenCookie = useCookie("tela-access-token", {
50
+ secure: true,
51
+ sameSite: "lax",
52
+ maxAge: FIFTEEN_MINUTES
53
+ });
54
+ const refreshTokenCookie = useCookie("tela-refresh-token", {
55
+ secure: true,
56
+ sameSite: "lax",
57
+ maxAge: SEVEN_DAYS
58
+ });
59
+ const authClient = createNuxtAuthClient(appConfig.apiUrl, () => null, () => refreshTokenCookie.value ?? null);
60
+ const state = useApplicationSessionState();
61
+ if (!appConfig.application?.dashboardUrl) {
62
+ throw new Error(
63
+ "[Tela Auth SDK] Auth dashboard URL is not configured, but it is required to use application authentication."
64
+ );
65
+ }
66
+ if (!appConfig.application?.applicationId) {
67
+ throw new Error(
68
+ "[Tela Auth SDK] Application ID is not configured, but it is required to use application authentication."
69
+ );
70
+ }
71
+ if (!appConfig.application?.redirectUri) {
72
+ throw new Error(
73
+ "[Tela Auth SDK] Redirect URI is not configured, but it is required to use application authentication."
74
+ );
75
+ }
76
+ const { applicationId, redirectUri } = appConfig.application;
77
+ async function login() {
78
+ if (import.meta.server) {
79
+ throw new AuthorizationFlowError("The login function can only be called on the client side.");
80
+ }
81
+ if (typeof localStorage === "undefined") {
82
+ throw new AuthorizationFlowError("localStorage is not available. The login function must be called on the client side.");
83
+ }
84
+ const codeVerifier = generateCodeVerifier();
85
+ const codeChallenge = await generateCodeChallenge(codeVerifier);
86
+ const stateKey = generateStateKey(query);
87
+ localStorage.setItem(verifier(stateKey), codeVerifier);
88
+ const url = new URL("/applications/login", appConfig.application?.dashboardUrl);
89
+ url.searchParams.set("application_id", applicationId);
90
+ url.searchParams.set("code_challenge", codeChallenge);
91
+ url.searchParams.set("redirect_uri", redirectUri);
92
+ url.searchParams.set("state", stateKey);
93
+ navigateTo(url.toString(), { external: true });
94
+ }
95
+ async function logout() {
96
+ accessTokenCookie.value = null;
97
+ refreshTokenCookie.value = null;
98
+ state.user.value = null;
99
+ state.activeOrganization.value = null;
100
+ if (typeof localStorage !== "undefined") {
101
+ for (const key in localStorage) {
102
+ if (key.startsWith("code_verifier_")) {
103
+ localStorage.removeItem(key);
104
+ }
105
+ }
106
+ }
107
+ }
108
+ async function exchangeCodeForToken() {
109
+ if (import.meta.server) {
110
+ throw new AuthorizationFlowError("The exchangeCodeForToken function can only be called on the client side.");
111
+ }
112
+ const code = query.code;
113
+ const stateKey = query.state;
114
+ if (!code) {
115
+ throw new AuthorizationFlowError("Authorization code not found in query parameters");
116
+ }
117
+ if (!stateKey) {
118
+ throw new AuthorizationFlowError("State parameter not found in query parameters");
119
+ }
120
+ if (typeof localStorage === "undefined") {
121
+ throw new AuthorizationFlowError("localStorage is not available");
122
+ }
123
+ const codeVerifierKey = verifier(stateKey);
124
+ const codeVerifier = localStorage.getItem(codeVerifierKey);
125
+ if (!codeVerifier) {
126
+ throw new AuthorizationFlowError("Code verifier not found. This may indicate a CSRF attack or expired session.");
127
+ }
128
+ try {
129
+ const { accessToken, refreshToken: refreshToken2, user, organization } = await authClient.application.completeAuthorizationFlow(code, codeVerifier);
130
+ accessTokenCookie.value = accessToken;
131
+ refreshTokenCookie.value = refreshToken2;
132
+ state.user.value = user;
133
+ state.activeOrganization.value = mapOrganization(organization);
134
+ } finally {
135
+ localStorage.removeItem(codeVerifierKey);
136
+ }
137
+ }
138
+ async function refreshToken() {
139
+ if (!refreshTokenCookie.value) {
140
+ throw new RefreshTokenExpiredError();
141
+ }
142
+ const { accessToken, refreshToken: refreshToken2, user, organization } = await authClient.application.refreshAccessToken();
143
+ accessTokenCookie.value = accessToken;
144
+ refreshTokenCookie.value = refreshToken2;
145
+ state.user.value = user;
146
+ state.activeOrganization.value = mapOrganization(organization);
147
+ }
148
+ async function initSession() {
149
+ const code = query.code;
150
+ if (code) {
151
+ await exchangeCodeForToken();
152
+ }
153
+ if (!accessTokenCookie.value && !refreshTokenCookie.value) {
154
+ throw new RefreshTokenExpiredError();
155
+ }
156
+ const isExpiredOrClose = accessTokenCookie.value ? isTokenExpired(accessTokenCookie.value, ONE_MINUTE) : true;
157
+ if (isExpiredOrClose && !refreshTokenCookie.value) {
158
+ await logout();
159
+ throw new RefreshTokenExpiredError();
160
+ }
161
+ if (isExpiredOrClose && refreshTokenCookie.value) {
162
+ await refreshToken();
163
+ }
164
+ }
165
+ return {
166
+ ...state,
167
+ login,
168
+ logout,
169
+ initSession
170
+ };
171
+ }
@@ -1,16 +1,15 @@
1
1
  import type { Ref } from 'vue';
2
2
  import type { CreateTeamPayload, FullOrganization, Invitation, InviteUserToOrganizationOptions, ListMembersOptions, Member, RemoveUserFromOrganizationOptions, Team, TeamMember, UpdateMemberRoleOptions, UpdateOrganizationPayload, UpdateTeamPayload } from '@meistrari/auth-core';
3
- import type { FullOrganizationWithRelations } from './state.js';
4
3
  export interface UseTelaOrganizationReturn {
5
4
  /** Reactive reference to the active organization with members, invitations, and teams. */
6
- activeOrganization: Ref<FullOrganizationWithRelations | null>;
5
+ activeOrganization: Ref<FullOrganization | null>;
7
6
  /** Reactive reference to the current user's membership in the active organization. */
8
7
  activeMember: Ref<Member | null>;
9
8
  /**
10
9
  * Retrieves the active organization for the current user session and updates the active organization state.
11
10
  * @returns The active organization with members, invitations, and teams
12
11
  */
13
- getActiveOrganization: () => Promise<FullOrganizationWithRelations | undefined>;
12
+ getActiveOrganization: () => Promise<FullOrganization | undefined>;
14
13
  /**
15
14
  * Lists all organizations for the current user session.
16
15
  * @returns A list of organizations
@@ -10,14 +10,7 @@ export function useTelaOrganization() {
10
10
  if (!session.value?.activeOrganizationId) {
11
11
  return;
12
12
  }
13
- const organization = await authClient.organization.getOrganization(
14
- session.value?.activeOrganizationId,
15
- {
16
- includeMembers: true,
17
- includeInvitations: true,
18
- includeTeams: true
19
- }
20
- );
13
+ const organization = await authClient.organization.getOrganization(session.value?.activeOrganizationId);
21
14
  activeOrganization.value = organization;
22
15
  return organization;
23
16
  }
@@ -27,11 +20,7 @@ export function useTelaOrganization() {
27
20
  async function setActiveOrganization(id) {
28
21
  await authClient.organization.setActiveOrganization(id);
29
22
  const [organization, { token }] = await Promise.all([
30
- authClient.organization.getOrganization(id, {
31
- includeMembers: true,
32
- includeInvitations: true,
33
- includeTeams: true
34
- }),
23
+ authClient.organization.getOrganization(id),
35
24
  authClient.session.getToken()
36
25
  ]);
37
26
  useCookie(jwtCookieName).value = token;
@@ -1,9 +1,4 @@
1
- import type { FullOrganization, Invitation, Member, Team } from '@meistrari/auth-core';
2
- export type FullOrganizationWithRelations = FullOrganization & {
3
- members: Member[];
4
- invitations: Invitation[];
5
- teams: Team[];
6
- };
1
+ import type { FullOrganization, Member } from '@meistrari/auth-core';
7
2
  /**
8
3
  * Shared state for session management.
9
4
  * This module provides access to session-related state without creating circular dependencies.
@@ -69,6 +64,38 @@ export declare function useSessionState(): {
69
64
  * This module provides access to organization-related state without creating circular dependencies.
70
65
  */
71
66
  export declare function useOrganizationState(): {
72
- activeOrganization: import("vue").Ref<FullOrganizationWithRelations | null, FullOrganizationWithRelations | null>;
67
+ activeOrganization: import("vue").Ref<FullOrganization | null, FullOrganization | null>;
73
68
  activeMember: import("vue").Ref<Member | null, Member | null>;
74
69
  };
70
+ export declare function useApplicationSessionState(): {
71
+ user: import("vue").Ref<{
72
+ id: string;
73
+ createdAt: Date;
74
+ updatedAt: Date;
75
+ email: string;
76
+ emailVerified: boolean;
77
+ name: string;
78
+ image?: string | null | undefined;
79
+ twoFactorEnabled: boolean | null | undefined;
80
+ banned: boolean | null | undefined;
81
+ role?: string | null | undefined;
82
+ banReason?: string | null | undefined;
83
+ banExpires?: Date | null | undefined;
84
+ lastActiveAt?: Date | null | undefined;
85
+ } | null, {
86
+ id: string;
87
+ createdAt: Date;
88
+ updatedAt: Date;
89
+ email: string;
90
+ emailVerified: boolean;
91
+ name: string;
92
+ image?: string | null | undefined;
93
+ twoFactorEnabled: boolean | null | undefined;
94
+ banned: boolean | null | undefined;
95
+ role?: string | null | undefined;
96
+ banReason?: string | null | undefined;
97
+ banExpires?: Date | null | undefined;
98
+ lastActiveAt?: Date | null | undefined;
99
+ } | null>;
100
+ activeOrganization: import("vue").Ref<Omit<FullOrganization, "members" | "invitations" | "teams"> | null, Omit<FullOrganization, "members" | "invitations" | "teams"> | null>;
101
+ };
@@ -15,3 +15,11 @@ export function useOrganizationState() {
15
15
  activeMember
16
16
  };
17
17
  }
18
+ export function useApplicationSessionState() {
19
+ const user = useState("user", () => null);
20
+ const activeOrganization = useState("activeOrganization", () => null);
21
+ return {
22
+ user,
23
+ activeOrganization
24
+ };
25
+ }
@@ -0,0 +1,134 @@
1
+ import { defineNuxtPlugin, useCookie, useRuntimeConfig } from "#app";
2
+ import { isTokenExpired } from "@meistrari/auth-core";
3
+ import { useApplicationSessionState } from "../composables/state.js";
4
+ import { createNuxtAuthClient } from "../shared.js";
5
+ import { watch } from "vue";
6
+ const SEVEN_DAYS = 60 * 60 * 24 * 7;
7
+ const FIFTEEN_MINUTES = 60 * 15;
8
+ const TWO_MINUTES = 2 * 60 * 1e3;
9
+ function parseTokenExpiry(token) {
10
+ try {
11
+ const tokenParts = token.split(".");
12
+ const payloadPart = tokenParts[1];
13
+ if (!payloadPart) return null;
14
+ const payload = JSON.parse(atob(payloadPart));
15
+ if (!payload.exp) return null;
16
+ return payload.exp * 1e3;
17
+ } catch {
18
+ return null;
19
+ }
20
+ }
21
+ function mapOrganization(organization) {
22
+ return {
23
+ name: organization.title,
24
+ id: organization.id,
25
+ createdAt: organization.createdAt,
26
+ logo: organization.avatarUrl,
27
+ metadata: organization.metadata,
28
+ slug: organization.slug ?? ""
29
+ };
30
+ }
31
+ export default defineNuxtPlugin({
32
+ name: "tela-application-token-refresh",
33
+ enforce: "post",
34
+ env: {
35
+ islands: false
36
+ },
37
+ async setup() {
38
+ const appConfig = useRuntimeConfig().public.telaAuth;
39
+ const state = useApplicationSessionState();
40
+ const accessTokenCookie = useCookie("tela-access-token", {
41
+ secure: true,
42
+ sameSite: "lax",
43
+ maxAge: FIFTEEN_MINUTES
44
+ });
45
+ const refreshTokenCookie = useCookie("tela-refresh-token", {
46
+ secure: true,
47
+ sameSite: "lax",
48
+ maxAge: SEVEN_DAYS
49
+ });
50
+ const authClient = createNuxtAuthClient(appConfig.apiUrl, () => null, () => refreshTokenCookie.value ?? null);
51
+ let tokenRefreshInterval = null;
52
+ let isRefreshing = false;
53
+ let retryCount = 0;
54
+ const MAX_RETRIES = 3;
55
+ async function refreshToken() {
56
+ if (isRefreshing) {
57
+ return;
58
+ }
59
+ isRefreshing = true;
60
+ try {
61
+ const { accessToken, refreshToken: refreshToken2, user, organization } = await authClient.application.refreshAccessToken();
62
+ accessTokenCookie.value = accessToken;
63
+ refreshTokenCookie.value = refreshToken2;
64
+ state.user.value = user;
65
+ state.activeOrganization.value = mapOrganization(organization);
66
+ } finally {
67
+ isRefreshing = false;
68
+ }
69
+ }
70
+ function logout() {
71
+ accessTokenCookie.value = null;
72
+ refreshTokenCookie.value = null;
73
+ state.user.value = null;
74
+ state.activeOrganization.value = null;
75
+ }
76
+ async function scheduleTokenRefresh() {
77
+ if (tokenRefreshInterval) {
78
+ clearTimeout(tokenRefreshInterval);
79
+ tokenRefreshInterval = null;
80
+ }
81
+ if (!accessTokenCookie.value) {
82
+ return;
83
+ }
84
+ if (isTokenExpired(accessTokenCookie.value, TWO_MINUTES)) {
85
+ await refreshToken();
86
+ }
87
+ const expiry = parseTokenExpiry(accessTokenCookie.value);
88
+ if (!expiry) {
89
+ return;
90
+ }
91
+ const nextRefresh = Math.max(expiry - TWO_MINUTES - Date.now(), 0);
92
+ tokenRefreshInterval = window.setTimeout(refreshToken, nextRefresh);
93
+ }
94
+ if (import.meta.server) {
95
+ if (accessTokenCookie.value) {
96
+ try {
97
+ const data = await authClient.application.whoAmI(accessTokenCookie.value);
98
+ state.user.value = data.user;
99
+ state.activeOrganization.value = mapOrganization(data.organization);
100
+ } catch (error) {
101
+ console.error(`[Tela Auth SDK] Failed to get user and organization:`, error.message);
102
+ if (!refreshTokenCookie.value) {
103
+ console.error(`[Tela Auth SDK] Missing refresh token, logging out...`);
104
+ logout();
105
+ return;
106
+ }
107
+ const refreshTokenValue = refreshTokenCookie.value;
108
+ try {
109
+ await refreshToken();
110
+ } catch (error2) {
111
+ console.error(`[Tela Auth SDK] Failed to refresh token ${refreshTokenValue}...:`, error2.message);
112
+ logout();
113
+ }
114
+ }
115
+ }
116
+ if (!accessTokenCookie.value && refreshTokenCookie.value) {
117
+ try {
118
+ await refreshToken();
119
+ } catch (error) {
120
+ console.error(`[Tela Auth SDK] Failed to refresh token ${refreshTokenCookie.value}...:`, error.message);
121
+ logout();
122
+ }
123
+ }
124
+ return;
125
+ }
126
+ if (import.meta.client) {
127
+ watch(refreshTokenCookie, async (newVal) => {
128
+ if (newVal) {
129
+ await scheduleTokenRefresh();
130
+ }
131
+ }, { immediate: true });
132
+ }
133
+ }
134
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: import("#app").Plugin<Record<string, unknown>> & import("#app").ObjectPlugin<Record<string, unknown>>;
2
+ export default _default;
@@ -1,10 +1,10 @@
1
1
  import { defineNuxtPlugin, useCookie, useRuntimeConfig, useRequestURL } from "#app";
2
2
  import { watch } from "vue";
3
- import { useTelaSession } from "./composables/session.js";
4
- import { createNuxtAuthClient } from "./shared.js";
3
+ import { useTelaSession } from "../composables/session.js";
4
+ import { createNuxtAuthClient } from "../shared.js";
5
5
  import { useRoute, navigateTo } from "#imports";
6
6
  export default defineNuxtPlugin({
7
- name: "tela-auth",
7
+ name: "tela-auth-handshake",
8
8
  enforce: "pre",
9
9
  env: {
10
10
  islands: false
@@ -1,2 +1,2 @@
1
1
  import { AuthClient } from '@meistrari/auth-core';
2
- export declare function createNuxtAuthClient(apiUrl: string, getAuthToken: () => string | null): AuthClient;
2
+ export declare function createNuxtAuthClient(apiUrl: string, getAuthToken: () => string | null, getRefreshToken?: () => string | null): AuthClient;
@@ -1,6 +1,6 @@
1
1
  import { AuthClient } from "@meistrari/auth-core";
2
2
  import { version } from "../../package.json";
3
- export function createNuxtAuthClient(apiUrl, getAuthToken) {
3
+ export function createNuxtAuthClient(apiUrl, getAuthToken, getRefreshToken = () => null) {
4
4
  const serviceName = typeof process !== "undefined" ? process.env.SERVICE_NAME : "";
5
5
  const userAgent = `auth-sdk:nuxt:${version}${serviceName ? `@${serviceName}` : ""}`;
6
6
  return new AuthClient(apiUrl, {
@@ -14,6 +14,15 @@ export function createNuxtAuthClient(apiUrl, getAuthToken) {
14
14
  if (token && !isTokenRequest) {
15
15
  context.headers.set("Authorization", `Bearer ${token}`);
16
16
  }
17
+ const isRefreshTokenRequest = requestUrl.pathname.endsWith("/api/auth/applications/token/refresh");
18
+ if (isRefreshTokenRequest) {
19
+ const refreshToken = getRefreshToken();
20
+ if (refreshToken) {
21
+ const cookie = context.headers.get("Cookie");
22
+ const newCookie = cookie ? `${cookie}; tela-refresh-token=${refreshToken}` : `tela-refresh-token=${refreshToken}`;
23
+ context.headers.set("Cookie", newCookie);
24
+ }
25
+ }
17
26
  }
18
27
  });
19
28
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/auth-nuxt",
3
- "version": "1.0.5",
3
+ "version": "1.1.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.5.1"
34
+ "@meistrari/auth-core": "1.6.0"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "nuxt": "^3.0.0 || ^4.0.0"