@devcoffee/nuxt-core 1.3.0 → 1.4.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/CHANGELOG.md +34 -0
- package/dist/module.d.mts +249 -243
- package/dist/module.d.ts +249 -243
- package/dist/module.json +1 -1
- package/dist/module.mjs +2 -2
- package/dist/runtime/server/core/helpers.d.ts +24 -1
- package/dist/runtime/server/core/helpers.js +27 -8
- package/dist/runtime/server/core/nuxtForwardHandler.js +11 -2
- package/dist/runtime/server/plugins/authts.js +9 -13
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.4.1
|
|
4
|
+
|
|
5
|
+
[compare changes](https://github.com/coolkg1412/devcoffee-nuxt-core/compare/v1.4.0...v1.4.1)
|
|
6
|
+
|
|
7
|
+
### 🩹 Fixes
|
|
8
|
+
|
|
9
|
+
- Normalize expiresIn to seconds across all session consumers ([2c197ab](https://github.com/coolkg1412/devcoffee-nuxt-core/commit/2c197ab))
|
|
10
|
+
|
|
11
|
+
### ❤️ Contributors
|
|
12
|
+
|
|
13
|
+
- Hieu Nguyen <hieu.nguyen@devcoffee.tech>
|
|
14
|
+
|
|
15
|
+
## v1.4.0
|
|
16
|
+
|
|
17
|
+
[compare changes](https://github.com/coolkg1412/devcoffee-nuxt-core/compare/v1.3.0...v1.4.0)
|
|
18
|
+
|
|
19
|
+
### 🚀 Enhancements
|
|
20
|
+
|
|
21
|
+
- Implement writeSessionCookie function and integrate it into NuxtForwardRequestHandler and authts plugin ([e761f6c](https://github.com/coolkg1412/devcoffee-nuxt-core/commit/e761f6c))
|
|
22
|
+
|
|
23
|
+
### 🩹 Fixes
|
|
24
|
+
|
|
25
|
+
- Drop return from proxyRequest in NuxtForwardRequestHandler to prevent ERR_HTTP_HEADERS_SENT ([fd3b91c](https://github.com/coolkg1412/devcoffee-nuxt-core/commit/fd3b91c))
|
|
26
|
+
- Prevent sending headers after response has been finalized in beforeResponse hook ([444a012](https://github.com/coolkg1412/devcoffee-nuxt-core/commit/444a012))
|
|
27
|
+
- Update version to 1.3.2 in package.json and package-lock.json ([e6ab449](https://github.com/coolkg1412/devcoffee-nuxt-core/commit/e6ab449))
|
|
28
|
+
|
|
29
|
+
### 💅 Refactors
|
|
30
|
+
|
|
31
|
+
- Remove redundant signSessionId checks from authts.sec03.test.ts ([18686f4](https://github.com/coolkg1412/devcoffee-nuxt-core/commit/18686f4))
|
|
32
|
+
|
|
33
|
+
### ❤️ Contributors
|
|
34
|
+
|
|
35
|
+
- Hieu Nguyen <hieu.nguyen@devcoffee.tech>
|
|
36
|
+
|
|
3
37
|
## v1.3.0
|
|
4
38
|
|
|
5
39
|
[compare changes](https://github.com/coolkg1412/devcoffee-nuxt-core/compare/v1.2.5...v1.3.0)
|
package/dist/module.d.mts
CHANGED
|
@@ -3,249 +3,255 @@ import { CookieSerializeOptions } from '../dist/runtime/server/adapters/http.js'
|
|
|
3
3
|
import { OidcUserInfo } from '../dist/runtime/server/adapters/oidc.js';
|
|
4
4
|
import { LogLevel as LogLevel$1, ConsolaOptions, ConsolaInstance } from 'consola';
|
|
5
5
|
|
|
6
|
-
interface AuthorizedUser {
|
|
7
|
-
id: string
|
|
8
|
-
sub: string
|
|
9
|
-
email: string
|
|
10
|
-
firstName: string
|
|
11
|
-
lastName: string
|
|
12
|
-
locale: string
|
|
13
|
-
language: string
|
|
14
|
-
timezone: string
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
type AuthStatus = 'unauthenticated' | 'authenticated'
|
|
18
|
-
|
|
19
|
-
type AuthData = {
|
|
20
|
-
status: AuthStatus
|
|
21
|
-
tokenSet?: {
|
|
22
|
-
accessToken: string
|
|
23
|
-
tokenType: string
|
|
24
|
-
expiresAt: number
|
|
25
|
-
idToken: string
|
|
26
|
-
refreshToken: string
|
|
27
|
-
scopes: string[]
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
type SessionContext<TData = Record<string, unknown>> = {
|
|
32
|
-
id: string
|
|
33
|
-
auth: AuthData
|
|
34
|
-
user: AuthorizedUser
|
|
35
|
-
data: TData
|
|
36
|
-
issuedAt: number
|
|
37
|
-
expiresAt: number
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
type NuxtAuthOptions = {
|
|
41
|
-
/**
|
|
42
|
-
* Default Devcoffee Nuxt AuthTS option callbacks for session and user mapping.
|
|
43
|
-
* These can be overridden by passing custom callbacks to `NuxtAuthtsHandler()`.
|
|
44
|
-
* @since 1.0.0
|
|
45
|
-
*/
|
|
46
|
-
session: (
|
|
47
|
-
session: Omit<SessionContext, 'auth'>,
|
|
48
|
-
auth: SessionContext['auth']
|
|
49
|
-
) => Awaitable<Omit<SessionContext, 'auth' | 'issuedAt' | 'expiresAt'> & { isAuthenticated: boolean }>
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Maps the OpenID Connect user info response to your local user schema.
|
|
53
|
-
*
|
|
54
|
-
* @param user - The raw user info response from the OpenID provider.
|
|
55
|
-
* @param opts - The token response containing access and ID tokens.
|
|
56
|
-
* @returns A normalized user object.
|
|
57
|
-
* @since 1.0.0
|
|
58
|
-
*/
|
|
59
|
-
userInfo: (
|
|
60
|
-
user: AuthorizedUser,
|
|
61
|
-
opts: {
|
|
62
|
-
openidUser?: OidcUserInfo
|
|
63
|
-
tokenSet: Exclude<SessionContext['auth']['tokenSet'], undefined>
|
|
64
|
-
}
|
|
65
|
-
) => Awaitable<AuthorizedUser>
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
type NuxtSessionContext<TData = Record<string, unknown>> = {
|
|
69
|
-
readonly id: string
|
|
70
|
-
readonly isAuthenticated: boolean
|
|
71
|
-
readonly user: AuthorizedUser
|
|
72
|
-
readonly data: TData
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
type NuxtSessionUpdateContext<TData = Record<string, unknown>> = DeepPartial<{
|
|
76
|
-
user: AuthorizedUser
|
|
77
|
-
data: TData
|
|
78
|
-
}>
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* Defines parameters used for client registration, discovery,
|
|
84
|
-
* and token exchange with the OpenID Provider.
|
|
85
|
-
*
|
|
86
|
-
* @since 1.0.0
|
|
87
|
-
*/
|
|
88
|
-
type OpenIdOptions = {
|
|
89
|
-
/** Client ID registered with the OpenID Provider. */
|
|
90
|
-
clientId: string
|
|
91
|
-
|
|
92
|
-
/** Client secret for token exchange (if applicable). */
|
|
93
|
-
clientSecret: string
|
|
94
|
-
|
|
95
|
-
/** Whether to use Proof Key for Code Exchange (PKCE) flow. */
|
|
96
|
-
usePkce: boolean
|
|
97
|
-
|
|
98
|
-
/** Code challenge method, e.g. `S256`. */
|
|
99
|
-
codeChallengeMethod: string
|
|
100
|
-
|
|
101
|
-
/** Scopes requested from the OpenID Provider. */
|
|
102
|
-
scopes: string[]
|
|
103
|
-
|
|
104
|
-
/** Redirect URI used after authorization. */
|
|
105
|
-
redirectUri: string
|
|
106
|
-
|
|
107
|
-
/** The `.well-known/openid-configuration` discovery URL. */
|
|
108
|
-
wellKnownUrl: string
|
|
109
|
-
|
|
110
|
-
/** Whether to automatically fetch user info after login. */
|
|
111
|
-
autoFetchUser: boolean
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* TTL in seconds for caching autoFetchUser results per session.
|
|
115
|
-
* A GET_SESSION within the TTL window does not trigger an OIDC provider call.
|
|
116
|
-
* Only applies when autoFetchUser is true.
|
|
117
|
-
* @default 300
|
|
118
|
-
* @since 1.0.0
|
|
119
|
-
*/
|
|
120
|
-
autoFetchUserTtl: number
|
|
121
|
-
|
|
122
|
-
/** Whether to fetch user info immediately after token grant. */
|
|
123
|
-
fetchUserOnLogin: boolean
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Window in milliseconds before token expiry at which a refresh is triggered.
|
|
127
|
-
* Tokens with expiresAt more than this many milliseconds in the future are
|
|
128
|
-
* considered fresh and the refresh path is skipped entirely.
|
|
129
|
-
* @default 60000
|
|
130
|
-
* @since 1.0.0
|
|
131
|
-
*/
|
|
132
|
-
tokenRefreshBufferMs: number
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* When `true`, the token refresh mutex uses an atomic Redis NX write
|
|
136
|
-
* (`SET key value NX EX ttl`) via the IORedis native client accessor for
|
|
137
|
-
* true distributed exclusion across multiple server instances. When `false`
|
|
138
|
-
* (default), the existing optimistic lock (`hasItem` + `setItem`) is used.
|
|
139
|
-
*
|
|
140
|
-
* @remarks Only effective when the Nitro `cache` storage mount is backed by
|
|
141
|
-
* a Redis driver. Setting `true` with a non-Redis backend falls through to
|
|
142
|
-
* the optimistic path silently.
|
|
143
|
-
*
|
|
144
|
-
* @default false
|
|
145
|
-
* @since 1.0.0
|
|
146
|
-
*/
|
|
147
|
-
distributedLock?: boolean
|
|
148
|
-
|
|
149
|
-
/** Cache configuration for storing OpenID metadata. */
|
|
150
|
-
cache: {
|
|
151
|
-
/** Cache key prefix for the metadata store. */
|
|
152
|
-
prefix: string
|
|
153
|
-
/** Expiry time in seconds for cached metadata. */
|
|
154
|
-
expires: number
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
* Defines session storage, expiration, and cookie behavior
|
|
162
|
-
* for authentication session tracking.
|
|
163
|
-
*
|
|
164
|
-
* @since 1.0.0
|
|
165
|
-
*/
|
|
166
|
-
type SessionsOptions = {
|
|
167
|
-
/** Cookie name mappings used by the session handler. */
|
|
168
|
-
names: {
|
|
169
|
-
/** PKCE verifier cookie name. */
|
|
170
|
-
pkce: string
|
|
171
|
-
/** State cookie name. */
|
|
172
|
-
state: string
|
|
173
|
-
/** Session ID cookie name. */
|
|
174
|
-
sessionId: string
|
|
175
|
-
/** Redirect URL cookie name. */
|
|
176
|
-
redirectUrl: string
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
storage: {
|
|
180
|
-
name: string
|
|
181
|
-
|
|
182
|
-
/** Key prefix for session data in the storage backend. */
|
|
183
|
-
prefix: string
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
/** Default
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
|
|
6
|
+
interface AuthorizedUser {
|
|
7
|
+
id: string
|
|
8
|
+
sub: string
|
|
9
|
+
email: string
|
|
10
|
+
firstName: string
|
|
11
|
+
lastName: string
|
|
12
|
+
locale: string
|
|
13
|
+
language: string
|
|
14
|
+
timezone: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type AuthStatus = 'unauthenticated' | 'authenticated'
|
|
18
|
+
|
|
19
|
+
type AuthData = {
|
|
20
|
+
status: AuthStatus
|
|
21
|
+
tokenSet?: {
|
|
22
|
+
accessToken: string
|
|
23
|
+
tokenType: string
|
|
24
|
+
expiresAt: number
|
|
25
|
+
idToken: string
|
|
26
|
+
refreshToken: string
|
|
27
|
+
scopes: string[]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type SessionContext<TData = Record<string, unknown>> = {
|
|
32
|
+
id: string
|
|
33
|
+
auth: AuthData
|
|
34
|
+
user: AuthorizedUser
|
|
35
|
+
data: TData
|
|
36
|
+
issuedAt: number
|
|
37
|
+
expiresAt: number
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type NuxtAuthOptions = {
|
|
41
|
+
/**
|
|
42
|
+
* Default Devcoffee Nuxt AuthTS option callbacks for session and user mapping.
|
|
43
|
+
* These can be overridden by passing custom callbacks to `NuxtAuthtsHandler()`.
|
|
44
|
+
* @since 1.0.0
|
|
45
|
+
*/
|
|
46
|
+
session: (
|
|
47
|
+
session: Omit<SessionContext, 'auth'>,
|
|
48
|
+
auth: SessionContext['auth']
|
|
49
|
+
) => Awaitable<Omit<SessionContext, 'auth' | 'issuedAt' | 'expiresAt'> & { isAuthenticated: boolean }>
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Maps the OpenID Connect user info response to your local user schema.
|
|
53
|
+
*
|
|
54
|
+
* @param user - The raw user info response from the OpenID provider.
|
|
55
|
+
* @param opts - The token response containing access and ID tokens.
|
|
56
|
+
* @returns A normalized user object.
|
|
57
|
+
* @since 1.0.0
|
|
58
|
+
*/
|
|
59
|
+
userInfo: (
|
|
60
|
+
user: AuthorizedUser,
|
|
61
|
+
opts: {
|
|
62
|
+
openidUser?: OidcUserInfo
|
|
63
|
+
tokenSet: Exclude<SessionContext['auth']['tokenSet'], undefined>
|
|
64
|
+
}
|
|
65
|
+
) => Awaitable<AuthorizedUser>
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
type NuxtSessionContext<TData = Record<string, unknown>> = {
|
|
69
|
+
readonly id: string
|
|
70
|
+
readonly isAuthenticated: boolean
|
|
71
|
+
readonly user: AuthorizedUser
|
|
72
|
+
readonly data: TData
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
type NuxtSessionUpdateContext<TData = Record<string, unknown>> = DeepPartial<{
|
|
76
|
+
user: AuthorizedUser
|
|
77
|
+
data: TData
|
|
78
|
+
}>
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* OpenID Connect configuration options.
|
|
82
|
+
*
|
|
83
|
+
* Defines parameters used for client registration, discovery,
|
|
84
|
+
* and token exchange with the OpenID Provider.
|
|
85
|
+
*
|
|
86
|
+
* @since 1.0.0
|
|
87
|
+
*/
|
|
88
|
+
type OpenIdOptions = {
|
|
89
|
+
/** Client ID registered with the OpenID Provider. */
|
|
90
|
+
clientId: string
|
|
91
|
+
|
|
92
|
+
/** Client secret for token exchange (if applicable). */
|
|
93
|
+
clientSecret: string
|
|
94
|
+
|
|
95
|
+
/** Whether to use Proof Key for Code Exchange (PKCE) flow. */
|
|
96
|
+
usePkce: boolean
|
|
97
|
+
|
|
98
|
+
/** Code challenge method, e.g. `S256`. */
|
|
99
|
+
codeChallengeMethod: string
|
|
100
|
+
|
|
101
|
+
/** Scopes requested from the OpenID Provider. */
|
|
102
|
+
scopes: string[]
|
|
103
|
+
|
|
104
|
+
/** Redirect URI used after authorization. */
|
|
105
|
+
redirectUri: string
|
|
106
|
+
|
|
107
|
+
/** The `.well-known/openid-configuration` discovery URL. */
|
|
108
|
+
wellKnownUrl: string
|
|
109
|
+
|
|
110
|
+
/** Whether to automatically fetch user info after login. */
|
|
111
|
+
autoFetchUser: boolean
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* TTL in seconds for caching autoFetchUser results per session.
|
|
115
|
+
* A GET_SESSION within the TTL window does not trigger an OIDC provider call.
|
|
116
|
+
* Only applies when autoFetchUser is true.
|
|
117
|
+
* @default 300
|
|
118
|
+
* @since 1.0.0
|
|
119
|
+
*/
|
|
120
|
+
autoFetchUserTtl: number
|
|
121
|
+
|
|
122
|
+
/** Whether to fetch user info immediately after token grant. */
|
|
123
|
+
fetchUserOnLogin: boolean
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Window in milliseconds before token expiry at which a refresh is triggered.
|
|
127
|
+
* Tokens with expiresAt more than this many milliseconds in the future are
|
|
128
|
+
* considered fresh and the refresh path is skipped entirely.
|
|
129
|
+
* @default 60000
|
|
130
|
+
* @since 1.0.0
|
|
131
|
+
*/
|
|
132
|
+
tokenRefreshBufferMs: number
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* When `true`, the token refresh mutex uses an atomic Redis NX write
|
|
136
|
+
* (`SET key value NX EX ttl`) via the IORedis native client accessor for
|
|
137
|
+
* true distributed exclusion across multiple server instances. When `false`
|
|
138
|
+
* (default), the existing optimistic lock (`hasItem` + `setItem`) is used.
|
|
139
|
+
*
|
|
140
|
+
* @remarks Only effective when the Nitro `cache` storage mount is backed by
|
|
141
|
+
* a Redis driver. Setting `true` with a non-Redis backend falls through to
|
|
142
|
+
* the optimistic path silently.
|
|
143
|
+
*
|
|
144
|
+
* @default false
|
|
145
|
+
* @since 1.0.0
|
|
146
|
+
*/
|
|
147
|
+
distributedLock?: boolean
|
|
148
|
+
|
|
149
|
+
/** Cache configuration for storing OpenID metadata. */
|
|
150
|
+
cache: {
|
|
151
|
+
/** Cache key prefix for the metadata store. */
|
|
152
|
+
prefix: string
|
|
153
|
+
/** Expiry time in seconds for cached metadata. */
|
|
154
|
+
expires: number
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Session management configuration.
|
|
160
|
+
*
|
|
161
|
+
* Defines session storage, expiration, and cookie behavior
|
|
162
|
+
* for authentication session tracking.
|
|
163
|
+
*
|
|
164
|
+
* @since 1.0.0
|
|
165
|
+
*/
|
|
166
|
+
type SessionsOptions = {
|
|
167
|
+
/** Cookie name mappings used by the session handler. */
|
|
168
|
+
names: {
|
|
169
|
+
/** PKCE verifier cookie name. */
|
|
170
|
+
pkce: string
|
|
171
|
+
/** State cookie name. */
|
|
172
|
+
state: string
|
|
173
|
+
/** Session ID cookie name. */
|
|
174
|
+
sessionId: string
|
|
175
|
+
/** Redirect URL cookie name. */
|
|
176
|
+
redirectUrl: string
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
storage: {
|
|
180
|
+
name: string
|
|
181
|
+
|
|
182
|
+
/** Key prefix for session data in the storage backend. */
|
|
183
|
+
prefix: string
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Session lifetime in seconds.
|
|
188
|
+
* Converted to milliseconds internally for Date.now() expiry calculations.
|
|
189
|
+
* Passed directly as Redis TTL (seconds).
|
|
190
|
+
* @default 518400 (6 days)
|
|
191
|
+
* @since 1.0.0
|
|
192
|
+
*/
|
|
193
|
+
expiresIn: number
|
|
194
|
+
|
|
195
|
+
/** Options for how session cookies are serialized. */
|
|
196
|
+
cookieOpts: CookieSerializeOptions
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Secret used to HMAC-sign session ID cookies and derive the AES-256-GCM key
|
|
200
|
+
* for tokenSet encryption. When empty, signing and encryption are skipped.
|
|
201
|
+
* @since 1.0.0
|
|
202
|
+
*/
|
|
203
|
+
secret?: string
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Authentication behavior configuration.
|
|
208
|
+
*
|
|
209
|
+
* Defines default redirect paths and URIs used for login/logout flow.
|
|
210
|
+
*
|
|
211
|
+
* @since 1.0.0
|
|
212
|
+
*/
|
|
213
|
+
type AuthOptions = {
|
|
214
|
+
/** Path to the login page. */
|
|
215
|
+
loginUri: string
|
|
216
|
+
|
|
217
|
+
/** Default redirect URI after successful login. */
|
|
218
|
+
defaultLoginRedirectUri: string
|
|
219
|
+
|
|
220
|
+
/** Default redirect URI after logout. */
|
|
221
|
+
defaultLogoutRedirectUri: string
|
|
222
|
+
|
|
223
|
+
/** Default anonymous user object. */
|
|
224
|
+
anonymousUser: AuthorizedUser
|
|
225
|
+
|
|
226
|
+
ignoreRegexPatterns: RegExp[]
|
|
227
|
+
|
|
228
|
+
ignoreRegexPatternsDev: RegExp[]
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Main configuration interface for the Nuxt AuthTS module.
|
|
233
|
+
*
|
|
234
|
+
* Combines OpenID Connect, session, and general authentication options.
|
|
235
|
+
*
|
|
236
|
+
* @since 1.0.0
|
|
237
|
+
*/
|
|
238
|
+
type AuthtsModuleOptions = {
|
|
239
|
+
/** Enable or disable the module */
|
|
240
|
+
enabled: boolean
|
|
241
|
+
openid: OpenIdOptions
|
|
242
|
+
sessions: SessionsOptions
|
|
243
|
+
auth: AuthOptions
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
interface AuthtsMiddlewareMeta {
|
|
247
|
+
/** Require authentication to access */
|
|
248
|
+
required?: boolean
|
|
249
|
+
|
|
250
|
+
/** Only accessible if unauthenticated (login/register pages) */
|
|
251
|
+
unauthenticatedOnly?: boolean
|
|
252
|
+
|
|
253
|
+
/** Restrict access to these roles */
|
|
254
|
+
roles?: string[]
|
|
249
255
|
}
|
|
250
256
|
|
|
251
257
|
type CoreLogLevel = LogLevel$1
|
package/dist/module.d.ts
CHANGED
|
@@ -3,249 +3,255 @@ import { CookieSerializeOptions } from '../dist/runtime/server/adapters/http.js'
|
|
|
3
3
|
import { OidcUserInfo } from '../dist/runtime/server/adapters/oidc.js';
|
|
4
4
|
import { LogLevel as LogLevel$1, ConsolaOptions, ConsolaInstance } from 'consola';
|
|
5
5
|
|
|
6
|
-
interface AuthorizedUser {
|
|
7
|
-
id: string
|
|
8
|
-
sub: string
|
|
9
|
-
email: string
|
|
10
|
-
firstName: string
|
|
11
|
-
lastName: string
|
|
12
|
-
locale: string
|
|
13
|
-
language: string
|
|
14
|
-
timezone: string
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
type AuthStatus = 'unauthenticated' | 'authenticated'
|
|
18
|
-
|
|
19
|
-
type AuthData = {
|
|
20
|
-
status: AuthStatus
|
|
21
|
-
tokenSet?: {
|
|
22
|
-
accessToken: string
|
|
23
|
-
tokenType: string
|
|
24
|
-
expiresAt: number
|
|
25
|
-
idToken: string
|
|
26
|
-
refreshToken: string
|
|
27
|
-
scopes: string[]
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
type SessionContext<TData = Record<string, unknown>> = {
|
|
32
|
-
id: string
|
|
33
|
-
auth: AuthData
|
|
34
|
-
user: AuthorizedUser
|
|
35
|
-
data: TData
|
|
36
|
-
issuedAt: number
|
|
37
|
-
expiresAt: number
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
type NuxtAuthOptions = {
|
|
41
|
-
/**
|
|
42
|
-
* Default Devcoffee Nuxt AuthTS option callbacks for session and user mapping.
|
|
43
|
-
* These can be overridden by passing custom callbacks to `NuxtAuthtsHandler()`.
|
|
44
|
-
* @since 1.0.0
|
|
45
|
-
*/
|
|
46
|
-
session: (
|
|
47
|
-
session: Omit<SessionContext, 'auth'>,
|
|
48
|
-
auth: SessionContext['auth']
|
|
49
|
-
) => Awaitable<Omit<SessionContext, 'auth' | 'issuedAt' | 'expiresAt'> & { isAuthenticated: boolean }>
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Maps the OpenID Connect user info response to your local user schema.
|
|
53
|
-
*
|
|
54
|
-
* @param user - The raw user info response from the OpenID provider.
|
|
55
|
-
* @param opts - The token response containing access and ID tokens.
|
|
56
|
-
* @returns A normalized user object.
|
|
57
|
-
* @since 1.0.0
|
|
58
|
-
*/
|
|
59
|
-
userInfo: (
|
|
60
|
-
user: AuthorizedUser,
|
|
61
|
-
opts: {
|
|
62
|
-
openidUser?: OidcUserInfo
|
|
63
|
-
tokenSet: Exclude<SessionContext['auth']['tokenSet'], undefined>
|
|
64
|
-
}
|
|
65
|
-
) => Awaitable<AuthorizedUser>
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
type NuxtSessionContext<TData = Record<string, unknown>> = {
|
|
69
|
-
readonly id: string
|
|
70
|
-
readonly isAuthenticated: boolean
|
|
71
|
-
readonly user: AuthorizedUser
|
|
72
|
-
readonly data: TData
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
type NuxtSessionUpdateContext<TData = Record<string, unknown>> = DeepPartial<{
|
|
76
|
-
user: AuthorizedUser
|
|
77
|
-
data: TData
|
|
78
|
-
}>
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* Defines parameters used for client registration, discovery,
|
|
84
|
-
* and token exchange with the OpenID Provider.
|
|
85
|
-
*
|
|
86
|
-
* @since 1.0.0
|
|
87
|
-
*/
|
|
88
|
-
type OpenIdOptions = {
|
|
89
|
-
/** Client ID registered with the OpenID Provider. */
|
|
90
|
-
clientId: string
|
|
91
|
-
|
|
92
|
-
/** Client secret for token exchange (if applicable). */
|
|
93
|
-
clientSecret: string
|
|
94
|
-
|
|
95
|
-
/** Whether to use Proof Key for Code Exchange (PKCE) flow. */
|
|
96
|
-
usePkce: boolean
|
|
97
|
-
|
|
98
|
-
/** Code challenge method, e.g. `S256`. */
|
|
99
|
-
codeChallengeMethod: string
|
|
100
|
-
|
|
101
|
-
/** Scopes requested from the OpenID Provider. */
|
|
102
|
-
scopes: string[]
|
|
103
|
-
|
|
104
|
-
/** Redirect URI used after authorization. */
|
|
105
|
-
redirectUri: string
|
|
106
|
-
|
|
107
|
-
/** The `.well-known/openid-configuration` discovery URL. */
|
|
108
|
-
wellKnownUrl: string
|
|
109
|
-
|
|
110
|
-
/** Whether to automatically fetch user info after login. */
|
|
111
|
-
autoFetchUser: boolean
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* TTL in seconds for caching autoFetchUser results per session.
|
|
115
|
-
* A GET_SESSION within the TTL window does not trigger an OIDC provider call.
|
|
116
|
-
* Only applies when autoFetchUser is true.
|
|
117
|
-
* @default 300
|
|
118
|
-
* @since 1.0.0
|
|
119
|
-
*/
|
|
120
|
-
autoFetchUserTtl: number
|
|
121
|
-
|
|
122
|
-
/** Whether to fetch user info immediately after token grant. */
|
|
123
|
-
fetchUserOnLogin: boolean
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Window in milliseconds before token expiry at which a refresh is triggered.
|
|
127
|
-
* Tokens with expiresAt more than this many milliseconds in the future are
|
|
128
|
-
* considered fresh and the refresh path is skipped entirely.
|
|
129
|
-
* @default 60000
|
|
130
|
-
* @since 1.0.0
|
|
131
|
-
*/
|
|
132
|
-
tokenRefreshBufferMs: number
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* When `true`, the token refresh mutex uses an atomic Redis NX write
|
|
136
|
-
* (`SET key value NX EX ttl`) via the IORedis native client accessor for
|
|
137
|
-
* true distributed exclusion across multiple server instances. When `false`
|
|
138
|
-
* (default), the existing optimistic lock (`hasItem` + `setItem`) is used.
|
|
139
|
-
*
|
|
140
|
-
* @remarks Only effective when the Nitro `cache` storage mount is backed by
|
|
141
|
-
* a Redis driver. Setting `true` with a non-Redis backend falls through to
|
|
142
|
-
* the optimistic path silently.
|
|
143
|
-
*
|
|
144
|
-
* @default false
|
|
145
|
-
* @since 1.0.0
|
|
146
|
-
*/
|
|
147
|
-
distributedLock?: boolean
|
|
148
|
-
|
|
149
|
-
/** Cache configuration for storing OpenID metadata. */
|
|
150
|
-
cache: {
|
|
151
|
-
/** Cache key prefix for the metadata store. */
|
|
152
|
-
prefix: string
|
|
153
|
-
/** Expiry time in seconds for cached metadata. */
|
|
154
|
-
expires: number
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
* Defines session storage, expiration, and cookie behavior
|
|
162
|
-
* for authentication session tracking.
|
|
163
|
-
*
|
|
164
|
-
* @since 1.0.0
|
|
165
|
-
*/
|
|
166
|
-
type SessionsOptions = {
|
|
167
|
-
/** Cookie name mappings used by the session handler. */
|
|
168
|
-
names: {
|
|
169
|
-
/** PKCE verifier cookie name. */
|
|
170
|
-
pkce: string
|
|
171
|
-
/** State cookie name. */
|
|
172
|
-
state: string
|
|
173
|
-
/** Session ID cookie name. */
|
|
174
|
-
sessionId: string
|
|
175
|
-
/** Redirect URL cookie name. */
|
|
176
|
-
redirectUrl: string
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
storage: {
|
|
180
|
-
name: string
|
|
181
|
-
|
|
182
|
-
/** Key prefix for session data in the storage backend. */
|
|
183
|
-
prefix: string
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
/** Default
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
|
|
6
|
+
interface AuthorizedUser {
|
|
7
|
+
id: string
|
|
8
|
+
sub: string
|
|
9
|
+
email: string
|
|
10
|
+
firstName: string
|
|
11
|
+
lastName: string
|
|
12
|
+
locale: string
|
|
13
|
+
language: string
|
|
14
|
+
timezone: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type AuthStatus = 'unauthenticated' | 'authenticated'
|
|
18
|
+
|
|
19
|
+
type AuthData = {
|
|
20
|
+
status: AuthStatus
|
|
21
|
+
tokenSet?: {
|
|
22
|
+
accessToken: string
|
|
23
|
+
tokenType: string
|
|
24
|
+
expiresAt: number
|
|
25
|
+
idToken: string
|
|
26
|
+
refreshToken: string
|
|
27
|
+
scopes: string[]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type SessionContext<TData = Record<string, unknown>> = {
|
|
32
|
+
id: string
|
|
33
|
+
auth: AuthData
|
|
34
|
+
user: AuthorizedUser
|
|
35
|
+
data: TData
|
|
36
|
+
issuedAt: number
|
|
37
|
+
expiresAt: number
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type NuxtAuthOptions = {
|
|
41
|
+
/**
|
|
42
|
+
* Default Devcoffee Nuxt AuthTS option callbacks for session and user mapping.
|
|
43
|
+
* These can be overridden by passing custom callbacks to `NuxtAuthtsHandler()`.
|
|
44
|
+
* @since 1.0.0
|
|
45
|
+
*/
|
|
46
|
+
session: (
|
|
47
|
+
session: Omit<SessionContext, 'auth'>,
|
|
48
|
+
auth: SessionContext['auth']
|
|
49
|
+
) => Awaitable<Omit<SessionContext, 'auth' | 'issuedAt' | 'expiresAt'> & { isAuthenticated: boolean }>
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Maps the OpenID Connect user info response to your local user schema.
|
|
53
|
+
*
|
|
54
|
+
* @param user - The raw user info response from the OpenID provider.
|
|
55
|
+
* @param opts - The token response containing access and ID tokens.
|
|
56
|
+
* @returns A normalized user object.
|
|
57
|
+
* @since 1.0.0
|
|
58
|
+
*/
|
|
59
|
+
userInfo: (
|
|
60
|
+
user: AuthorizedUser,
|
|
61
|
+
opts: {
|
|
62
|
+
openidUser?: OidcUserInfo
|
|
63
|
+
tokenSet: Exclude<SessionContext['auth']['tokenSet'], undefined>
|
|
64
|
+
}
|
|
65
|
+
) => Awaitable<AuthorizedUser>
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
type NuxtSessionContext<TData = Record<string, unknown>> = {
|
|
69
|
+
readonly id: string
|
|
70
|
+
readonly isAuthenticated: boolean
|
|
71
|
+
readonly user: AuthorizedUser
|
|
72
|
+
readonly data: TData
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
type NuxtSessionUpdateContext<TData = Record<string, unknown>> = DeepPartial<{
|
|
76
|
+
user: AuthorizedUser
|
|
77
|
+
data: TData
|
|
78
|
+
}>
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* OpenID Connect configuration options.
|
|
82
|
+
*
|
|
83
|
+
* Defines parameters used for client registration, discovery,
|
|
84
|
+
* and token exchange with the OpenID Provider.
|
|
85
|
+
*
|
|
86
|
+
* @since 1.0.0
|
|
87
|
+
*/
|
|
88
|
+
type OpenIdOptions = {
|
|
89
|
+
/** Client ID registered with the OpenID Provider. */
|
|
90
|
+
clientId: string
|
|
91
|
+
|
|
92
|
+
/** Client secret for token exchange (if applicable). */
|
|
93
|
+
clientSecret: string
|
|
94
|
+
|
|
95
|
+
/** Whether to use Proof Key for Code Exchange (PKCE) flow. */
|
|
96
|
+
usePkce: boolean
|
|
97
|
+
|
|
98
|
+
/** Code challenge method, e.g. `S256`. */
|
|
99
|
+
codeChallengeMethod: string
|
|
100
|
+
|
|
101
|
+
/** Scopes requested from the OpenID Provider. */
|
|
102
|
+
scopes: string[]
|
|
103
|
+
|
|
104
|
+
/** Redirect URI used after authorization. */
|
|
105
|
+
redirectUri: string
|
|
106
|
+
|
|
107
|
+
/** The `.well-known/openid-configuration` discovery URL. */
|
|
108
|
+
wellKnownUrl: string
|
|
109
|
+
|
|
110
|
+
/** Whether to automatically fetch user info after login. */
|
|
111
|
+
autoFetchUser: boolean
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* TTL in seconds for caching autoFetchUser results per session.
|
|
115
|
+
* A GET_SESSION within the TTL window does not trigger an OIDC provider call.
|
|
116
|
+
* Only applies when autoFetchUser is true.
|
|
117
|
+
* @default 300
|
|
118
|
+
* @since 1.0.0
|
|
119
|
+
*/
|
|
120
|
+
autoFetchUserTtl: number
|
|
121
|
+
|
|
122
|
+
/** Whether to fetch user info immediately after token grant. */
|
|
123
|
+
fetchUserOnLogin: boolean
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Window in milliseconds before token expiry at which a refresh is triggered.
|
|
127
|
+
* Tokens with expiresAt more than this many milliseconds in the future are
|
|
128
|
+
* considered fresh and the refresh path is skipped entirely.
|
|
129
|
+
* @default 60000
|
|
130
|
+
* @since 1.0.0
|
|
131
|
+
*/
|
|
132
|
+
tokenRefreshBufferMs: number
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* When `true`, the token refresh mutex uses an atomic Redis NX write
|
|
136
|
+
* (`SET key value NX EX ttl`) via the IORedis native client accessor for
|
|
137
|
+
* true distributed exclusion across multiple server instances. When `false`
|
|
138
|
+
* (default), the existing optimistic lock (`hasItem` + `setItem`) is used.
|
|
139
|
+
*
|
|
140
|
+
* @remarks Only effective when the Nitro `cache` storage mount is backed by
|
|
141
|
+
* a Redis driver. Setting `true` with a non-Redis backend falls through to
|
|
142
|
+
* the optimistic path silently.
|
|
143
|
+
*
|
|
144
|
+
* @default false
|
|
145
|
+
* @since 1.0.0
|
|
146
|
+
*/
|
|
147
|
+
distributedLock?: boolean
|
|
148
|
+
|
|
149
|
+
/** Cache configuration for storing OpenID metadata. */
|
|
150
|
+
cache: {
|
|
151
|
+
/** Cache key prefix for the metadata store. */
|
|
152
|
+
prefix: string
|
|
153
|
+
/** Expiry time in seconds for cached metadata. */
|
|
154
|
+
expires: number
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Session management configuration.
|
|
160
|
+
*
|
|
161
|
+
* Defines session storage, expiration, and cookie behavior
|
|
162
|
+
* for authentication session tracking.
|
|
163
|
+
*
|
|
164
|
+
* @since 1.0.0
|
|
165
|
+
*/
|
|
166
|
+
type SessionsOptions = {
|
|
167
|
+
/** Cookie name mappings used by the session handler. */
|
|
168
|
+
names: {
|
|
169
|
+
/** PKCE verifier cookie name. */
|
|
170
|
+
pkce: string
|
|
171
|
+
/** State cookie name. */
|
|
172
|
+
state: string
|
|
173
|
+
/** Session ID cookie name. */
|
|
174
|
+
sessionId: string
|
|
175
|
+
/** Redirect URL cookie name. */
|
|
176
|
+
redirectUrl: string
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
storage: {
|
|
180
|
+
name: string
|
|
181
|
+
|
|
182
|
+
/** Key prefix for session data in the storage backend. */
|
|
183
|
+
prefix: string
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Session lifetime in seconds.
|
|
188
|
+
* Converted to milliseconds internally for Date.now() expiry calculations.
|
|
189
|
+
* Passed directly as Redis TTL (seconds).
|
|
190
|
+
* @default 518400 (6 days)
|
|
191
|
+
* @since 1.0.0
|
|
192
|
+
*/
|
|
193
|
+
expiresIn: number
|
|
194
|
+
|
|
195
|
+
/** Options for how session cookies are serialized. */
|
|
196
|
+
cookieOpts: CookieSerializeOptions
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Secret used to HMAC-sign session ID cookies and derive the AES-256-GCM key
|
|
200
|
+
* for tokenSet encryption. When empty, signing and encryption are skipped.
|
|
201
|
+
* @since 1.0.0
|
|
202
|
+
*/
|
|
203
|
+
secret?: string
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Authentication behavior configuration.
|
|
208
|
+
*
|
|
209
|
+
* Defines default redirect paths and URIs used for login/logout flow.
|
|
210
|
+
*
|
|
211
|
+
* @since 1.0.0
|
|
212
|
+
*/
|
|
213
|
+
type AuthOptions = {
|
|
214
|
+
/** Path to the login page. */
|
|
215
|
+
loginUri: string
|
|
216
|
+
|
|
217
|
+
/** Default redirect URI after successful login. */
|
|
218
|
+
defaultLoginRedirectUri: string
|
|
219
|
+
|
|
220
|
+
/** Default redirect URI after logout. */
|
|
221
|
+
defaultLogoutRedirectUri: string
|
|
222
|
+
|
|
223
|
+
/** Default anonymous user object. */
|
|
224
|
+
anonymousUser: AuthorizedUser
|
|
225
|
+
|
|
226
|
+
ignoreRegexPatterns: RegExp[]
|
|
227
|
+
|
|
228
|
+
ignoreRegexPatternsDev: RegExp[]
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Main configuration interface for the Nuxt AuthTS module.
|
|
233
|
+
*
|
|
234
|
+
* Combines OpenID Connect, session, and general authentication options.
|
|
235
|
+
*
|
|
236
|
+
* @since 1.0.0
|
|
237
|
+
*/
|
|
238
|
+
type AuthtsModuleOptions = {
|
|
239
|
+
/** Enable or disable the module */
|
|
240
|
+
enabled: boolean
|
|
241
|
+
openid: OpenIdOptions
|
|
242
|
+
sessions: SessionsOptions
|
|
243
|
+
auth: AuthOptions
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
interface AuthtsMiddlewareMeta {
|
|
247
|
+
/** Require authentication to access */
|
|
248
|
+
required?: boolean
|
|
249
|
+
|
|
250
|
+
/** Only accessible if unauthenticated (login/register pages) */
|
|
251
|
+
unauthenticatedOnly?: boolean
|
|
252
|
+
|
|
253
|
+
/** Restrict access to these roles */
|
|
254
|
+
roles?: string[]
|
|
249
255
|
}
|
|
250
256
|
|
|
251
257
|
type CoreLogLevel = LogLevel$1
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { addCustomTab } from '@nuxt/devtools-kit';
|
|
|
2
2
|
import { defineNuxtModule, useLogger, createResolver, addTemplate, addServerImports, addServerImportsDir, addServerPlugin, addImportsDir, addPlugin, addRouteMiddleware, addServerHandler } from '@nuxt/kit';
|
|
3
3
|
import { deepMerge, pick } from '../dist/runtime/utils.js';
|
|
4
4
|
|
|
5
|
-
const version = "1.
|
|
5
|
+
const version = "1.4.1";
|
|
6
6
|
|
|
7
7
|
const defaultLocale = "vi-VN";
|
|
8
8
|
const defaultLanguage = "vi";
|
|
@@ -50,7 +50,7 @@ const authtsDefaults = {
|
|
|
50
50
|
},
|
|
51
51
|
storage: { name: "sessions", prefix: "session" },
|
|
52
52
|
expiresIn: 60 * 60 * 24 * 6,
|
|
53
|
-
// 6 days
|
|
53
|
+
// 6 days in seconds
|
|
54
54
|
cookieOpts: {
|
|
55
55
|
path: "/",
|
|
56
56
|
sameSite: "lax",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SessionContext } from '@devcoffee/nuxt-core';
|
|
2
|
+
import type { CookieSerializeOptions, H3Event } from '#devcoffee-core/server/adapters/http';
|
|
2
3
|
import type { ClientMetadata, ServerMetadata, TokenEndpointResponse } from '#devcoffee-core/server/adapters/oidc';
|
|
3
4
|
/** Options used for session creation and validation. */
|
|
4
5
|
type SessionCreateOptions = {
|
|
@@ -19,6 +20,28 @@ type SessionCreateOptions = {
|
|
|
19
20
|
* @since 1.0.0
|
|
20
21
|
*/
|
|
21
22
|
export declare function isSameOrigin(redirectUrl: string, requestUrl: URL): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Write the session cookie to the response.
|
|
25
|
+
*
|
|
26
|
+
* Shared by the `beforeResponse` Nitro hook (buffered responses) and the
|
|
27
|
+
* `proxyRequest` `onResponse` callback (streaming proxy responses). Calling
|
|
28
|
+
* it in both places ensures the cookie is always refreshed regardless of
|
|
29
|
+
* whether `headersSent` is true by the time `beforeResponse` fires.
|
|
30
|
+
*
|
|
31
|
+
* @param event - The H3 event for the current request.
|
|
32
|
+
* @param session - The resolved session context to encode into the cookie.
|
|
33
|
+
* @param sessionsConfig - Session cookie options from `nuxtCore.authts.sessions`.
|
|
34
|
+
* @since 1.3.3
|
|
35
|
+
*/
|
|
36
|
+
export declare function writeSessionCookie(event: H3Event, session: SessionContext, sessionsConfig: {
|
|
37
|
+
cookieOpts: Omit<CookieSerializeOptions, 'sameSite'> & {
|
|
38
|
+
sameSite?: string | boolean;
|
|
39
|
+
};
|
|
40
|
+
secret?: string;
|
|
41
|
+
names: {
|
|
42
|
+
sessionId: string;
|
|
43
|
+
};
|
|
44
|
+
}): void;
|
|
22
45
|
/**
|
|
23
46
|
* Retrieve an existing session from storage.
|
|
24
47
|
*
|
|
@@ -48,7 +71,7 @@ export declare function validateSession(sessionCookieId: string | undefined, opt
|
|
|
48
71
|
*
|
|
49
72
|
* @param sessionId - The session ID.
|
|
50
73
|
* @param input - Partial update fields for the session (excluding ID, issuedAt, expiresAt).
|
|
51
|
-
* @param opts - Expiration configuration (in
|
|
74
|
+
* @param opts - Expiration configuration (expiresIn in seconds).
|
|
52
75
|
* @returns The updated {@link SessionContext}.
|
|
53
76
|
* @throws {Error} If session cannot be found in storage.
|
|
54
77
|
* @since 1.0.0
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createError } from "#devcoffee-core/server/adapters/http";
|
|
1
|
+
import { createError, setCookie } from "#devcoffee-core/server/adapters/http";
|
|
2
2
|
import {
|
|
3
3
|
allowInsecureRequests,
|
|
4
4
|
authorizationCodeGrant as authorizationCodeGrantOidc,
|
|
@@ -22,7 +22,14 @@ import { deepMerge, omit } from "#devcoffee-core/server/adapters/utils";
|
|
|
22
22
|
import useServerLogger from "#devcoffee-core/server/composables/useServerLogger";
|
|
23
23
|
import { useRuntimeConfig } from "#imports";
|
|
24
24
|
import { useStorage } from "nitropack/runtime";
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
decryptTokenSet,
|
|
27
|
+
encryptTokenSet,
|
|
28
|
+
generateSessionId,
|
|
29
|
+
isValidSessionId,
|
|
30
|
+
signSessionId,
|
|
31
|
+
verifySessionId
|
|
32
|
+
} from "./crypto.js";
|
|
26
33
|
import { tryAcquireLock } from "./mutex.js";
|
|
27
34
|
function getAnonymousUser(extras) {
|
|
28
35
|
const anonymous = useRuntimeConfig().nuxtCore.authts.auth.anonymousUser;
|
|
@@ -56,6 +63,18 @@ export function isSameOrigin(redirectUrl, requestUrl) {
|
|
|
56
63
|
return false;
|
|
57
64
|
}
|
|
58
65
|
}
|
|
66
|
+
export function writeSessionCookie(event, session, sessionsConfig) {
|
|
67
|
+
const {
|
|
68
|
+
cookieOpts,
|
|
69
|
+
secret = "",
|
|
70
|
+
names: { sessionId: cookieName }
|
|
71
|
+
} = sessionsConfig;
|
|
72
|
+
const cookieValue = secret ? signSessionId(session.id, secret) : session.id;
|
|
73
|
+
setCookie(event, cookieName, cookieValue, {
|
|
74
|
+
...cookieOpts,
|
|
75
|
+
expires: new Date(session.expiresAt)
|
|
76
|
+
});
|
|
77
|
+
}
|
|
59
78
|
export async function getSession(sessionId, opts) {
|
|
60
79
|
const sessingKey = getSessionStorageKey(opts.storagePrefix, sessionId);
|
|
61
80
|
if (!await hasSessionData(opts.storageName, sessingKey)) return null;
|
|
@@ -87,7 +106,7 @@ function newSession(sessionId, expiresAt) {
|
|
|
87
106
|
}
|
|
88
107
|
export async function validateSession(sessionCookieId, opts) {
|
|
89
108
|
const now = Date.now();
|
|
90
|
-
const expiresAt = now + opts.expiresIn;
|
|
109
|
+
const expiresAt = now + opts.expiresIn * 1e3;
|
|
91
110
|
let sessionId = void 0;
|
|
92
111
|
let sessionKey = void 0;
|
|
93
112
|
let deleteSessionKey = void 0;
|
|
@@ -114,7 +133,7 @@ export async function validateSession(sessionCookieId, opts) {
|
|
|
114
133
|
if (deleteSessionKey && deleteSessionKey !== sessionKey) {
|
|
115
134
|
await removeSessionData(opts.storageName, deleteSessionKey);
|
|
116
135
|
}
|
|
117
|
-
await setSessionData(opts.storageName, sessionKey, session, opts.expiresIn
|
|
136
|
+
await setSessionData(opts.storageName, sessionKey, session, opts.expiresIn);
|
|
118
137
|
if (session.auth?.tokenSet && session.auth.tokenSet.encrypted === true) {
|
|
119
138
|
if (opts.secret) {
|
|
120
139
|
const decrypted = decryptTokenSet(session.auth.tokenSet, opts.secret);
|
|
@@ -141,7 +160,7 @@ export async function updateSession(sessionId, input, opts) {
|
|
|
141
160
|
});
|
|
142
161
|
}
|
|
143
162
|
session = deepMerge({}, session, normalizedInput);
|
|
144
|
-
session.expiresAt = now + opts.expiresIn;
|
|
163
|
+
session.expiresAt = now + opts.expiresIn * 1e3;
|
|
145
164
|
const sessionToStore = { ...session };
|
|
146
165
|
if (opts.secret && sessionToStore.auth?.tokenSet) {
|
|
147
166
|
sessionToStore.auth = {
|
|
@@ -152,7 +171,7 @@ export async function updateSession(sessionId, input, opts) {
|
|
|
152
171
|
)
|
|
153
172
|
};
|
|
154
173
|
}
|
|
155
|
-
await setSessionData(opts.storageName, serverKey, sessionToStore, opts.expiresIn
|
|
174
|
+
await setSessionData(opts.storageName, serverKey, sessionToStore, opts.expiresIn);
|
|
156
175
|
return session;
|
|
157
176
|
}
|
|
158
177
|
export async function renewSession(sessionId, opts) {
|
|
@@ -160,9 +179,9 @@ export async function renewSession(sessionId, opts) {
|
|
|
160
179
|
if (await hasSessionData(opts.storageName, sessionKey)) {
|
|
161
180
|
await removeSessionData(opts.storageName, sessionKey);
|
|
162
181
|
}
|
|
163
|
-
const session = newSession(generateSessionId(), Date.now() + opts.expiresIn);
|
|
182
|
+
const session = newSession(generateSessionId(), Date.now() + opts.expiresIn * 1e3);
|
|
164
183
|
sessionKey = getSessionStorageKey(opts.storagePrefix, session.id);
|
|
165
|
-
await setSessionData(opts.storageName, sessionKey, session, opts.expiresIn
|
|
184
|
+
await setSessionData(opts.storageName, sessionKey, session, opts.expiresIn);
|
|
166
185
|
return session;
|
|
167
186
|
}
|
|
168
187
|
export async function deleteSession(sessionId, opts) {
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "#devcoffee-core/server/adapters/http";
|
|
8
8
|
import { deepMerge } from "#devcoffee-core/server/adapters/utils";
|
|
9
9
|
import useServerLogger from "#devcoffee-core/server/composables/useServerLogger";
|
|
10
|
-
import { getOpenIdConfiguration } from "./helpers.js";
|
|
10
|
+
import { getOpenIdConfiguration, writeSessionCookie } from "./helpers.js";
|
|
11
11
|
const defaultOpts = {
|
|
12
12
|
logLevel: 2,
|
|
13
13
|
proxyPrefix: ""
|
|
@@ -51,6 +51,15 @@ export default function NuxtForwardRequestHandler(opts) {
|
|
|
51
51
|
};
|
|
52
52
|
logger.info(`from '${event.path}' to '${defaultForwardInit.forwardUrl}' - auth stt='${session?.auth?.status}'`);
|
|
53
53
|
const { forwardUrl, ...proxyOption } = await onBeforeRequest?.(defaultForwardInit, params) || defaultForwardInit;
|
|
54
|
-
|
|
54
|
+
const { sessions } = useRuntimeConfig(event).nuxtCore.authts;
|
|
55
|
+
await proxyRequest(event, forwardUrl, {
|
|
56
|
+
...proxyOption,
|
|
57
|
+
onResponse(proxyEvent, _upstreamResponse) {
|
|
58
|
+
const session2 = proxyEvent.context.session;
|
|
59
|
+
if (session2) {
|
|
60
|
+
writeSessionCookie(proxyEvent, session2, sessions);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
55
64
|
});
|
|
56
65
|
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import { defineNitroPlugin, getCookie,
|
|
1
|
+
import { defineNitroPlugin, getCookie, useRuntimeConfig } from "#devcoffee-core/server/adapters/http";
|
|
2
2
|
import useServerLogger from "#devcoffee-core/server/composables/useServerLogger";
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
refreshTokenIfNeeded,
|
|
5
|
+
updateSession,
|
|
6
|
+
validateSession,
|
|
7
|
+
writeSessionCookie
|
|
8
|
+
} from "#devcoffee-core/server/core/helpers";
|
|
5
9
|
import { useNitroApp, useStorage } from "nitropack/runtime";
|
|
6
10
|
export default defineNitroPlugin((nitroApp) => {
|
|
7
11
|
nitroApp.hooks.hook("request", async (event) => {
|
|
@@ -65,18 +69,10 @@ export default defineNitroPlugin((nitroApp) => {
|
|
|
65
69
|
event.context.session = session;
|
|
66
70
|
});
|
|
67
71
|
nitroApp.hooks.hook("beforeResponse", async (event) => {
|
|
68
|
-
|
|
69
|
-
cookieOpts,
|
|
70
|
-
secret = "",
|
|
71
|
-
names: { sessionId: cookieName }
|
|
72
|
-
} = useRuntimeConfig(event).nuxtCore.authts.sessions;
|
|
72
|
+
if (event.node.res.headersSent) return;
|
|
73
73
|
const session = event.context.session;
|
|
74
74
|
if (session) {
|
|
75
|
-
|
|
76
|
-
setCookie(event, cookieName, cookieValue, {
|
|
77
|
-
...cookieOpts,
|
|
78
|
-
expires: new Date(session.expiresAt)
|
|
79
|
-
});
|
|
75
|
+
writeSessionCookie(event, session, useRuntimeConfig(event).nuxtCore.authts.sessions);
|
|
80
76
|
}
|
|
81
77
|
});
|
|
82
78
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devcoffee/nuxt-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"cleanup": "nuxi cleanup && nuxi cleanup playground",
|
|
54
54
|
"dev:build": "nuxi build playground",
|
|
55
55
|
"prepack": "nuxt-module-build build",
|
|
56
|
-
"release": "npm run lint && npm run test:all && npm run prepack &&
|
|
56
|
+
"release": "npm run lint && npm run test:all && npm run prepack && npm publish && git push --follow-tags",
|
|
57
57
|
"lint": "eslint",
|
|
58
58
|
"lint:fix": "eslint --fix",
|
|
59
59
|
"test": "cross-env NODE_OPTIONS=--no-deprecation vitest run test/unit",
|