nuxt-bearer-auth 0.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/LICENSE +21 -0
- package/README.md +265 -0
- package/dist/module.cjs +5 -0
- package/dist/module.d.mts +134 -0
- package/dist/module.d.ts +134 -0
- package/dist/module.json +12 -0
- package/dist/module.mjs +222 -0
- package/dist/runtime/composables/useBearerAuth.d.ts +34 -0
- package/dist/runtime/composables/useBearerAuth.js +259 -0
- package/dist/runtime/middleware/bearer-auth.global.d.ts +3 -0
- package/dist/runtime/middleware/bearer-auth.global.js +28 -0
- package/dist/runtime/plugins/bearer-auth.server.d.ts +3 -0
- package/dist/runtime/plugins/bearer-auth.server.js +26 -0
- package/dist/runtime/server/api/auth/forgot-password.post.d.ts +3 -0
- package/dist/runtime/server/api/auth/forgot-password.post.js +16 -0
- package/dist/runtime/server/api/auth/login.post.d.ts +15 -0
- package/dist/runtime/server/api/auth/login.post.js +54 -0
- package/dist/runtime/server/api/auth/logout.post.d.ts +6 -0
- package/dist/runtime/server/api/auth/logout.post.js +20 -0
- package/dist/runtime/server/api/auth/me.get.d.ts +5 -0
- package/dist/runtime/server/api/auth/me.get.js +37 -0
- package/dist/runtime/server/api/auth/otp-verification.post.d.ts +8 -0
- package/dist/runtime/server/api/auth/otp-verification.post.js +38 -0
- package/dist/runtime/server/api/auth/refresh.post.d.ts +7 -0
- package/dist/runtime/server/api/auth/refresh.post.js +37 -0
- package/dist/runtime/server/api/auth/register.post.d.ts +8 -0
- package/dist/runtime/server/api/auth/register.post.js +32 -0
- package/dist/runtime/server/api/auth/resend-otp/[identifier].post.d.ts +3 -0
- package/dist/runtime/server/api/auth/resend-otp/[identifier].post.js +21 -0
- package/dist/runtime/server/api/auth/reset-password.post.d.ts +3 -0
- package/dist/runtime/server/api/auth/reset-password.post.js +16 -0
- package/dist/runtime/server/api/auth/sessions/[id].delete.d.ts +6 -0
- package/dist/runtime/server/api/auth/sessions/[id].delete.js +20 -0
- package/dist/runtime/server/api/auth/sessions.get.d.ts +5 -0
- package/dist/runtime/server/api/auth/sessions.get.js +11 -0
- package/dist/runtime/server/api/auth/social-login.post.d.ts +7 -0
- package/dist/runtime/server/api/auth/social-login.post.js +41 -0
- package/dist/runtime/server/middleware/auth.d.ts +3 -0
- package/dist/runtime/server/middleware/auth.js +29 -0
- package/dist/runtime/server/plugins/redis.d.ts +3 -0
- package/dist/runtime/server/plugins/redis.js +23 -0
- package/dist/runtime/server/utils/config.d.ts +16 -0
- package/dist/runtime/server/utils/config.js +24 -0
- package/dist/runtime/server/utils/errors.d.ts +2 -0
- package/dist/runtime/server/utils/errors.js +20 -0
- package/dist/runtime/server/utils/external-api.d.ts +15 -0
- package/dist/runtime/server/utils/external-api.js +31 -0
- package/dist/runtime/server/utils/normalize.d.ts +16 -0
- package/dist/runtime/server/utils/normalize.js +30 -0
- package/dist/runtime/server/utils/paths.d.ts +4 -0
- package/dist/runtime/server/utils/paths.js +23 -0
- package/dist/runtime/server/utils/sessions.d.ts +4645 -0
- package/dist/runtime/server/utils/sessions.js +207 -0
- package/dist/runtime/types/auth.d.ts +49 -0
- package/dist/runtime/types/auth.js +0 -0
- package/dist/runtime/types/h3.d.ts +7 -0
- package/dist/types.d.mts +7 -0
- package/dist/types.d.ts +7 -0
- package/package.json +58 -0
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { defineNuxtModule, createResolver, installModule, addImports, addPlugin, addRouteMiddleware, addServerPlugin, addServerHandler } from '@nuxt/kit';
|
|
2
|
+
import { defu } from 'defu';
|
|
3
|
+
|
|
4
|
+
const defaultOptions = {
|
|
5
|
+
apiBaseUrl: "",
|
|
6
|
+
redisUrl: "redis://127.0.0.1:6379",
|
|
7
|
+
sessionSecret: "",
|
|
8
|
+
appEnv: process.env.APP_ENV || process.env.NODE_ENV || "development",
|
|
9
|
+
installCsurf: true,
|
|
10
|
+
endpoints: {
|
|
11
|
+
login: "auth/login",
|
|
12
|
+
socialLogin: "auth/social-login",
|
|
13
|
+
logout: "auth/logout",
|
|
14
|
+
me: "auth/account/me",
|
|
15
|
+
refresh: "auth/refresh",
|
|
16
|
+
forgotPassword: "auth/forgot-password",
|
|
17
|
+
resetPassword: "auth/reset-password",
|
|
18
|
+
verifyOtp: "auth/verify-otp",
|
|
19
|
+
resendOtp: "auth/resend-otp/:identifier",
|
|
20
|
+
register: "auth/register"
|
|
21
|
+
},
|
|
22
|
+
responsePaths: {
|
|
23
|
+
token: ["data.token", "token", "access_token", "data.access_token"],
|
|
24
|
+
refreshToken: [
|
|
25
|
+
"data.refreshToken",
|
|
26
|
+
"data.refresh_token",
|
|
27
|
+
"refreshToken",
|
|
28
|
+
"refresh_token"
|
|
29
|
+
],
|
|
30
|
+
user: ["data.user", "user", "data", "$"],
|
|
31
|
+
userId: ["id", "uuid", "data.id", "data.uuid"],
|
|
32
|
+
message: ["message", "data.message"],
|
|
33
|
+
success: ["success", "status"],
|
|
34
|
+
code: ["code", "data.code"],
|
|
35
|
+
nextAction: ["next_action", "data.next_action", "nextAction"]
|
|
36
|
+
},
|
|
37
|
+
redirects: {
|
|
38
|
+
login: "/login",
|
|
39
|
+
authenticated: "/dashboard",
|
|
40
|
+
logout: "/",
|
|
41
|
+
unauthorized: "/auth/not-allowed"
|
|
42
|
+
},
|
|
43
|
+
routes: {
|
|
44
|
+
localApiPrefix: "/api/auth",
|
|
45
|
+
public: ["/", "/login", "/forgot-password", "/reset-password"],
|
|
46
|
+
authPages: ["/login", "/forgot-password", "/reset-password"],
|
|
47
|
+
protectedApiPrefixes: ["/api"],
|
|
48
|
+
publicApiPrefixes: [
|
|
49
|
+
"/api/auth/login",
|
|
50
|
+
"/api/auth/social-login",
|
|
51
|
+
"/api/auth/register",
|
|
52
|
+
"/api/auth/forgot-password",
|
|
53
|
+
"/api/auth/reset-password",
|
|
54
|
+
"/api/auth/otp-verification",
|
|
55
|
+
"/api/auth/resend-otp",
|
|
56
|
+
"/api/_csrf",
|
|
57
|
+
"/_nuxt",
|
|
58
|
+
"/__nuxt",
|
|
59
|
+
"/_ipx",
|
|
60
|
+
"/favicon.ico"
|
|
61
|
+
],
|
|
62
|
+
middleware: true
|
|
63
|
+
},
|
|
64
|
+
sessionCookie: {
|
|
65
|
+
name: "nuxt_bearer_auth_session",
|
|
66
|
+
devName: "nuxt_bearer_auth_session_dev",
|
|
67
|
+
maxAge: 60 * 60 * 24 * 7,
|
|
68
|
+
sameSite: "lax",
|
|
69
|
+
secure: void 0,
|
|
70
|
+
path: "/"
|
|
71
|
+
},
|
|
72
|
+
csrf: {
|
|
73
|
+
enabled: true,
|
|
74
|
+
https: false,
|
|
75
|
+
cookieKey: "nuxt_bearer_auth_csrf",
|
|
76
|
+
devCookieKey: "nuxt_bearer_auth_csrf_dev",
|
|
77
|
+
headerName: "x-csrf-token",
|
|
78
|
+
methods: ["POST", "PUT", "PATCH", "DELETE"],
|
|
79
|
+
methodsToProtect: ["POST", "PUT", "PATCH", "DELETE"],
|
|
80
|
+
cookie: {
|
|
81
|
+
path: "/",
|
|
82
|
+
httpOnly: true,
|
|
83
|
+
sameSite: "strict"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
verificationRequiredActions: ["verify_account", "verification_required"],
|
|
87
|
+
twoFactorRequiredActions: ["two_factor_required", "2fa_required"]
|
|
88
|
+
};
|
|
89
|
+
const module = defineNuxtModule({
|
|
90
|
+
meta: {
|
|
91
|
+
name: "nuxt-bearer-auth",
|
|
92
|
+
configKey: "bearerAuth",
|
|
93
|
+
compatibility: {
|
|
94
|
+
nuxt: "^3.12.0 || ^4.0.0"
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
defaults: defaultOptions,
|
|
98
|
+
async setup(moduleOptions, nuxt) {
|
|
99
|
+
const resolver = createResolver(import.meta.url);
|
|
100
|
+
const options = defu(moduleOptions, defaultOptions);
|
|
101
|
+
nuxt.options.runtimeConfig.bearerAuth = defu(
|
|
102
|
+
nuxt.options.runtimeConfig.bearerAuth,
|
|
103
|
+
{
|
|
104
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
105
|
+
redisUrl: options.redisUrl,
|
|
106
|
+
sessionSecret: options.sessionSecret,
|
|
107
|
+
appEnv: options.appEnv,
|
|
108
|
+
endpoints: options.endpoints,
|
|
109
|
+
responsePaths: options.responsePaths,
|
|
110
|
+
sessionCookie: options.sessionCookie,
|
|
111
|
+
verificationRequiredActions: options.verificationRequiredActions,
|
|
112
|
+
twoFactorRequiredActions: options.twoFactorRequiredActions
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
nuxt.options.runtimeConfig.public.bearerAuth = defu(
|
|
116
|
+
nuxt.options.runtimeConfig.public.bearerAuth,
|
|
117
|
+
{
|
|
118
|
+
redirects: options.redirects,
|
|
119
|
+
routes: options.routes
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
if (options.installCsurf && options.csrf?.enabled) {
|
|
123
|
+
await installModule("nuxt-csurf", {
|
|
124
|
+
https: options.csrf.https,
|
|
125
|
+
cookieKey: options.appEnv === "local" || options.appEnv === "development" ? options.csrf.devCookieKey : options.csrf.cookieKey,
|
|
126
|
+
cookie: options.csrf.cookie,
|
|
127
|
+
methods: options.csrf.methods,
|
|
128
|
+
methodsToProtect: options.csrf.methodsToProtect,
|
|
129
|
+
encryptAlgorithm: "aes-256-cbc",
|
|
130
|
+
addCsrfTokenToEventCtx: true,
|
|
131
|
+
headerName: options.csrf.headerName
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
addImports([
|
|
135
|
+
{
|
|
136
|
+
name: "useBearerAuth",
|
|
137
|
+
from: resolver.resolve("runtime/composables/useBearerAuth")
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: "useBearerAuth",
|
|
141
|
+
as: "useAuth",
|
|
142
|
+
from: resolver.resolve("runtime/composables/useBearerAuth")
|
|
143
|
+
}
|
|
144
|
+
]);
|
|
145
|
+
addPlugin(resolver.resolve("runtime/plugins/bearer-auth.server"));
|
|
146
|
+
if (options.routes?.middleware !== false) {
|
|
147
|
+
addRouteMiddleware({
|
|
148
|
+
name: "bearer-auth",
|
|
149
|
+
path: resolver.resolve("runtime/middleware/bearer-auth.global"),
|
|
150
|
+
global: true
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
addServerPlugin(resolver.resolve("runtime/server/plugins/redis"));
|
|
154
|
+
addServerHandler({
|
|
155
|
+
middleware: true,
|
|
156
|
+
handler: resolver.resolve("runtime/server/middleware/auth")
|
|
157
|
+
});
|
|
158
|
+
const prefix = options.routes?.localApiPrefix || "/api/auth";
|
|
159
|
+
addServerHandler({
|
|
160
|
+
route: `${prefix}/login`,
|
|
161
|
+
method: "post",
|
|
162
|
+
handler: resolver.resolve("runtime/server/api/auth/login.post")
|
|
163
|
+
});
|
|
164
|
+
addServerHandler({
|
|
165
|
+
route: `${prefix}/social-login`,
|
|
166
|
+
method: "post",
|
|
167
|
+
handler: resolver.resolve("runtime/server/api/auth/social-login.post")
|
|
168
|
+
});
|
|
169
|
+
addServerHandler({
|
|
170
|
+
route: `${prefix}/logout`,
|
|
171
|
+
method: "post",
|
|
172
|
+
handler: resolver.resolve("runtime/server/api/auth/logout.post")
|
|
173
|
+
});
|
|
174
|
+
addServerHandler({
|
|
175
|
+
route: `${prefix}/me`,
|
|
176
|
+
method: "get",
|
|
177
|
+
handler: resolver.resolve("runtime/server/api/auth/me.get")
|
|
178
|
+
});
|
|
179
|
+
addServerHandler({
|
|
180
|
+
route: `${prefix}/refresh`,
|
|
181
|
+
method: "post",
|
|
182
|
+
handler: resolver.resolve("runtime/server/api/auth/refresh.post")
|
|
183
|
+
});
|
|
184
|
+
addServerHandler({
|
|
185
|
+
route: `${prefix}/forgot-password`,
|
|
186
|
+
method: "post",
|
|
187
|
+
handler: resolver.resolve("runtime/server/api/auth/forgot-password.post")
|
|
188
|
+
});
|
|
189
|
+
addServerHandler({
|
|
190
|
+
route: `${prefix}/reset-password`,
|
|
191
|
+
method: "post",
|
|
192
|
+
handler: resolver.resolve("runtime/server/api/auth/reset-password.post")
|
|
193
|
+
});
|
|
194
|
+
addServerHandler({
|
|
195
|
+
route: `${prefix}/otp-verification`,
|
|
196
|
+
method: "post",
|
|
197
|
+
handler: resolver.resolve("runtime/server/api/auth/otp-verification.post")
|
|
198
|
+
});
|
|
199
|
+
addServerHandler({
|
|
200
|
+
route: `${prefix}/resend-otp/:identifier`,
|
|
201
|
+
method: "post",
|
|
202
|
+
handler: resolver.resolve("runtime/server/api/auth/resend-otp/[identifier].post")
|
|
203
|
+
});
|
|
204
|
+
addServerHandler({
|
|
205
|
+
route: `${prefix}/register`,
|
|
206
|
+
method: "post",
|
|
207
|
+
handler: resolver.resolve("runtime/server/api/auth/register.post")
|
|
208
|
+
});
|
|
209
|
+
addServerHandler({
|
|
210
|
+
route: `${prefix}/sessions`,
|
|
211
|
+
method: "get",
|
|
212
|
+
handler: resolver.resolve("runtime/server/api/auth/sessions.get")
|
|
213
|
+
});
|
|
214
|
+
addServerHandler({
|
|
215
|
+
route: `${prefix}/sessions/:id`,
|
|
216
|
+
method: "delete",
|
|
217
|
+
handler: resolver.resolve("runtime/server/api/auth/sessions/[id].delete")
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
export { module as default };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { BearerAuthUser, FetchUserOptions, LoginCredentials, SocialLoginCredentials } from "../types/auth.js";
|
|
2
|
+
export declare function useBearerAuth<User extends BearerAuthUser = BearerAuthUser>(): {
|
|
3
|
+
user: any;
|
|
4
|
+
status: any;
|
|
5
|
+
ready: any;
|
|
6
|
+
error: any;
|
|
7
|
+
loading: any;
|
|
8
|
+
isAuthenticated: any;
|
|
9
|
+
serverReady: any;
|
|
10
|
+
login: (credentials: LoginCredentials, redirectPath?: string | null) => Promise<any>;
|
|
11
|
+
socialLogin: (credentials: SocialLoginCredentials, redirectPath?: string | null) => Promise<any>;
|
|
12
|
+
register: (payload: Record<string, unknown>, redirectPath?: string | null) => Promise<any>;
|
|
13
|
+
verifyOtp: (payload: Record<string, unknown>, redirectPath?: string | null) => Promise<any>;
|
|
14
|
+
fetchUser: (options?: FetchUserOptions) => Promise<{
|
|
15
|
+
data: User | null;
|
|
16
|
+
error: string | null;
|
|
17
|
+
} | {
|
|
18
|
+
data: any;
|
|
19
|
+
error: null;
|
|
20
|
+
} | {
|
|
21
|
+
data: null;
|
|
22
|
+
error: any;
|
|
23
|
+
}>;
|
|
24
|
+
refresh: () => Promise<any>;
|
|
25
|
+
logout: (destination?: string) => Promise<void>;
|
|
26
|
+
forgotPassword: (payload: Record<string, unknown>) => Promise<any>;
|
|
27
|
+
resetPassword: (payload: Record<string, unknown>) => Promise<any>;
|
|
28
|
+
resendOtp: (identifier: string, payload?: Record<string, unknown>) => Promise<any>;
|
|
29
|
+
setUser: (value: User | null) => void;
|
|
30
|
+
setAuthReady: (value: boolean) => void;
|
|
31
|
+
setServerChecked: () => void;
|
|
32
|
+
clearAuthState: () => void;
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=useBearerAuth.d.ts.map
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
let serverCheckPromise = null;
|
|
2
|
+
let serverCheckResolve = null;
|
|
3
|
+
async function authFetch(request, options = {}) {
|
|
4
|
+
try {
|
|
5
|
+
const csrf = typeof useCsrf === "function" ? useCsrf() : null;
|
|
6
|
+
const fetcher = csrf?.csrfFetch || $fetch;
|
|
7
|
+
return await fetcher(request, options);
|
|
8
|
+
} catch (error) {
|
|
9
|
+
throw error;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function useBearerAuth() {
|
|
13
|
+
const config = useRuntimeConfig();
|
|
14
|
+
const publicAuth = config.public.bearerAuth;
|
|
15
|
+
const apiPrefix = publicAuth.routes.localApiPrefix || "/api/auth";
|
|
16
|
+
const user = useState("bearer-auth-user", () => null);
|
|
17
|
+
const status = useState("bearer-auth-status", () => "idle");
|
|
18
|
+
const ready = useState("bearer-auth-ready", () => false);
|
|
19
|
+
const error = useState("bearer-auth-error", () => null);
|
|
20
|
+
const serverChecked = useState("bearer-auth-server-checked", () => false);
|
|
21
|
+
const serverReady = computed(() => {
|
|
22
|
+
if (import.meta.client || serverChecked.value) {
|
|
23
|
+
return Promise.resolve();
|
|
24
|
+
}
|
|
25
|
+
if (!serverCheckPromise) {
|
|
26
|
+
serverCheckPromise = new Promise((resolve) => {
|
|
27
|
+
serverCheckResolve = resolve;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return serverCheckPromise;
|
|
31
|
+
});
|
|
32
|
+
const loading = computed(() => status.value === "loading");
|
|
33
|
+
const isAuthenticated = computed(() => status.value === "authenticated");
|
|
34
|
+
function resolveErrorMessage(err, fallback) {
|
|
35
|
+
if (err instanceof Error) return err.message;
|
|
36
|
+
if (typeof err === "object" && err !== null && "data" in err && typeof err.data === "object") {
|
|
37
|
+
const data = err.data;
|
|
38
|
+
return data?.message || data?.statusMessage || fallback;
|
|
39
|
+
}
|
|
40
|
+
return fallback;
|
|
41
|
+
}
|
|
42
|
+
async function completeAuthenticatedFlow(authenticatedUser, redirectPath) {
|
|
43
|
+
user.value = authenticatedUser || null;
|
|
44
|
+
status.value = authenticatedUser ? "authenticated" : "unauthenticated";
|
|
45
|
+
ready.value = true;
|
|
46
|
+
if (redirectPath !== null) {
|
|
47
|
+
await navigateTo(redirectPath || publicAuth.redirects.authenticated);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function login(credentials, redirectPath) {
|
|
51
|
+
status.value = "loading";
|
|
52
|
+
error.value = null;
|
|
53
|
+
try {
|
|
54
|
+
const response = await authFetch(`${apiPrefix}/login`, {
|
|
55
|
+
method: "POST",
|
|
56
|
+
body: credentials
|
|
57
|
+
});
|
|
58
|
+
if (response.user && !response.nextAction) {
|
|
59
|
+
await completeAuthenticatedFlow(response.user, redirectPath);
|
|
60
|
+
} else {
|
|
61
|
+
user.value = response.user || null;
|
|
62
|
+
status.value = "unauthenticated";
|
|
63
|
+
ready.value = true;
|
|
64
|
+
}
|
|
65
|
+
return response;
|
|
66
|
+
} catch (err) {
|
|
67
|
+
error.value = resolveErrorMessage(err, "Login failed");
|
|
68
|
+
status.value = "unauthenticated";
|
|
69
|
+
ready.value = true;
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function socialLogin(credentials, redirectPath) {
|
|
74
|
+
status.value = "loading";
|
|
75
|
+
error.value = null;
|
|
76
|
+
try {
|
|
77
|
+
const response = await authFetch(
|
|
78
|
+
`${apiPrefix}/social-login`,
|
|
79
|
+
{
|
|
80
|
+
method: "POST",
|
|
81
|
+
body: credentials
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
await completeAuthenticatedFlow(response.user || null, redirectPath);
|
|
85
|
+
return response;
|
|
86
|
+
} catch (err) {
|
|
87
|
+
error.value = resolveErrorMessage(err, "Social login failed");
|
|
88
|
+
status.value = "unauthenticated";
|
|
89
|
+
ready.value = true;
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function register(payload, redirectPath) {
|
|
94
|
+
status.value = "loading";
|
|
95
|
+
error.value = null;
|
|
96
|
+
try {
|
|
97
|
+
const response = await authFetch(`${apiPrefix}/register`, {
|
|
98
|
+
method: "POST",
|
|
99
|
+
body: payload
|
|
100
|
+
});
|
|
101
|
+
if (response.user) {
|
|
102
|
+
await completeAuthenticatedFlow(response.user, redirectPath);
|
|
103
|
+
} else {
|
|
104
|
+
status.value = "unauthenticated";
|
|
105
|
+
ready.value = true;
|
|
106
|
+
}
|
|
107
|
+
return response;
|
|
108
|
+
} catch (err) {
|
|
109
|
+
error.value = resolveErrorMessage(err, "Registration failed");
|
|
110
|
+
status.value = "unauthenticated";
|
|
111
|
+
ready.value = true;
|
|
112
|
+
throw err;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function verifyOtp(payload, redirectPath) {
|
|
116
|
+
status.value = "loading";
|
|
117
|
+
error.value = null;
|
|
118
|
+
try {
|
|
119
|
+
const response = await authFetch(
|
|
120
|
+
`${apiPrefix}/otp-verification`,
|
|
121
|
+
{
|
|
122
|
+
method: "POST",
|
|
123
|
+
body: payload
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
if (response.user) {
|
|
127
|
+
await completeAuthenticatedFlow(response.user, redirectPath);
|
|
128
|
+
} else {
|
|
129
|
+
status.value = "unauthenticated";
|
|
130
|
+
ready.value = true;
|
|
131
|
+
}
|
|
132
|
+
return response;
|
|
133
|
+
} catch (err) {
|
|
134
|
+
error.value = resolveErrorMessage(err, "OTP verification failed");
|
|
135
|
+
status.value = "unauthenticated";
|
|
136
|
+
ready.value = true;
|
|
137
|
+
throw err;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async function fetchUser(options = {}) {
|
|
141
|
+
if (status.value === "loading") {
|
|
142
|
+
return new Promise((resolve) => {
|
|
143
|
+
const unwatch = watch(status, (newStatus) => {
|
|
144
|
+
if (newStatus !== "loading") {
|
|
145
|
+
unwatch();
|
|
146
|
+
resolve({ data: user.value, error: error.value });
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
status.value = "loading";
|
|
152
|
+
error.value = null;
|
|
153
|
+
try {
|
|
154
|
+
const query = options.refresh ? "?refresh=true" : "";
|
|
155
|
+
const response = await authFetch(
|
|
156
|
+
`${apiPrefix}/me${query}`,
|
|
157
|
+
{
|
|
158
|
+
method: "GET"
|
|
159
|
+
}
|
|
160
|
+
);
|
|
161
|
+
user.value = response.user || null;
|
|
162
|
+
status.value = user.value ? "authenticated" : "unauthenticated";
|
|
163
|
+
ready.value = true;
|
|
164
|
+
return { data: user.value, error: null };
|
|
165
|
+
} catch (err) {
|
|
166
|
+
clearAuthState();
|
|
167
|
+
ready.value = true;
|
|
168
|
+
error.value = resolveErrorMessage(err, "Session expired");
|
|
169
|
+
return { data: null, error: error.value };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async function refresh() {
|
|
173
|
+
try {
|
|
174
|
+
const response = await authFetch(`${apiPrefix}/refresh`, {
|
|
175
|
+
method: "POST"
|
|
176
|
+
});
|
|
177
|
+
if (response.user) {
|
|
178
|
+
user.value = response.user;
|
|
179
|
+
status.value = "authenticated";
|
|
180
|
+
}
|
|
181
|
+
ready.value = true;
|
|
182
|
+
return response;
|
|
183
|
+
} catch (err) {
|
|
184
|
+
clearAuthState();
|
|
185
|
+
ready.value = true;
|
|
186
|
+
throw err;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async function logout(destination = publicAuth.redirects.logout) {
|
|
190
|
+
status.value = "loading";
|
|
191
|
+
error.value = null;
|
|
192
|
+
try {
|
|
193
|
+
await authFetch(`${apiPrefix}/logout`, { method: "POST" });
|
|
194
|
+
} finally {
|
|
195
|
+
clearAuthState();
|
|
196
|
+
ready.value = true;
|
|
197
|
+
await navigateTo(destination);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
async function forgotPassword(payload) {
|
|
201
|
+
return await authFetch(`${apiPrefix}/forgot-password`, {
|
|
202
|
+
method: "POST",
|
|
203
|
+
body: payload
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
async function resetPassword(payload) {
|
|
207
|
+
return await authFetch(`${apiPrefix}/reset-password`, {
|
|
208
|
+
method: "POST",
|
|
209
|
+
body: payload
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
async function resendOtp(identifier, payload = {}) {
|
|
213
|
+
return await authFetch(`${apiPrefix}/resend-otp/${encodeURIComponent(identifier)}`, {
|
|
214
|
+
method: "POST",
|
|
215
|
+
body: payload
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
function setServerChecked() {
|
|
219
|
+
if (import.meta.server) {
|
|
220
|
+
serverChecked.value = true;
|
|
221
|
+
serverCheckResolve?.();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function setAuthReady(value) {
|
|
225
|
+
ready.value = value;
|
|
226
|
+
}
|
|
227
|
+
function setUser(value) {
|
|
228
|
+
user.value = value;
|
|
229
|
+
status.value = value ? "authenticated" : "unauthenticated";
|
|
230
|
+
}
|
|
231
|
+
function clearAuthState() {
|
|
232
|
+
user.value = null;
|
|
233
|
+
error.value = null;
|
|
234
|
+
status.value = "unauthenticated";
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
user,
|
|
238
|
+
status,
|
|
239
|
+
ready,
|
|
240
|
+
error,
|
|
241
|
+
loading,
|
|
242
|
+
isAuthenticated,
|
|
243
|
+
serverReady,
|
|
244
|
+
login,
|
|
245
|
+
socialLogin,
|
|
246
|
+
register,
|
|
247
|
+
verifyOtp,
|
|
248
|
+
fetchUser,
|
|
249
|
+
refresh,
|
|
250
|
+
logout,
|
|
251
|
+
forgotPassword,
|
|
252
|
+
resetPassword,
|
|
253
|
+
resendOtp,
|
|
254
|
+
setUser,
|
|
255
|
+
setAuthReady,
|
|
256
|
+
setServerChecked,
|
|
257
|
+
clearAuthState
|
|
258
|
+
};
|
|
259
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useBearerAuth } from "../composables/useBearerAuth.js";
|
|
2
|
+
export default defineNuxtRouteMiddleware(async (to) => {
|
|
3
|
+
const config = useRuntimeConfig();
|
|
4
|
+
const publicAuth = config.public.bearerAuth;
|
|
5
|
+
const auth = useBearerAuth();
|
|
6
|
+
const isPublicRoute = publicAuth.routes.public.some((route) => {
|
|
7
|
+
return route === to.path || route !== "/" && to.path.startsWith(route);
|
|
8
|
+
});
|
|
9
|
+
const isAuthPage = publicAuth.routes.authPages.some((route) => {
|
|
10
|
+
return route === to.path || route !== "/" && to.path.startsWith(route);
|
|
11
|
+
});
|
|
12
|
+
if (import.meta.server) {
|
|
13
|
+
await auth.serverReady.value;
|
|
14
|
+
}
|
|
15
|
+
if (import.meta.client && !auth.ready.value) {
|
|
16
|
+
await auth.fetchUser();
|
|
17
|
+
}
|
|
18
|
+
if (auth.isAuthenticated.value && isAuthPage) {
|
|
19
|
+
const redirectPath = typeof to.query.redirect === "string" ? to.query.redirect : publicAuth.redirects.authenticated;
|
|
20
|
+
return navigateTo(redirectPath);
|
|
21
|
+
}
|
|
22
|
+
if (!auth.isAuthenticated.value && !isPublicRoute) {
|
|
23
|
+
return navigateTo({
|
|
24
|
+
path: publicAuth.redirects.login,
|
|
25
|
+
query: { redirect: to.fullPath }
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { getBearerAuthSession } from "../server/utils/sessions.js";
|
|
2
|
+
import { useBearerAuth } from "../composables/useBearerAuth.js";
|
|
3
|
+
export default defineNuxtPlugin(async (nuxtApp) => {
|
|
4
|
+
if (import.meta.client) return;
|
|
5
|
+
const event = nuxtApp.ssrContext?.event;
|
|
6
|
+
if (!event) return;
|
|
7
|
+
const auth = useBearerAuth();
|
|
8
|
+
try {
|
|
9
|
+
const session = await getBearerAuthSession(event);
|
|
10
|
+
auth.setUser(session?.profile || null);
|
|
11
|
+
nuxtApp.payload.bearerAuth = {
|
|
12
|
+
user: session?.profile || null,
|
|
13
|
+
status: session?.profile ? "authenticated" : "unauthenticated"
|
|
14
|
+
};
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error("[nuxt-bearer-auth] SSR auth hydration failed:", error);
|
|
17
|
+
auth.clearAuthState();
|
|
18
|
+
nuxtApp.payload.bearerAuth = {
|
|
19
|
+
user: null,
|
|
20
|
+
status: "unauthenticated"
|
|
21
|
+
};
|
|
22
|
+
} finally {
|
|
23
|
+
auth.setAuthReady(true);
|
|
24
|
+
auth.setServerChecked();
|
|
25
|
+
}
|
|
26
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineEventHandler, readBody } from "h3";
|
|
2
|
+
import { callAuthApi, getAuthEndpoint } from "../../utils/external-api.js";
|
|
3
|
+
import { toPublicError } from "../../utils/errors.js";
|
|
4
|
+
export default defineEventHandler(async (event) => {
|
|
5
|
+
try {
|
|
6
|
+
const body = await readBody(event);
|
|
7
|
+
const response = await callAuthApi(getAuthEndpoint("forgotPassword"), {
|
|
8
|
+
event,
|
|
9
|
+
method: "POST",
|
|
10
|
+
body
|
|
11
|
+
});
|
|
12
|
+
return response;
|
|
13
|
+
} catch (error) {
|
|
14
|
+
toPublicError(error, "Forgot password request failed");
|
|
15
|
+
}
|
|
16
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
|
|
2
|
+
success: boolean;
|
|
3
|
+
code: string | number | undefined;
|
|
4
|
+
nextAction: string | undefined;
|
|
5
|
+
user: import("../../../types/auth").BearerAuthUser | null;
|
|
6
|
+
message: string | undefined;
|
|
7
|
+
} | {
|
|
8
|
+
success: boolean;
|
|
9
|
+
user: import("../../../types/auth").BearerAuthUser | undefined;
|
|
10
|
+
message: string;
|
|
11
|
+
code?: undefined;
|
|
12
|
+
nextAction?: undefined;
|
|
13
|
+
}>>;
|
|
14
|
+
export default _default;
|
|
15
|
+
//# sourceMappingURL=login.post.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { createError, defineEventHandler, readBody } from "h3";
|
|
2
|
+
import { callAuthApi, getAuthEndpoint } from "../../utils/external-api.js";
|
|
3
|
+
import { toPublicError } from "../../utils/errors.js";
|
|
4
|
+
import {
|
|
5
|
+
normalizeAuthResponse,
|
|
6
|
+
requiresTwoFactor,
|
|
7
|
+
requiresVerification
|
|
8
|
+
} from "../../utils/normalize.js";
|
|
9
|
+
import { createBearerAuthSession } from "../../utils/sessions.js";
|
|
10
|
+
export default defineEventHandler(async (event) => {
|
|
11
|
+
try {
|
|
12
|
+
const body = await readBody(event);
|
|
13
|
+
if (!body.identifier || !body.password) {
|
|
14
|
+
throw createError({
|
|
15
|
+
statusCode: 422,
|
|
16
|
+
statusMessage: "Identifier and password are required"
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
const response = await callAuthApi(getAuthEndpoint("login"), {
|
|
20
|
+
event,
|
|
21
|
+
method: "POST",
|
|
22
|
+
body
|
|
23
|
+
});
|
|
24
|
+
const auth = normalizeAuthResponse(response);
|
|
25
|
+
if (requiresVerification(auth.nextAction) || requiresTwoFactor(auth.nextAction)) {
|
|
26
|
+
return {
|
|
27
|
+
success: true,
|
|
28
|
+
code: auth.code,
|
|
29
|
+
nextAction: auth.nextAction,
|
|
30
|
+
user: auth.user || null,
|
|
31
|
+
message: auth.message
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
if (!auth.token || !auth.userId) {
|
|
35
|
+
throw createError({
|
|
36
|
+
statusCode: 401,
|
|
37
|
+
statusMessage: auth.message || "Authentication failed"
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
await createBearerAuthSession(event, {
|
|
41
|
+
userId: auth.userId,
|
|
42
|
+
token: auth.token,
|
|
43
|
+
refreshToken: auth.refreshToken,
|
|
44
|
+
profile: auth.user
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
success: true,
|
|
48
|
+
user: auth.user,
|
|
49
|
+
message: auth.message || "Login successful"
|
|
50
|
+
};
|
|
51
|
+
} catch (error) {
|
|
52
|
+
toPublicError(error, "Login failed");
|
|
53
|
+
}
|
|
54
|
+
});
|