nuxt-umbu 0.0.1
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/LICENSE +21 -0
- package/README.md +21 -0
- package/dist/module.d.mts +22 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +114 -0
- package/dist/runtime/composables/autx.d.ts +3 -0
- package/dist/runtime/composables/autx.js +62 -0
- package/dist/runtime/composables/useConfig.d.ts +4 -0
- package/dist/runtime/composables/useConfig.js +4 -0
- package/dist/runtime/composables/useEnsureCsrf.d.ts +2 -0
- package/dist/runtime/composables/useEnsureCsrf.js +11 -0
- package/dist/runtime/composables/useStates.d.ts +2 -0
- package/dist/runtime/composables/useStates.js +6 -0
- package/dist/runtime/passport/middleware/2fa.d.ts +2 -0
- package/dist/runtime/passport/middleware/2fa.js +46 -0
- package/dist/runtime/passport/middleware/auth.d.ts +2 -0
- package/dist/runtime/passport/middleware/auth.js +46 -0
- package/dist/runtime/passport/plugin.d.ts +2 -0
- package/dist/runtime/passport/plugin.js +282 -0
- package/dist/runtime/passport/server/api/2fa.d.ts +5 -0
- package/dist/runtime/passport/server/api/2fa.js +78 -0
- package/dist/runtime/passport/server/api/login.d.ts +6 -0
- package/dist/runtime/passport/server/api/login.js +63 -0
- package/dist/runtime/passport/server/api/logout.d.ts +10 -0
- package/dist/runtime/passport/server/api/logout.js +37 -0
- package/dist/runtime/passport/server/api/refresh.d.ts +6 -0
- package/dist/runtime/passport/server/api/refresh.js +72 -0
- package/dist/runtime/passport/server/api/session.d.ts +7 -0
- package/dist/runtime/passport/server/api/session.js +16 -0
- package/dist/runtime/passport/server/middleware/protected.d.ts +2 -0
- package/dist/runtime/passport/server/middleware/protected.js +17 -0
- package/dist/runtime/passport/server/utils/index.d.ts +14 -0
- package/dist/runtime/passport/server/utils/index.js +14 -0
- package/dist/runtime/passport/utils/index.d.ts +1 -0
- package/dist/runtime/passport/utils/index.js +1 -0
- package/dist/runtime/passport/utils/middleware.d.ts +22 -0
- package/dist/runtime/passport/utils/middleware.js +36 -0
- package/dist/runtime/sanctum/middleware/2fa.d.ts +2 -0
- package/dist/runtime/sanctum/middleware/2fa.js +37 -0
- package/dist/runtime/sanctum/middleware/auth.d.ts +2 -0
- package/dist/runtime/sanctum/middleware/auth.js +43 -0
- package/dist/runtime/sanctum/plugin.d.ts +2 -0
- package/dist/runtime/sanctum/plugin.js +272 -0
- package/dist/runtime/sanctum/utils/index.d.ts +1 -0
- package/dist/runtime/sanctum/utils/index.js +1 -0
- package/dist/runtime/sanctum/utils/middleware.d.ts +32 -0
- package/dist/runtime/sanctum/utils/middleware.js +48 -0
- package/dist/runtime/types/core/auth-instance.d.ts +1 -0
- package/dist/runtime/types/core/auth-instance.js +0 -0
- package/dist/runtime/types/core/plugin.d.ts +0 -0
- package/dist/runtime/types/core/state.d.ts +5 -0
- package/dist/runtime/types/core/state.js +0 -0
- package/dist/runtime/types/index.d.ts +28 -0
- package/dist/runtime/types/index.js +4 -0
- package/dist/runtime/types/providers/passport.d.ts +50 -0
- package/dist/runtime/types/providers/passport.js +0 -0
- package/dist/runtime/types/providers/sanctum.d.ts +29 -0
- package/dist/runtime/types/providers/sanctum.js +0 -0
- package/dist/runtime/types/shared.d.ts +19 -0
- package/dist/runtime/types/shared.js +0 -0
- package/dist/types.d.mts +7 -0
- package/package.json +74 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineNuxtPlugin,
|
|
3
|
+
useRequestEvent,
|
|
4
|
+
navigateTo,
|
|
5
|
+
useAuthStore,
|
|
6
|
+
useAuthConfig,
|
|
7
|
+
useRuntimeConfig,
|
|
8
|
+
createError
|
|
9
|
+
} from "#imports";
|
|
10
|
+
import { parseCookies, setCookie } from "h3";
|
|
11
|
+
import { $fetch } from "ofetch";
|
|
12
|
+
export default defineNuxtPlugin(async (nuxtApp) => {
|
|
13
|
+
const store = useAuthStore();
|
|
14
|
+
class Auth {
|
|
15
|
+
$headers;
|
|
16
|
+
_state = { user: null, loggedIn: false, strategy: "" };
|
|
17
|
+
_prefix;
|
|
18
|
+
options;
|
|
19
|
+
constructor(options) {
|
|
20
|
+
this.$headers = new Headers();
|
|
21
|
+
this._prefix = options.cookie.prefix;
|
|
22
|
+
this.options = options;
|
|
23
|
+
}
|
|
24
|
+
get state() {
|
|
25
|
+
return this._state;
|
|
26
|
+
}
|
|
27
|
+
get user() {
|
|
28
|
+
return this._state.user;
|
|
29
|
+
}
|
|
30
|
+
get strategy() {
|
|
31
|
+
return this._state.strategy;
|
|
32
|
+
}
|
|
33
|
+
get loggedIn() {
|
|
34
|
+
return this._state.loggedIn;
|
|
35
|
+
}
|
|
36
|
+
get headers() {
|
|
37
|
+
return this.$headers;
|
|
38
|
+
}
|
|
39
|
+
get prefix() {
|
|
40
|
+
return this._prefix;
|
|
41
|
+
}
|
|
42
|
+
set headers(headers) {
|
|
43
|
+
this.$headers = headers;
|
|
44
|
+
}
|
|
45
|
+
set state(val) {
|
|
46
|
+
this._state = val;
|
|
47
|
+
}
|
|
48
|
+
getRedirect(strategyName) {
|
|
49
|
+
return this.options.strategies?.[strategyName]?.redirect ?? null;
|
|
50
|
+
}
|
|
51
|
+
getUserProperty(strategyName) {
|
|
52
|
+
return this.options.strategies?.[strategyName]?.user?.property ?? null;
|
|
53
|
+
}
|
|
54
|
+
getEndpointsUser(strategyName) {
|
|
55
|
+
return this.options.strategies?.[strategyName]?.endpoints.user ?? null;
|
|
56
|
+
}
|
|
57
|
+
getHandler(strategyName, key) {
|
|
58
|
+
return this.options.strategies?.[strategyName]?.handler?.find((value) => value[key])?.[key] ?? null;
|
|
59
|
+
}
|
|
60
|
+
hasValidProperty(obj, key) {
|
|
61
|
+
if (!obj || !key) return false;
|
|
62
|
+
const value = obj[key];
|
|
63
|
+
return value !== null && value !== void 0;
|
|
64
|
+
}
|
|
65
|
+
async initialize() {
|
|
66
|
+
try {
|
|
67
|
+
let strategy = null;
|
|
68
|
+
let token = null;
|
|
69
|
+
if (import.meta.server) {
|
|
70
|
+
const event2 = useRequestEvent();
|
|
71
|
+
if (!event2) {
|
|
72
|
+
console.warn("No request event available. Skipping initialization.");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const cookies = parseCookies(event2);
|
|
76
|
+
strategy = cookies[this._prefix + `strategy`];
|
|
77
|
+
token = strategy ? cookies[this._prefix + `_token.` + strategy] : null;
|
|
78
|
+
} else {
|
|
79
|
+
strategy = localStorage.getItem(this._prefix + `strategy`);
|
|
80
|
+
token = strategy ? localStorage.getItem(this._prefix + `_token.` + strategy) : null;
|
|
81
|
+
}
|
|
82
|
+
if (!strategy || !token) {
|
|
83
|
+
console.warn("No valid session found. Skipping profile fetch.");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
this._state.strategy = strategy ?? null;
|
|
87
|
+
this.$headers.set("Authorization", token);
|
|
88
|
+
const data = await this._setProfile();
|
|
89
|
+
if (data) {
|
|
90
|
+
const property = this.getUserProperty(this._state.strategy);
|
|
91
|
+
const user = this.hasValidProperty(data, property) ? data[property] : data ?? null;
|
|
92
|
+
this._state = {
|
|
93
|
+
user,
|
|
94
|
+
loggedIn: true,
|
|
95
|
+
strategy: strategy ?? null
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error("Failed to initialize auth:", error);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async loginWith(strategyName, value) {
|
|
103
|
+
try {
|
|
104
|
+
const loginUrl = this.getHandler(strategyName, "login");
|
|
105
|
+
if (!loginUrl) throw new Error("Login endpoint not found");
|
|
106
|
+
const response = await $fetch(loginUrl, {
|
|
107
|
+
method: "POST",
|
|
108
|
+
body: { strategyName, value }
|
|
109
|
+
});
|
|
110
|
+
if (!response.token) throw new Error("Token is missing in the response");
|
|
111
|
+
localStorage.setItem(this._prefix + `_token.` + strategyName, response.token);
|
|
112
|
+
localStorage.setItem(this._prefix + `strategy`, strategyName);
|
|
113
|
+
localStorage.setItem(this._prefix + `_token_expiration.` + strategyName, response.expires);
|
|
114
|
+
this._state.strategy = strategyName ?? null;
|
|
115
|
+
this.$headers.set("Authorization", response.token);
|
|
116
|
+
const data = await this._setProfile();
|
|
117
|
+
if (!data) {
|
|
118
|
+
throw new Error("Failed to load user profile.");
|
|
119
|
+
}
|
|
120
|
+
const redirectUrl = this.getRedirect(strategyName)?.login;
|
|
121
|
+
if (redirectUrl) {
|
|
122
|
+
await navigateTo(redirectUrl);
|
|
123
|
+
}
|
|
124
|
+
return response ?? Promise.reject("No data returned");
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error("Login failed:", error);
|
|
127
|
+
return Promise.reject(error);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async logout(strategyName) {
|
|
131
|
+
try {
|
|
132
|
+
const logoutUrl = this.getHandler(strategyName, "logout");
|
|
133
|
+
if (!logoutUrl) throw new Error("Logout endpoint not found");
|
|
134
|
+
const response = await $fetch(logoutUrl, {
|
|
135
|
+
method: "POST",
|
|
136
|
+
body: { strategyName }
|
|
137
|
+
});
|
|
138
|
+
this._state = {
|
|
139
|
+
user: null,
|
|
140
|
+
loggedIn: false,
|
|
141
|
+
strategy: ""
|
|
142
|
+
};
|
|
143
|
+
store.value = this._state;
|
|
144
|
+
Object.keys(localStorage).filter((key) => key.startsWith(this._prefix)).forEach((key) => localStorage.removeItem(key));
|
|
145
|
+
const redirectUrl = this.getRedirect(strategyName)?.logout ?? "/";
|
|
146
|
+
await navigateTo(redirectUrl);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error("Logout failed:", error);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async _2fa(strategyName, code) {
|
|
152
|
+
try {
|
|
153
|
+
if (!code) {
|
|
154
|
+
throw new Error("2FA code is required");
|
|
155
|
+
}
|
|
156
|
+
const twoFaUrl = this.getHandler(strategyName, "2fa");
|
|
157
|
+
if (!twoFaUrl) {
|
|
158
|
+
throw new Error("2FA endpoint not found");
|
|
159
|
+
}
|
|
160
|
+
const response = await $fetch(twoFaUrl, {
|
|
161
|
+
method: "POST",
|
|
162
|
+
body: { strategyName, code }
|
|
163
|
+
});
|
|
164
|
+
if (!response?.token || !response?.expires) {
|
|
165
|
+
throw new Error("Invalid 2FA response");
|
|
166
|
+
}
|
|
167
|
+
if (import.meta.client) {
|
|
168
|
+
localStorage.setItem(this._prefix + "_2fa." + strategyName, response.token);
|
|
169
|
+
localStorage.setItem(this._prefix + "_2fa_expiration." + strategyName, response.expires);
|
|
170
|
+
}
|
|
171
|
+
this.$headers.set("2fa", response.token);
|
|
172
|
+
return { success: true };
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.error("2FA failed:", error);
|
|
175
|
+
return Promise.reject(error);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async refreshToken(strategyName) {
|
|
179
|
+
return new Promise((resolve, reject) => {
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
async csrfToken(event2) {
|
|
183
|
+
try {
|
|
184
|
+
const baseURL = useRuntimeConfig().public.baseURL;
|
|
185
|
+
const csrfEndpoint = this.options?.csrf;
|
|
186
|
+
if (!csrfEndpoint) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
const data = await $fetch(csrfEndpoint, { baseURL });
|
|
190
|
+
if (!data?.csrf_token) {
|
|
191
|
+
throw new Error("Invalid CSRF response: Missing token.");
|
|
192
|
+
}
|
|
193
|
+
this.$headers.set("X-CSRF-TOKEN", data.csrf_token);
|
|
194
|
+
if (import.meta.server && event2) {
|
|
195
|
+
const cookies = parseCookies(event2);
|
|
196
|
+
const csrfCookie = cookies["X-CSRF-TOKEN"];
|
|
197
|
+
if (!csrfCookie) {
|
|
198
|
+
setCookie(
|
|
199
|
+
event2,
|
|
200
|
+
"X-CSRF-TOKEN",
|
|
201
|
+
data.csrf_token,
|
|
202
|
+
this.options.cookie.options || {
|
|
203
|
+
httpOnly: true,
|
|
204
|
+
secure: true,
|
|
205
|
+
sameSite: "strict",
|
|
206
|
+
path: "/"
|
|
207
|
+
}
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return true;
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.error("Error fetching CSRF token:", error instanceof Error ? error.message : error);
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async _setProfile() {
|
|
218
|
+
try {
|
|
219
|
+
const baseURL = useRuntimeConfig().public.baseURL;
|
|
220
|
+
const endpoint = this.getEndpointsUser(this._state.strategy);
|
|
221
|
+
if (!endpoint?.url || !endpoint?.method) return false;
|
|
222
|
+
const headers = this.$headers instanceof Headers ? Object.fromEntries(this.$headers.entries()) : this.$headers;
|
|
223
|
+
const data = await $fetch(endpoint.url, {
|
|
224
|
+
baseURL,
|
|
225
|
+
method: endpoint.method,
|
|
226
|
+
headers: {
|
|
227
|
+
...headers,
|
|
228
|
+
"Content-Type": "application/json"
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
if (!data) return false;
|
|
232
|
+
const property = this.getUserProperty(this._state.strategy);
|
|
233
|
+
const user = this.hasValidProperty(data, property) ? data[property] : data ?? null;
|
|
234
|
+
store.value = {
|
|
235
|
+
user,
|
|
236
|
+
strategy: this._state.strategy ?? null,
|
|
237
|
+
loggedIn: true
|
|
238
|
+
};
|
|
239
|
+
this._state = store.value;
|
|
240
|
+
return data;
|
|
241
|
+
} catch (error) {
|
|
242
|
+
throw createError({
|
|
243
|
+
statusCode: error.statusCode,
|
|
244
|
+
statusMessage: "Access denied"
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const $auth = new Auth(useAuthConfig());
|
|
250
|
+
const event = import.meta.server ? useRequestEvent() : void 0;
|
|
251
|
+
await $auth.csrfToken(event);
|
|
252
|
+
await $auth.initialize();
|
|
253
|
+
const exposed = Object.defineProperties(
|
|
254
|
+
{},
|
|
255
|
+
{
|
|
256
|
+
state: { get: () => $auth.state },
|
|
257
|
+
user: { get: () => $auth.user },
|
|
258
|
+
strategy: { get: () => $auth.strategy },
|
|
259
|
+
loggedIn: { get: () => $auth.loggedIn },
|
|
260
|
+
headers: {
|
|
261
|
+
get: () => $auth.headers,
|
|
262
|
+
set: (headers) => {
|
|
263
|
+
$auth.headers = headers;
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
prefix: { get: () => $auth.prefix }
|
|
267
|
+
}
|
|
268
|
+
);
|
|
269
|
+
exposed.getRedirect = (strategyName) => {
|
|
270
|
+
return $auth.getRedirect?.(strategyName) ?? null;
|
|
271
|
+
};
|
|
272
|
+
exposed.loginWith = async (strategyName, value) => {
|
|
273
|
+
return await $auth.loginWith(strategyName, value);
|
|
274
|
+
};
|
|
275
|
+
exposed.logout = async (strategyName) => {
|
|
276
|
+
return await $auth.logout(strategyName);
|
|
277
|
+
};
|
|
278
|
+
exposed._2fa = async (strategyName, code) => {
|
|
279
|
+
return await $auth._2fa(strategyName, code);
|
|
280
|
+
};
|
|
281
|
+
nuxtApp.provide("auth", exposed);
|
|
282
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { useRuntimeConfig } from "#imports";
|
|
2
|
+
import { defineEventHandler, readBody, getCookie, setCookie, createError, setHeader } from "h3";
|
|
3
|
+
import { $fetch } from "ofetch";
|
|
4
|
+
import protectedMiddleware from "../middleware/protected.js";
|
|
5
|
+
export default defineEventHandler(async (event) => {
|
|
6
|
+
try {
|
|
7
|
+
await protectedMiddleware(event);
|
|
8
|
+
const body = await readBody(event);
|
|
9
|
+
if (!body?.strategyName || !body?.code) {
|
|
10
|
+
throw createError({
|
|
11
|
+
statusCode: 400,
|
|
12
|
+
statusMessage: "Missing required parameters: strategyName or code"
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
const runtimeConfig = useRuntimeConfig();
|
|
16
|
+
const baseURL = runtimeConfig.public.baseURL;
|
|
17
|
+
const config = runtimeConfig.public["nuxt-umbu"];
|
|
18
|
+
if (!config) {
|
|
19
|
+
throw createError({ statusCode: 500, statusMessage: "Authentication module not configured" });
|
|
20
|
+
}
|
|
21
|
+
const { cookie, strategies } = config;
|
|
22
|
+
const prefix = cookie?.prefix || "auth.";
|
|
23
|
+
const strategy = strategies?.[body.strategyName];
|
|
24
|
+
if (!strategy) {
|
|
25
|
+
throw createError({ statusCode: 400, statusMessage: "Invalid authentication strategy" });
|
|
26
|
+
}
|
|
27
|
+
const { endpoints } = strategy;
|
|
28
|
+
if (!endpoints?.["2fa"]) {
|
|
29
|
+
throw createError({
|
|
30
|
+
statusCode: 400,
|
|
31
|
+
statusMessage: "2FA endpoint not configured for strategy " + body.strategyName
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
const token = getCookie(event, prefix + "_token." + body.strategyName) || "";
|
|
35
|
+
const response = await $fetch(endpoints["2fa"].url, {
|
|
36
|
+
baseURL,
|
|
37
|
+
method: endpoints["2fa"].method || "POST",
|
|
38
|
+
body: { code: body.code },
|
|
39
|
+
headers: {
|
|
40
|
+
"Content-Type": "application/json",
|
|
41
|
+
Authorization: token
|
|
42
|
+
},
|
|
43
|
+
onRequest({ options }) {
|
|
44
|
+
options.headers = options.headers || {};
|
|
45
|
+
setHeader(event, "Authorization", token);
|
|
46
|
+
}
|
|
47
|
+
}).catch((error) => {
|
|
48
|
+
console.error("[API Error]", error);
|
|
49
|
+
throw createError({ statusCode: 401, statusMessage: "Authentication processing failed" });
|
|
50
|
+
});
|
|
51
|
+
if (!response?.access_token || !response?.expires_in) {
|
|
52
|
+
throw createError({
|
|
53
|
+
statusCode: 500,
|
|
54
|
+
statusMessage: "Invalid 2FA response structure"
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
const expires = Date.now() + response.expires_in * 1e3;
|
|
58
|
+
setCookie(
|
|
59
|
+
event,
|
|
60
|
+
prefix + "_2fa." + body.strategyName,
|
|
61
|
+
response.access_token,
|
|
62
|
+
cookie?.options || {}
|
|
63
|
+
);
|
|
64
|
+
setCookie(
|
|
65
|
+
event,
|
|
66
|
+
prefix + "_2fa_expiration." + body.strategyName,
|
|
67
|
+
expires.toString(),
|
|
68
|
+
cookie?.options || {}
|
|
69
|
+
);
|
|
70
|
+
return { token: response.access_token, expires };
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error("[Auth Error]", error);
|
|
73
|
+
throw createError({
|
|
74
|
+
statusCode: error.statusCode || 500,
|
|
75
|
+
statusMessage: error.statusMessage || "Failed to fetch 2FA token"
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { useRuntimeConfig } from "#imports";
|
|
2
|
+
import { defineEventHandler, readBody, setCookie, createError } from "h3";
|
|
3
|
+
import { defu } from "defu";
|
|
4
|
+
import { $fetch } from "ofetch";
|
|
5
|
+
export default defineEventHandler(async (event) => {
|
|
6
|
+
try {
|
|
7
|
+
const body = await readBody(event);
|
|
8
|
+
if (!body?.strategyName || !body?.value) {
|
|
9
|
+
throw createError({ statusCode: 400, statusMessage: "Invalid request body" });
|
|
10
|
+
}
|
|
11
|
+
const runtimeConfig = useRuntimeConfig();
|
|
12
|
+
const baseURL = runtimeConfig.public.baseURL;
|
|
13
|
+
const config = runtimeConfig.public["nuxt-umbu"];
|
|
14
|
+
const secret = runtimeConfig.secret;
|
|
15
|
+
if (!config) {
|
|
16
|
+
throw createError({ statusCode: 500, statusMessage: "Authentication module not configured" });
|
|
17
|
+
}
|
|
18
|
+
const { cookie, strategies } = config;
|
|
19
|
+
const prefix = cookie?.prefix || "auth.";
|
|
20
|
+
const strategy = strategies?.[body.strategyName];
|
|
21
|
+
if (!strategy) {
|
|
22
|
+
throw createError({ statusCode: 400, statusMessage: "Invalid authentication strategy" });
|
|
23
|
+
}
|
|
24
|
+
const { endpoints } = strategy;
|
|
25
|
+
if (!endpoints?.login?.url) {
|
|
26
|
+
throw createError({ statusCode: 500, statusMessage: "Login endpoint not configured" });
|
|
27
|
+
}
|
|
28
|
+
const credentials = defu(body.value, secret?.[body.strategyName]);
|
|
29
|
+
const response = await $fetch(endpoints.login.url, {
|
|
30
|
+
baseURL,
|
|
31
|
+
method: endpoints.login.method || "POST",
|
|
32
|
+
body: credentials,
|
|
33
|
+
timeout: 1e4,
|
|
34
|
+
headers: {
|
|
35
|
+
"Content-Type": "application/json",
|
|
36
|
+
Accept: "application/json"
|
|
37
|
+
}
|
|
38
|
+
}).catch((error) => {
|
|
39
|
+
console.error("[API Error]", error);
|
|
40
|
+
throw createError({ statusCode: 401, statusMessage: "Authentication processing failed" });
|
|
41
|
+
});
|
|
42
|
+
if (!response?.access_token || !response?.refresh_token || !response?.expires_in) {
|
|
43
|
+
throw createError({ statusCode: 502, statusMessage: "Invalid token response" });
|
|
44
|
+
}
|
|
45
|
+
const token = "Bearer " + response.access_token;
|
|
46
|
+
const expires = Date.now() + response.expires_in * 1e3;
|
|
47
|
+
setCookie(event, prefix + "_token." + body.strategyName, token, cookie?.options || {});
|
|
48
|
+
setCookie(event, prefix + "strategy", body.strategyName, cookie?.options || {});
|
|
49
|
+
setCookie(
|
|
50
|
+
event,
|
|
51
|
+
prefix + "_token_expiration." + body.strategyName,
|
|
52
|
+
expires.toString(),
|
|
53
|
+
cookie?.options || {}
|
|
54
|
+
);
|
|
55
|
+
return { token, refresh_token: response.refresh_token, expires };
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error("[Auth Error]", error);
|
|
58
|
+
throw createError({
|
|
59
|
+
statusCode: error.statusCode || 500,
|
|
60
|
+
statusMessage: error.statusMessage || "Authentication failed"
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
|
|
2
|
+
success: boolean;
|
|
3
|
+
redirect: import("../../../types/index.js").RedirectOptions;
|
|
4
|
+
error?: undefined;
|
|
5
|
+
} | {
|
|
6
|
+
success: boolean;
|
|
7
|
+
error: string;
|
|
8
|
+
redirect?: undefined;
|
|
9
|
+
}>>;
|
|
10
|
+
export default _default;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useRuntimeConfig } from "#imports";
|
|
2
|
+
import { deleteCookie, defineEventHandler, readBody } from "h3";
|
|
3
|
+
import protectedMiddleware from "../middleware/protected.js";
|
|
4
|
+
export default defineEventHandler(async (event) => {
|
|
5
|
+
try {
|
|
6
|
+
await protectedMiddleware(event);
|
|
7
|
+
const body = await readBody(event);
|
|
8
|
+
const strategyName = body?.strategyName;
|
|
9
|
+
if (!strategyName) {
|
|
10
|
+
throw new Error("Strategy name is required.");
|
|
11
|
+
}
|
|
12
|
+
const runtimeConfig = useRuntimeConfig();
|
|
13
|
+
const config = runtimeConfig.public["nuxt-umbu"];
|
|
14
|
+
if (!config) {
|
|
15
|
+
throw new Error("Auth configuration is missing.");
|
|
16
|
+
}
|
|
17
|
+
const { cookie, strategies, twoFactorAuth } = config;
|
|
18
|
+
const prefix = (cookie?.prefix && !import.meta.dev ? cookie.prefix : "auth.") + "";
|
|
19
|
+
const strategyConfig = strategies[strategyName];
|
|
20
|
+
if (!strategyConfig) {
|
|
21
|
+
throw new Error("Strategy " + strategyName + " is not defined in the configuration.");
|
|
22
|
+
}
|
|
23
|
+
const { redirect } = strategyConfig;
|
|
24
|
+
const cookieOptions = cookie?.options ?? {};
|
|
25
|
+
deleteCookie(event, prefix + "_token." + strategyName, cookieOptions);
|
|
26
|
+
deleteCookie(event, prefix + "strategy", cookieOptions);
|
|
27
|
+
deleteCookie(event, prefix + "_token_expiration." + strategyName, cookieOptions);
|
|
28
|
+
if (twoFactorAuth) {
|
|
29
|
+
deleteCookie(event, prefix + "_2fa." + strategyName, cookieOptions);
|
|
30
|
+
deleteCookie(event, prefix + "_2fa_expiration." + strategyName, cookieOptions);
|
|
31
|
+
}
|
|
32
|
+
return { success: true, redirect };
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error("Error in logout handler:", error);
|
|
35
|
+
return { success: false, error: error.message };
|
|
36
|
+
}
|
|
37
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { useRuntimeConfig } from "#imports";
|
|
2
|
+
import { defineEventHandler, readBody, setCookie, createError, getCookie } from "h3";
|
|
3
|
+
import { $fetch } from "ofetch";
|
|
4
|
+
import protectedMiddleware from "../middleware/protected.js";
|
|
5
|
+
export default defineEventHandler(async (event) => {
|
|
6
|
+
try {
|
|
7
|
+
await protectedMiddleware(event);
|
|
8
|
+
const body = await readBody(event);
|
|
9
|
+
if (!body?.strategyName) {
|
|
10
|
+
throw createError({ statusCode: 400, statusMessage: "Invalid request body" });
|
|
11
|
+
}
|
|
12
|
+
const runtimeConfig = useRuntimeConfig();
|
|
13
|
+
const baseURL = runtimeConfig.public.baseURL;
|
|
14
|
+
const config = runtimeConfig.public["nuxt-umbu"];
|
|
15
|
+
if (!config) {
|
|
16
|
+
throw createError({ statusCode: 500, statusMessage: "Authentication module not configured" });
|
|
17
|
+
}
|
|
18
|
+
const { cookie, strategies } = config;
|
|
19
|
+
const prefix = cookie?.prefix || "auth.";
|
|
20
|
+
const strategy = strategies?.[body.strategyName];
|
|
21
|
+
if (!strategy) {
|
|
22
|
+
throw createError({ statusCode: 400, statusMessage: "Invalid authentication strategy" });
|
|
23
|
+
}
|
|
24
|
+
const { endpoints } = strategy;
|
|
25
|
+
if (!endpoints?.refresh?.url) {
|
|
26
|
+
throw createError({ statusCode: 500, statusMessage: "Refresh endpoint not configured" });
|
|
27
|
+
}
|
|
28
|
+
const refreshToken = getCookie(event, prefix + `_refresh_token.` + body.strategyName);
|
|
29
|
+
if (!refreshToken) {
|
|
30
|
+
throw createError({
|
|
31
|
+
statusCode: 401,
|
|
32
|
+
statusMessage: "No refresh token found. Please log in again."
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const response = await $fetch(endpoints.refresh.url, {
|
|
36
|
+
baseURL,
|
|
37
|
+
method: endpoints.refresh.method || "POST",
|
|
38
|
+
body: { refresh_token: refreshToken },
|
|
39
|
+
timeout: 1e4,
|
|
40
|
+
headers: { "Content-Type": "application/json" }
|
|
41
|
+
}).catch((error) => {
|
|
42
|
+
console.error("[API Error]", error);
|
|
43
|
+
throw createError({ statusCode: 502, statusMessage: "Authentication service error" });
|
|
44
|
+
});
|
|
45
|
+
if (!response?.access_token || !response?.refresh_token || !response?.expires_in) {
|
|
46
|
+
throw createError({ statusCode: 502, statusMessage: "Invalid token response" });
|
|
47
|
+
}
|
|
48
|
+
const token = "Bearer " + response.access_token;
|
|
49
|
+
const expires = Date.now() + response.expires_in * 1e3;
|
|
50
|
+
setCookie(event, prefix + "_token." + body.strategyName, token, cookie?.options || {});
|
|
51
|
+
setCookie(
|
|
52
|
+
event,
|
|
53
|
+
prefix + `_refresh_token.` + body.strategyName,
|
|
54
|
+
response.refresh_token,
|
|
55
|
+
cookie?.options || {}
|
|
56
|
+
);
|
|
57
|
+
setCookie(event, prefix + "strategy", body.strategyName, cookie?.options || {});
|
|
58
|
+
setCookie(
|
|
59
|
+
event,
|
|
60
|
+
prefix + "_token_expiration." + body.strategyName,
|
|
61
|
+
expires.toString(),
|
|
62
|
+
cookie?.options || {}
|
|
63
|
+
);
|
|
64
|
+
return { token, refresh_token: response.refresh_token, expires };
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error("[Auth Error]", error);
|
|
67
|
+
throw createError({
|
|
68
|
+
statusCode: error.statusCode || 500,
|
|
69
|
+
statusMessage: error.statusMessage || "Authentication failed"
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineEventHandler, createError } from "h3";
|
|
2
|
+
import { getAuthSession } from "../utils/index.js";
|
|
3
|
+
import protectedMiddleware from "../middleware/protected.js";
|
|
4
|
+
export default defineEventHandler(async (event) => {
|
|
5
|
+
try {
|
|
6
|
+
await protectedMiddleware(event);
|
|
7
|
+
const { token, expires, strategyName } = getAuthSession(event);
|
|
8
|
+
return { token, expires, strategyName, loggedIn: true };
|
|
9
|
+
} catch (error) {
|
|
10
|
+
console.error("[Auth Error] " + error);
|
|
11
|
+
throw createError({
|
|
12
|
+
statusCode: error.statusCode ? error.statusCode : 500,
|
|
13
|
+
statusMessage: error.statusMessage ? error.statusMessage : "Authentication failed"
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineEventHandler, createError } from "h3";
|
|
2
|
+
import { getAuthSession } from "../utils/index.js";
|
|
3
|
+
export default defineEventHandler(async (event) => {
|
|
4
|
+
try {
|
|
5
|
+
const { token, expires } = getAuthSession(event);
|
|
6
|
+
if (!token) {
|
|
7
|
+
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
8
|
+
}
|
|
9
|
+
const expirationTime = expires ? Number(expires) : NaN;
|
|
10
|
+
if (isNaN(expirationTime) || expirationTime < Date.now()) {
|
|
11
|
+
throw createError({ statusCode: 401, statusMessage: "Session expired" });
|
|
12
|
+
}
|
|
13
|
+
} catch (error) {
|
|
14
|
+
console.error("[Auth Error] ", error);
|
|
15
|
+
throw error.statusCode ? error : createError({ statusCode: 500, statusMessage: "Authentication failed" });
|
|
16
|
+
}
|
|
17
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { H3Event } from 'h3';
|
|
2
|
+
interface AuthSession {
|
|
3
|
+
token?: string;
|
|
4
|
+
expires?: string;
|
|
5
|
+
strategyName?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Retrieves the authentication session from HTTP-only cookies.
|
|
9
|
+
* @param event The H3 event from the request.
|
|
10
|
+
* @returns An object containing token, expires, and strategyName.
|
|
11
|
+
* @throws An error if the authentication configuration is missing.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getAuthSession(event: H3Event): AuthSession;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useRuntimeConfig } from "#imports";
|
|
2
|
+
import { getCookie, createError } from "h3";
|
|
3
|
+
export function getAuthSession(event) {
|
|
4
|
+
const runtimeConfig = useRuntimeConfig();
|
|
5
|
+
const config = runtimeConfig.public["nuxt-umbu"];
|
|
6
|
+
if (!config?.cookie) {
|
|
7
|
+
throw createError({ statusCode: 500, statusMessage: "Authentication module not configured" });
|
|
8
|
+
}
|
|
9
|
+
const prefix = config.cookie.prefix || "auth.";
|
|
10
|
+
const strategyName = getCookie(event, prefix + "strategy") || void 0;
|
|
11
|
+
const token = strategyName ? getCookie(event, prefix + "_token." + strategyName) || void 0 : void 0;
|
|
12
|
+
const expires = strategyName ? getCookie(event, prefix + "_token_expiration." + strategyName) || void 0 : void 0;
|
|
13
|
+
return { token, expires, strategyName };
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './middleware.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./middleware.js";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handles user logout by clearing session data and redirecting.
|
|
3
|
+
* @param strategy - The authentication strategy to log out from.
|
|
4
|
+
* @param redirectPath - The path to redirect after logout.
|
|
5
|
+
* @param middleware - The middleware type, defaults to "auth".
|
|
6
|
+
* @throws An error if called on the server side.
|
|
7
|
+
*/
|
|
8
|
+
export declare const handleLogout: (strategy: string | null, redirectPath: string, middleware?: string) => Promise<string | false | void | import("vue-router").RouteLocationAsRelativeGeneric | import("vue-router").RouteLocationAsPathGeneric | import("vue-router").NavigationFailure>;
|
|
9
|
+
/**
|
|
10
|
+
* Validates an authentication session based on token and expiration time.
|
|
11
|
+
* @param strategy - The authentication strategy in use.
|
|
12
|
+
* @param token - The authentication token.
|
|
13
|
+
* @param expires - Expiration timestamp of the token.
|
|
14
|
+
* @returns `true` if the session is valid, otherwise `false`.
|
|
15
|
+
*/
|
|
16
|
+
export declare const validateSession: (strategy: string | null, token: string | null, expires: string | null) => boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Retrieves the appropriate redirect path based on the authentication strategy.
|
|
19
|
+
* @param strategy - The authentication strategy in use.
|
|
20
|
+
* @returns The redirect path for login, callback, or home, defaults to `/` if none is found.
|
|
21
|
+
*/
|
|
22
|
+
export declare const getRedirectPath: (strategy: string | null) => string;
|