@meistrari/auth-nuxt 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/README.md +271 -0
- package/dist/module.d.mts +16 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +46 -0
- package/dist/runtime/composable.d.ts +5 -0
- package/dist/runtime/composable.js +29 -0
- package/dist/runtime/plugin.d.ts +15 -0
- package/dist/runtime/plugin.js +105 -0
- package/dist/runtime/server/api/sign-out.d.ts +2 -0
- package/dist/runtime/server/api/sign-out.js +16 -0
- package/dist/runtime/server/middleware/auth.d.ts +4 -0
- package/dist/runtime/server/middleware/auth.js +35 -0
- package/dist/runtime/server/middleware/set-cookies.d.ts +2 -0
- package/dist/runtime/server/middleware/set-cookies.js +42 -0
- package/dist/runtime/server/tsconfig.json +3 -0
- package/dist/runtime/server/types/h3.d.ts +22 -0
- package/dist/types.d.mts +3 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# @meistrari/auth-sdk
|
|
2
|
+
|
|
3
|
+
A Nuxt module that provides authentication and organization management capabilities using Better Auth.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @meistrari/auth-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
Add the module to your `nuxt.config.ts`:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
export default defineNuxtConfig({
|
|
17
|
+
modules: ['@meistrari/auth-sdk'],
|
|
18
|
+
authSdk: {
|
|
19
|
+
apiUrl: 'https://your-auth-api.com',
|
|
20
|
+
useJwt: true, // Enable JWT token refresh cycle
|
|
21
|
+
jwtCookieName: 'auth-jwt', // Custom JWT cookie name
|
|
22
|
+
skipServerMiddleware: false, // Skip automatic server middleware
|
|
23
|
+
isDevelopment: false, // Development mode settings
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Configuration Options
|
|
29
|
+
|
|
30
|
+
| Option | Type | Default | Description |
|
|
31
|
+
|--------|------|---------|-------------|
|
|
32
|
+
| `apiUrl` | `string` | **Required** | Base URL of your authentication API |
|
|
33
|
+
| `useJwt` | `boolean` | `false` | Enable JWT cookie management and token refresh |
|
|
34
|
+
| `jwtCookieName` | `string` | `'auth-jwt'` | Name of the JWT cookie |
|
|
35
|
+
| `skipServerMiddleware` | `boolean` | `false` | Skip automatic server-side auth context setup |
|
|
36
|
+
| `isDevelopment` | `boolean` | `false` | Enable development-specific features |
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
### Client-side Authentication
|
|
41
|
+
|
|
42
|
+
The SDK provides the `useMeistrariAuth` composable for managing authentication state:
|
|
43
|
+
|
|
44
|
+
```vue
|
|
45
|
+
<script setup>
|
|
46
|
+
const { user, session, signOut } = useMeistrariAuth()
|
|
47
|
+
|
|
48
|
+
// Access current user
|
|
49
|
+
console.log(user.value) // User object or null
|
|
50
|
+
|
|
51
|
+
// Access current session
|
|
52
|
+
console.log(session.value) // Session object or null
|
|
53
|
+
|
|
54
|
+
// Sign out
|
|
55
|
+
await signOut(() => {
|
|
56
|
+
// Optional callback after sign out
|
|
57
|
+
console.log('User signed out')
|
|
58
|
+
})
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<template>
|
|
62
|
+
<div v-if="user">
|
|
63
|
+
Welcome, {{ user.name }}!
|
|
64
|
+
<button @click="signOut">Sign Out</button>
|
|
65
|
+
</div>
|
|
66
|
+
<div v-else>
|
|
67
|
+
Please sign in
|
|
68
|
+
</div>
|
|
69
|
+
</template>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Authentication Client
|
|
73
|
+
|
|
74
|
+
Access the underlying Better Auth client through the Nuxt app:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
// In a plugin, middleware, or component
|
|
78
|
+
const { $auth } = useNuxtApp()
|
|
79
|
+
|
|
80
|
+
// Get current session
|
|
81
|
+
const sessionData = await $auth.client.getSession()
|
|
82
|
+
|
|
83
|
+
// Organization operations
|
|
84
|
+
await $auth.client.organization.create({
|
|
85
|
+
name: 'My Organization',
|
|
86
|
+
slug: 'my-org'
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
// Access custom endpoints
|
|
90
|
+
await $auth.client.customEndpoints.session.token()
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Server-side Usage
|
|
94
|
+
|
|
95
|
+
The SDK automatically sets up server-side authentication context for API routes:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// server/api/protected.ts
|
|
99
|
+
export default defineEventHandler(async (event) => {
|
|
100
|
+
// Authentication context is automatically available
|
|
101
|
+
const { user, session } = event.context.auth
|
|
102
|
+
|
|
103
|
+
if (!user) {
|
|
104
|
+
throw createError({
|
|
105
|
+
statusCode: 401,
|
|
106
|
+
statusMessage: 'Unauthorized'
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
message: `Hello, ${user.name}!`,
|
|
112
|
+
userId: user.id
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Custom Middleware
|
|
118
|
+
|
|
119
|
+
You can create custom server middleware using the provided helper:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// server/middleware/custom.ts
|
|
123
|
+
import { meistrariAuthMiddleware } from '@meistrari/auth-sdk/server/middleware/auth'
|
|
124
|
+
|
|
125
|
+
export default meistrariAuthMiddleware(async (event) => {
|
|
126
|
+
// Your custom logic here
|
|
127
|
+
// event.context.auth contains user and session
|
|
128
|
+
})
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### JWT Token Management
|
|
132
|
+
|
|
133
|
+
When `useJwt` is enabled, the SDK automatically:
|
|
134
|
+
|
|
135
|
+
- Manages JWT tokens in cookies
|
|
136
|
+
- Refreshes tokens before expiration (every 60 seconds)
|
|
137
|
+
- Validates token expiry client-side
|
|
138
|
+
- Provides `getToken()` method for accessing valid tokens
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
const { $auth } = useNuxtApp()
|
|
142
|
+
|
|
143
|
+
// Get a valid JWT token (refreshes if needed)
|
|
144
|
+
const token = await $auth.getToken()
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Types
|
|
148
|
+
|
|
149
|
+
The SDK exports comprehensive TypeScript types:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import type {
|
|
153
|
+
User,
|
|
154
|
+
Session,
|
|
155
|
+
Organization,
|
|
156
|
+
Workspace,
|
|
157
|
+
Member,
|
|
158
|
+
Invitation,
|
|
159
|
+
Team,
|
|
160
|
+
TeamMember
|
|
161
|
+
} from '@meistrari/auth-sdk/utils'
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Key Types
|
|
165
|
+
|
|
166
|
+
- **User**: User account information including email, name, and verification status
|
|
167
|
+
- **Session**: Active session data with expiration and device info
|
|
168
|
+
- **Organization**: Organization entity with basic metadata
|
|
169
|
+
- **Workspace**: Extended organization with settings and statistics
|
|
170
|
+
- **Member**: Organization member with role and team assignments
|
|
171
|
+
- **Team**: Team entity within an organization
|
|
172
|
+
- **Invitation**: Pending organization invitations
|
|
173
|
+
|
|
174
|
+
## Organization & Team Management
|
|
175
|
+
|
|
176
|
+
The SDK includes full organization and team management capabilities:
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
const { $auth } = useNuxtApp()
|
|
180
|
+
|
|
181
|
+
// Create organization
|
|
182
|
+
await $auth.client.organization.create({
|
|
183
|
+
name: 'Acme Corp',
|
|
184
|
+
slug: 'acme-corp'
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
// Invite members
|
|
188
|
+
await $auth.client.organization.inviteMember({
|
|
189
|
+
email: 'user@example.com',
|
|
190
|
+
role: 'org:member',
|
|
191
|
+
organizationId: 'org-id'
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
// Create teams
|
|
195
|
+
await $auth.client.organization.createTeam({
|
|
196
|
+
name: 'Development Team',
|
|
197
|
+
organizationId: 'org-id'
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
// Manage member roles
|
|
201
|
+
await $auth.client.organization.updateMemberRole({
|
|
202
|
+
membershipId: 'member-id',
|
|
203
|
+
role: 'org:admin'
|
|
204
|
+
})
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Role-based Access Control
|
|
208
|
+
|
|
209
|
+
The SDK implements role-based access control with three built-in roles:
|
|
210
|
+
|
|
211
|
+
- **org:admin**: Full organization management permissions
|
|
212
|
+
- **org:reviewer**: Review-only access
|
|
213
|
+
- **org:member**: Basic member access
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
// Access control is automatically enforced in API calls
|
|
217
|
+
// Roles are validated server-side based on user permissions
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Utilities
|
|
221
|
+
|
|
222
|
+
### Token Validation
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
import { isTokenExpired, validateToken } from '@meistrari/auth-sdk/utils'
|
|
226
|
+
|
|
227
|
+
// Check if token is expired
|
|
228
|
+
if (isTokenExpired(jwtToken)) {
|
|
229
|
+
// Token needs refresh
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Validate token against JWKS endpoint
|
|
233
|
+
const isValid = await validateToken(jwtToken, 'https://your-api.com')
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Error Handling
|
|
237
|
+
|
|
238
|
+
The SDK handles common authentication errors automatically:
|
|
239
|
+
|
|
240
|
+
- Expired tokens are refreshed automatically
|
|
241
|
+
- Invalid sessions redirect to sign-in
|
|
242
|
+
- Network errors are gracefully handled
|
|
243
|
+
- CSRF protection is built-in
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
try {
|
|
247
|
+
await $auth.client.getSession()
|
|
248
|
+
} catch (error) {
|
|
249
|
+
console.error('Authentication failed:', error.message)
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Development
|
|
254
|
+
|
|
255
|
+
Set `isDevelopment: true` in your config to enable:
|
|
256
|
+
|
|
257
|
+
- Additional debugging logs
|
|
258
|
+
- Development-specific cookie handling
|
|
259
|
+
- Relaxed security policies for local development
|
|
260
|
+
|
|
261
|
+
## Security Features
|
|
262
|
+
|
|
263
|
+
- **CSRF Protection**: Built into all API calls
|
|
264
|
+
- **JWT Validation**: Cryptographic validation using JWKS
|
|
265
|
+
- **Secure Cookies**: HTTP-only, secure cookies in production
|
|
266
|
+
- **Token Refresh**: Automatic token rotation
|
|
267
|
+
- **Session Management**: Secure session handling
|
|
268
|
+
|
|
269
|
+
## Migration
|
|
270
|
+
|
|
271
|
+
If upgrading from a previous version, ensure your configuration includes the required `apiUrl` parameter and update any deprecated options.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
|
|
3
|
+
interface ModuleOptions {
|
|
4
|
+
/** Auth API base URL */
|
|
5
|
+
apiUrl: string;
|
|
6
|
+
/** Set JWT cookie and start token refresh cycle */
|
|
7
|
+
useJwt: boolean;
|
|
8
|
+
/** Cookie name for authentication token */
|
|
9
|
+
jwtCookieName: string;
|
|
10
|
+
skipServerMiddleware: boolean;
|
|
11
|
+
isDevelopment: boolean;
|
|
12
|
+
}
|
|
13
|
+
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
14
|
+
|
|
15
|
+
export { _default as default };
|
|
16
|
+
export type { ModuleOptions };
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { defineNuxtModule, createResolver, addServerHandler, addImports, addPlugin } from '@nuxt/kit';
|
|
2
|
+
|
|
3
|
+
const module = defineNuxtModule({
|
|
4
|
+
meta: {
|
|
5
|
+
name: "@meistrari/auth-sdk",
|
|
6
|
+
configKey: "authSdk"
|
|
7
|
+
},
|
|
8
|
+
defaults: {
|
|
9
|
+
apiUrl: "",
|
|
10
|
+
useJwt: false,
|
|
11
|
+
jwtCookieName: "auth-jwt",
|
|
12
|
+
skipServerMiddleware: false,
|
|
13
|
+
isDevelopment: false
|
|
14
|
+
},
|
|
15
|
+
setup(options, nuxt) {
|
|
16
|
+
if (!options.apiUrl) {
|
|
17
|
+
console.error("[@meistrari/auth-sdk] error: apiUrl is required in module config");
|
|
18
|
+
}
|
|
19
|
+
const resolver = createResolver(import.meta.url);
|
|
20
|
+
nuxt.options.runtimeConfig.public.auth = options;
|
|
21
|
+
if (!options.skipServerMiddleware) {
|
|
22
|
+
addServerHandler({
|
|
23
|
+
route: "",
|
|
24
|
+
handler: resolver.resolve("./runtime/server/middleware/auth")
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
if (options.isDevelopment) {
|
|
28
|
+
addServerHandler({
|
|
29
|
+
route: "",
|
|
30
|
+
handler: resolver.resolve("./runtime/server/middleware/set-cookies")
|
|
31
|
+
});
|
|
32
|
+
addServerHandler({
|
|
33
|
+
route: "/api/meistrari-auth/sign-out",
|
|
34
|
+
handler: resolver.resolve("./runtime/server/api/sign-out")
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
addImports({
|
|
38
|
+
name: "useMeistrariAuth",
|
|
39
|
+
as: "useMeistrariAuth",
|
|
40
|
+
from: resolver.resolve("runtime/composable")
|
|
41
|
+
});
|
|
42
|
+
addPlugin(resolver.resolve("./runtime/plugin"));
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export { module as default };
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createAuthClient } from '@meistrari/auth-core';
|
|
2
|
+
declare const _default: import("#app").Plugin<{
|
|
3
|
+
auth: {
|
|
4
|
+
client: ReturnType<typeof createAuthClient>;
|
|
5
|
+
getToken: () => Promise<string | null | undefined>;
|
|
6
|
+
refreshToken: () => Promise<string | null | undefined>;
|
|
7
|
+
};
|
|
8
|
+
}> & import("#app").ObjectPlugin<{
|
|
9
|
+
auth: {
|
|
10
|
+
client: ReturnType<typeof createAuthClient>;
|
|
11
|
+
getToken: () => Promise<string | null | undefined>;
|
|
12
|
+
refreshToken: () => Promise<string | null | undefined>;
|
|
13
|
+
};
|
|
14
|
+
}>;
|
|
15
|
+
export default _default;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { defineNuxtPlugin, useCookie, useRequestHeaders, useRuntimeConfig } from "#app";
|
|
2
|
+
import { watch } from "vue";
|
|
3
|
+
import { createAuthClient, isTokenExpired } from "@meistrari/auth-core";
|
|
4
|
+
import { useMeistrariAuth } from "./composable.js";
|
|
5
|
+
export default defineNuxtPlugin({
|
|
6
|
+
name: "meistrari-auth",
|
|
7
|
+
parallel: true,
|
|
8
|
+
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
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
if (session2.error || !session2.data?.session) {
|
|
43
|
+
throw new Error("Failed to refresh token");
|
|
44
|
+
}
|
|
45
|
+
return token.value;
|
|
46
|
+
}
|
|
47
|
+
async function initializeSession() {
|
|
48
|
+
if (session.value && user.value)
|
|
49
|
+
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) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
user.value = sessionData.user;
|
|
74
|
+
session.value = sessionData.session;
|
|
75
|
+
}
|
|
76
|
+
await initializeSession();
|
|
77
|
+
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);
|
|
89
|
+
}
|
|
90
|
+
createTokenRefreshInterval();
|
|
91
|
+
}
|
|
92
|
+
}, { immediate: true });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
provide: {
|
|
97
|
+
auth: {
|
|
98
|
+
client: authClient,
|
|
99
|
+
getToken,
|
|
100
|
+
refreshToken
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AuthenticatedH3Event } from '../types/h3.js';
|
|
2
|
+
export declare function meistrariAuthMiddleware(callback: (event: AuthenticatedH3Event) => void | Promise<void>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
3
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
4
|
+
export default _default;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { defineEventHandler, getCookie } from "h3";
|
|
2
|
+
import { useRuntimeConfig } from "nitropack/runtime/config";
|
|
3
|
+
import { createAuthClient } from "@meistrari/auth-core";
|
|
4
|
+
async function setAuthContext(event) {
|
|
5
|
+
event.context.auth = {
|
|
6
|
+
session: null,
|
|
7
|
+
user: null
|
|
8
|
+
};
|
|
9
|
+
const sessionCookie = getCookie(event, "__Secure-__session");
|
|
10
|
+
if (!sessionCookie)
|
|
11
|
+
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) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
event.context.auth.session = sessionData.session;
|
|
23
|
+
event.context.auth.user = sessionData.user;
|
|
24
|
+
}
|
|
25
|
+
export function meistrariAuthMiddleware(callback) {
|
|
26
|
+
return defineEventHandler(async (event) => {
|
|
27
|
+
await setAuthContext(event);
|
|
28
|
+
await callback(event);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export default defineEventHandler(async (event) => {
|
|
32
|
+
if (!event.path.startsWith("/api"))
|
|
33
|
+
return;
|
|
34
|
+
await setAuthContext(event);
|
|
35
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { User, Session } from '@meistrari/auth-core'
|
|
2
|
+
import type { H3Event, H3EventContext } from 'h3'
|
|
3
|
+
|
|
4
|
+
declare module 'h3' {
|
|
5
|
+
interface H3EventContext {
|
|
6
|
+
auth?: {
|
|
7
|
+
user: User | null
|
|
8
|
+
session: Session | null
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface AuthenticatedH3EventContext extends H3EventContext {
|
|
14
|
+
auth: {
|
|
15
|
+
user: User | null
|
|
16
|
+
session: Session | null
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface AuthenticatedH3Event extends H3Event {
|
|
21
|
+
context: AuthenticatedH3EventContext
|
|
22
|
+
}
|
package/dist/types.d.mts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@meistrari/auth-nuxt",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/types.d.mts",
|
|
8
|
+
"import": "./dist/module.mjs"
|
|
9
|
+
},
|
|
10
|
+
"./server/middleware/auth": {
|
|
11
|
+
"types": "./dist/runtime/server/middleware/auth.d.ts",
|
|
12
|
+
"import": "./dist/runtime/server/middleware/auth.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/module.mjs",
|
|
16
|
+
"typesVersions": {
|
|
17
|
+
"*": {
|
|
18
|
+
".": [
|
|
19
|
+
"./dist/types.d.mts"
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt-module-build build"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@meistrari/auth-core": "0.1.1"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"nuxt": "^3.0.0 || ^4.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@nuxt/devtools": "2.6.3",
|
|
37
|
+
"@nuxt/eslint-config": "1.9.0",
|
|
38
|
+
"@nuxt/kit": "4.0.3",
|
|
39
|
+
"@nuxt/module-builder": "1.0.2",
|
|
40
|
+
"@nuxt/schema": "4.0.3",
|
|
41
|
+
"@nuxt/test-utils": "3.19.2",
|
|
42
|
+
"@types/node": "latest",
|
|
43
|
+
"changelogen": "0.6.2",
|
|
44
|
+
"eslint": "9.34.0",
|
|
45
|
+
"nuxt": "4.0.3",
|
|
46
|
+
"typescript": "5.9.2",
|
|
47
|
+
"unbuild": "3.6.1",
|
|
48
|
+
"vitest": "3.2.4",
|
|
49
|
+
"vue-tsc": "3.0.6"
|
|
50
|
+
}
|
|
51
|
+
}
|