@meistrari/auth-nuxt 0.2.0 → 1.0.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.
@@ -1,105 +1,90 @@
1
- import { defineNuxtPlugin, useCookie, useRequestHeaders, useRuntimeConfig } from "#app";
1
+ import { defineNuxtPlugin, useCookie, useRuntimeConfig, useRequestURL } from "#app";
2
2
  import { watch } from "vue";
3
- import { createAuthClient, isTokenExpired } from "@meistrari/auth-core";
4
- import { useMeistrariAuth } from "./composable.js";
3
+ import { useTelaSession } from "./composables/session.js";
4
+ import { createNuxtAuthClient } from "./shared.js";
5
+ import { useRoute, navigateTo } from "#imports";
5
6
  export default defineNuxtPlugin({
6
- name: "meistrari-auth",
7
- parallel: true,
7
+ name: "tela-auth",
8
+ enforce: "pre",
9
+ env: {
10
+ islands: false
11
+ },
8
12
  async setup() {
9
- const config = useRuntimeConfig().public.auth;
10
- const authClient = createAuthClient(config.apiUrl);
11
- const { user, session } = useMeistrariAuth();
12
- async function getToken() {
13
- const token = useCookie(config.jwtCookieName);
14
- if (token.value) {
15
- if (!isTokenExpired(token.value)) {
16
- return token.value;
17
- }
18
- }
19
- try {
20
- return await refreshToken();
21
- } catch (error) {
22
- throw new Error("Failed to get token: " + (error instanceof Error ? error.message : String(error)));
23
- }
24
- }
25
- async function refreshToken() {
26
- const token = useCookie(config.jwtCookieName);
27
- const headers = useRequestHeaders();
28
- const session2 = await authClient.getSession({
29
- fetchOptions: {
30
- // on the client side we include credentials
31
- credentials: "include",
32
- // on the server side we forward the cookies
33
- headers,
34
- onSuccess: (ctx) => {
35
- const jwt = ctx.response.headers.get("set-auth-jwt");
36
- if (jwt) {
37
- token.value = jwt;
38
- }
13
+ const authConfig = useRuntimeConfig().public.telaAuth;
14
+ const { session, user, getToken } = useTelaSession();
15
+ const tokenCookie = useCookie(authConfig.jwtCookieName);
16
+ tokenCookie.value = null;
17
+ const authClient = createNuxtAuthClient(authConfig.apiUrl, () => tokenCookie.value ?? null);
18
+ const query = useRoute().query;
19
+ const nonce = query.nonce;
20
+ const origin = useRequestURL();
21
+ if (import.meta.server) {
22
+ if (nonce && typeof nonce === "string") {
23
+ if (nonce !== "null") {
24
+ try {
25
+ const payload = await authClient.session.getNoncePayload(nonce);
26
+ tokenCookie.value = payload.token;
27
+ } catch {
28
+ await navigateTo(`${authConfig.apiUrl}/api/auth/handshake?origin=${origin.toString()}`, {
29
+ external: true,
30
+ redirectCode: 302
31
+ });
32
+ return;
39
33
  }
40
34
  }
41
- });
42
- if (session2.error || !session2.data?.session) {
43
- throw new Error("Failed to refresh token");
35
+ } else {
36
+ await navigateTo(`${authConfig.apiUrl}/api/auth/handshake?origin=${origin.toString()}`, {
37
+ external: true,
38
+ redirectCode: 302
39
+ });
40
+ return;
41
+ }
42
+ } else if (import.meta.client && !session.value) {
43
+ const token = await getToken();
44
+ if (token) {
45
+ tokenCookie.value = token;
44
46
  }
45
- return token.value;
46
47
  }
47
48
  async function initializeSession() {
48
49
  if (session.value && user.value)
49
50
  return;
50
- const token = useCookie(config.jwtCookieName);
51
- const headers = import.meta.server ? useRequestHeaders() : void 0;
52
- const { data: sessionData, error: sessionError } = await authClient.getSession(
53
- {
54
- fetchOptions: {
55
- // on the client side we include credentials
56
- credentials: "include",
57
- // on the server side we forward the cookies
58
- headers,
59
- ...config.useJwt && {
60
- onSuccess: (context) => {
61
- const jwt = context.response.headers.get("Set-Auth-Jwt");
62
- if (jwt) {
63
- token.value = jwt;
64
- }
65
- }
66
- }
67
- }
68
- }
69
- );
70
- if (sessionError || !sessionData) {
51
+ const sessionData = await authClient.session.getSession();
52
+ if (!sessionData) {
71
53
  return;
72
54
  }
73
55
  user.value = sessionData.user;
74
56
  session.value = sessionData.session;
75
57
  }
76
- await initializeSession();
58
+ if (tokenCookie.value) {
59
+ await initializeSession();
60
+ }
77
61
  if (import.meta.client) {
78
- if (config.useJwt) {
79
- let tokenRefreshInterval = null;
80
- watch(session, async (newVal) => {
81
- if (tokenRefreshInterval) {
82
- clearTimeout(tokenRefreshInterval);
83
- tokenRefreshInterval = null;
84
- }
85
- if (newVal) {
86
- async function createTokenRefreshInterval() {
87
- await refreshToken();
88
- tokenRefreshInterval = window.setTimeout(createTokenRefreshInterval, 6e4);
62
+ let tokenRefreshInterval = null;
63
+ watch(session, async (newVal) => {
64
+ if (tokenRefreshInterval) {
65
+ clearTimeout(tokenRefreshInterval);
66
+ tokenRefreshInterval = null;
67
+ }
68
+ if (newVal) {
69
+ async function createTokenRefreshInterval() {
70
+ const token = await getToken();
71
+ if (token) {
72
+ tokenCookie.value = token;
89
73
  }
90
- createTokenRefreshInterval();
74
+ tokenRefreshInterval = window.setTimeout(createTokenRefreshInterval, 6e4);
91
75
  }
92
- }, { immediate: true });
93
- }
94
- }
95
- return {
96
- provide: {
97
- auth: {
98
- client: authClient,
99
- getToken,
100
- refreshToken
76
+ createTokenRefreshInterval();
101
77
  }
102
- }
103
- };
78
+ }, { immediate: true });
79
+ }
80
+ },
81
+ hooks: {
82
+ "app:mounted": () => {
83
+ const url = new URL(window.location.href);
84
+ url.searchParams.delete("session");
85
+ url.searchParams.delete("session_data");
86
+ url.searchParams.delete("nonce");
87
+ window.history.replaceState({}, "", url.pathname + url.search + url.hash);
88
+ }
104
89
  }
105
90
  });
@@ -1,26 +1,21 @@
1
+ import { validateToken, extractTokenPayload } from "@meistrari/auth-core";
1
2
  import { defineEventHandler, getCookie } from "h3";
2
3
  import { useRuntimeConfig } from "nitropack/runtime/config";
3
- import { createAuthClient } from "@meistrari/auth-core";
4
4
  async function setAuthContext(event) {
5
5
  event.context.auth = {
6
- session: null,
7
- user: null
6
+ user: null,
7
+ workspace: null
8
8
  };
9
- const sessionCookie = getCookie(event, "__Secure-__session");
10
- if (!sessionCookie)
9
+ const authConfig = useRuntimeConfig(event).public.telaAuth;
10
+ const tokenCookie = getCookie(event, authConfig.jwtCookieName);
11
+ if (!tokenCookie)
11
12
  return;
12
- const authConfig = useRuntimeConfig(event).public.auth;
13
- const authClient = createAuthClient(authConfig.apiUrl);
14
- const { data: sessionData, error: sessionError } = await authClient.getSession({
15
- fetchOptions: {
16
- headers: event.headers
17
- }
18
- });
19
- if (sessionError || !sessionData) {
13
+ const isValid = await validateToken(tokenCookie, authConfig.apiUrl);
14
+ if (!isValid)
20
15
  return;
21
- }
22
- event.context.auth.session = sessionData.session;
23
- event.context.auth.user = sessionData.user;
16
+ const payload = extractTokenPayload(tokenCookie);
17
+ event.context.auth.user = { email: payload.email, ...payload.user };
18
+ event.context.auth.workspace = payload.workspace;
24
19
  }
25
20
  export function meistrariAuthMiddleware(callback) {
26
21
  return defineEventHandler(async (event) => {
@@ -1,3 +1,6 @@
1
1
  {
2
- "extends": "../../../.nuxt/tsconfig.server.json",
2
+ "extends": [
3
+ "../../../../../../node_modules/@meistrari/mise-en-place/tsconfig.base.json",
4
+ "../../../.nuxt/tsconfig.server.json"
5
+ ],
3
6
  }
@@ -1,22 +1,22 @@
1
- import type { User, Session } from '@meistrari/auth-core'
1
+ import type { JWTTokenPayload } from '@meistrari/auth-core'
2
2
  import type { H3Event, H3EventContext } from 'h3'
3
3
 
4
4
  declare module 'h3' {
5
- interface H3EventContext {
6
- auth?: {
7
- user: User | null
8
- session: Session | null
5
+ interface H3EventContext {
6
+ auth?: {
7
+ user: { email: string } & JWTTokenPayload['user'] | null
8
+ workspace: JWTTokenPayload['workspace'] | null
9
+ }
9
10
  }
10
- }
11
11
  }
12
12
 
13
13
  export interface AuthenticatedH3EventContext extends H3EventContext {
14
- auth: {
15
- user: User | null
16
- session: Session | null
17
- }
14
+ auth: {
15
+ user: { email: string } & JWTTokenPayload['user'] | null
16
+ workspace: JWTTokenPayload['workspace'] | null
17
+ }
18
18
  }
19
19
 
20
20
  export interface AuthenticatedH3Event extends H3Event {
21
- context: AuthenticatedH3EventContext
22
- }
21
+ context: AuthenticatedH3EventContext
22
+ }
@@ -0,0 +1 @@
1
+ export declare function createNuxtAuthClient(apiUrl: string, getAuthToken: () => string | null): any;
@@ -0,0 +1,19 @@
1
+ import { AuthClient } from "@meistrari/auth-core";
2
+ const version = "__PACKAGE_VERSION__";
3
+ export function createNuxtAuthClient(apiUrl, getAuthToken) {
4
+ const serviceName = typeof process !== "undefined" ? process.env.SERVICE_NAME : "";
5
+ const userAgent = `auth-sdk:nuxt:${version}${serviceName ? `@${serviceName}` : ""}`;
6
+ return new AuthClient(apiUrl, {
7
+ headers: {
8
+ "X-User-Agent": userAgent
9
+ },
10
+ onRequest: (context) => {
11
+ const requestUrl = new URL(context.url);
12
+ const token = getAuthToken();
13
+ const isTokenRequest = requestUrl.pathname.endsWith("/api/auth/token");
14
+ if (token && !isTokenRequest) {
15
+ context.headers.set("Authorization", `Bearer ${token}`);
16
+ }
17
+ }
18
+ });
19
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/auth-nuxt",
3
- "version": "0.2.0",
3
+ "version": "1.0.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": "0.1.1"
34
+ "@meistrari/auth-core": "1.4.0"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "nuxt": "^3.0.0 || ^4.0.0"
@@ -45,11 +45,10 @@
45
45
  "@nuxt/test-utils": "3.19.2",
46
46
  "@types/node": "latest",
47
47
  "changelogen": "0.6.2",
48
- "eslint": "9.34.0",
49
48
  "nuxt": "4.0.3",
50
49
  "typescript": "5.9.2",
51
50
  "unbuild": "3.6.1",
52
51
  "vitest": "3.2.4",
53
52
  "vue-tsc": "3.0.6"
54
53
  }
55
- }
54
+ }
@@ -1,5 +0,0 @@
1
- export declare function useMeistrariAuth(): {
2
- user: import("vue").Ref<any, any>;
3
- session: import("vue").Ref<any, any>;
4
- signOut: (callback?: () => void) => Promise<void>;
5
- };
@@ -1,29 +0,0 @@
1
- import { useCookie, useState, useRuntimeConfig, useNuxtApp } from "#app";
2
- export function useMeistrariAuth() {
3
- const user = useState("user", () => null);
4
- const session = useState("session", () => null);
5
- const { useJwt, jwtCookieName, isDevelopment } = useRuntimeConfig().public.auth;
6
- const { $auth } = useNuxtApp();
7
- async function signOut(callback) {
8
- await $auth.client.signOut({
9
- fetchOptions: {
10
- onSuccess: async () => {
11
- if (useJwt) {
12
- useCookie(jwtCookieName).value = null;
13
- }
14
- if (isDevelopment) {
15
- await $fetch("/api/meistrari-auth/sign-out");
16
- }
17
- user.value = null;
18
- session.value = null;
19
- callback?.();
20
- }
21
- }
22
- });
23
- }
24
- return {
25
- user,
26
- session,
27
- signOut
28
- };
29
- }
@@ -1,2 +0,0 @@
1
- declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
2
- export default _default;
@@ -1,16 +0,0 @@
1
- import { defineEventHandler, deleteCookie, sendNoContent } from "h3";
2
- export default defineEventHandler(async (event) => {
3
- deleteCookie(event, "__Secure-__session", {
4
- httpOnly: true,
5
- secure: true,
6
- sameSite: "lax",
7
- path: "/"
8
- });
9
- deleteCookie(event, "__Secure-__session_data", {
10
- httpOnly: true,
11
- secure: true,
12
- sameSite: "lax",
13
- path: "/"
14
- });
15
- return sendNoContent(event);
16
- });
@@ -1,2 +0,0 @@
1
- declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
2
- export default _default;
@@ -1,42 +0,0 @@
1
- import { defineEventHandler, getQuery, setCookie, sendRedirect, getRequestURL } from "h3";
2
- export default defineEventHandler(async (event) => {
3
- const query = getQuery(event);
4
- const hasSessionParam = query.session && typeof query.session === "string";
5
- const hasSessionDataParam = query.session_data && typeof query.session_data === "string";
6
- if (hasSessionParam || hasSessionDataParam) {
7
- if (hasSessionParam) {
8
- setCookie(event, "__Secure-__session", query.session, {
9
- httpOnly: true,
10
- secure: true,
11
- sameSite: "lax",
12
- path: "/",
13
- maxAge: 60 * 60 * 24 * 7
14
- // 1 week in seconds
15
- });
16
- }
17
- if (hasSessionDataParam) {
18
- setCookie(event, "__Secure-__session_data", query.session_data, {
19
- httpOnly: true,
20
- secure: true,
21
- sameSite: "lax",
22
- path: "/",
23
- maxAge: 60 * 60 * 24 * 7
24
- // 1 week in seconds
25
- });
26
- }
27
- const url = getRequestURL(event);
28
- const newQuery = new URLSearchParams();
29
- for (const [key, value] of Object.entries(query)) {
30
- if (key !== "session" && key !== "session_data" && value !== void 0 && value !== null) {
31
- if (Array.isArray(value)) {
32
- value.forEach((v) => newQuery.append(key, String(v)));
33
- } else {
34
- newQuery.append(key, String(value));
35
- }
36
- }
37
- }
38
- const queryString = newQuery.toString();
39
- const redirectUrl = `${url.pathname}${queryString ? `?${queryString}` : ""}`;
40
- return sendRedirect(event, redirectUrl, 302);
41
- }
42
- });