nuxt-auth-utils 0.3.7 → 0.3.9

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 CHANGED
@@ -15,9 +15,9 @@ Add Authentication to Nuxt applications with secured & sealed cookies sessions.
15
15
  ## Features
16
16
 
17
17
  - [Hybrid Rendering](#hybrid-rendering) support (SSR / CSR / SWR / Prerendering)
18
- - [15+ OAuth Providers](#supported-oauth-providers)
19
- - [Vue composable](#vue-composable)
20
- - [Server utils](#server-utils)
18
+ - [20+ OAuth Providers](#supported-oauth-providers)
19
+ - [`useUserSession()` Vue composable](#vue-composable)
20
+ - [Tree-shakable server utils](#server-utils)
21
21
  - [`<AuthState>` component](#authstate-component)
22
22
  - [Extendable with hooks](#extend-session)
23
23
 
@@ -105,6 +105,9 @@ interface UserSessionComposable {
105
105
  }
106
106
  ```
107
107
 
108
+ > [!IMPORTANT]
109
+ > Nuxt Auth Utils uses the `/api/_auth/session` route for session management. Ensure your API route middleware doesn't interfere with this path.
110
+
108
111
  ## Server Utils
109
112
 
110
113
  The following helpers are auto-imported in your `server/` directory.
@@ -114,13 +117,18 @@ The following helpers are auto-imported in your `server/` directory.
114
117
  ```ts
115
118
  // Set a user session, note that this data is encrypted in the cookie but can be decrypted with an API call
116
119
  // Only store the data that allow you to recognize a user, but do not store sensitive data
117
- // Merges new data with existing data using defu()
120
+ // Merges new data with existing data using unjs/defu library
118
121
  await setUserSession(event, {
122
+ // User data
119
123
  user: {
120
- // ... user data
124
+ login: 'atinux'
125
+ },
126
+ // Private data accessible on server/ routes
127
+ secure: {
128
+ apiToken: '1234567890'
121
129
  },
130
+ // Any extra fields for the session data
122
131
  loggedInAt: new Date()
123
- // Any extra fields
124
132
  })
125
133
 
126
134
  // Replace a user session. Same behaviour as setUserSession, except it does not merge data with existing data
@@ -148,11 +156,18 @@ declare module '#auth-utils' {
148
156
  interface UserSession {
149
157
  // Add your own fields
150
158
  }
159
+
160
+ interface SecureSessionData {
161
+ // Add your own fields
162
+ }
151
163
  }
152
164
 
153
165
  export {}
154
166
  ```
155
167
 
168
+ > [!IMPORTANT]
169
+ > Since we encrypt and store session data in cookies, we're constrained by the 4096-byte cookie size limit. Store only essential information.
170
+
156
171
  ### OAuth Event Handlers
157
172
 
158
173
  All handlers can be auto-imported and used in your server routes or API routes.
@@ -192,7 +207,9 @@ It can also be set using environment variables:
192
207
  - Discord
193
208
  - Facebook
194
209
  - GitHub
210
+ - GitLab
195
211
  - Google
212
+ - Instagram
196
213
  - Keycloak
197
214
  - LinkedIn
198
215
  - Microsoft
@@ -201,6 +218,7 @@ It can also be set using environment variables:
201
218
  - Steam
202
219
  - TikTok
203
220
  - Twitch
221
+ - VK
204
222
  - X (Twitter)
205
223
  - XSUAA
206
224
  - Yandex
@@ -350,6 +368,14 @@ Our defaults are:
350
368
  }
351
369
  ```
352
370
 
371
+ You can also overwrite the session config by passing it as 3rd argument of the `setUserSession` and `replaceUserSession` functions:
372
+
373
+ ```ts
374
+ await setUserSession(event, { ... } , {
375
+ maxAge: 60 * 60 * 24 * 7 // 1 week
376
+ })
377
+ ```
378
+
353
379
  Checkout the [`SessionConfig`](https://github.com/unjs/h3/blob/c04c458810e34eb15c1647e1369e7d7ef19f567d/src/utils/session.ts#L20) for all options.
354
380
 
355
381
  ## More
package/dist/module.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "auth-utils",
3
3
  "configKey": "auth",
4
- "version": "0.3.7",
4
+ "version": "0.3.9",
5
5
  "builder": {
6
- "@nuxt/module-builder": "0.8.3",
6
+ "@nuxt/module-builder": "0.8.4",
7
7
  "unbuild": "2.0.0"
8
8
  }
9
9
  }
package/dist/module.mjs CHANGED
@@ -13,7 +13,9 @@ const module = defineNuxtModule({
13
13
  defaults: {},
14
14
  async setup(options, nuxt) {
15
15
  const resolver = createResolver(import.meta.url);
16
- nuxt.options.alias["#auth-utils"] = resolver.resolve("./runtime/types/index");
16
+ nuxt.options.alias["#auth-utils"] = resolver.resolve(
17
+ "./runtime/types/index"
18
+ );
17
19
  addComponentsDir({ path: resolver.resolve("./runtime/app/components") });
18
20
  addImportsDir(resolver.resolve("./runtime/app/composables"));
19
21
  addPlugin(resolver.resolve("./runtime/app/plugins/session.server"));
@@ -58,6 +60,11 @@ const module = defineNuxtModule({
58
60
  clientSecret: "",
59
61
  redirectURL: ""
60
62
  });
63
+ runtimeConfig.oauth.gitlab = defu(runtimeConfig.oauth.gitlab, {
64
+ clientId: "",
65
+ clientSecret: "",
66
+ redirectURL: ""
67
+ });
61
68
  runtimeConfig.oauth.spotify = defu(runtimeConfig.oauth.spotify, {
62
69
  clientId: "",
63
70
  clientSecret: "",
@@ -124,6 +131,11 @@ const module = defineNuxtModule({
124
131
  clientSecret: "",
125
132
  redirectURL: ""
126
133
  });
134
+ runtimeConfig.oauth.instagram = defu(runtimeConfig.oauth.instagram, {
135
+ clientId: "",
136
+ clientSecret: "",
137
+ redirectURL: ""
138
+ });
127
139
  runtimeConfig.oauth.paypal = defu(runtimeConfig.oauth.paypal, {
128
140
  clientId: "",
129
141
  clientSecret: "",
@@ -144,6 +156,11 @@ const module = defineNuxtModule({
144
156
  domain: "",
145
157
  redirectURL: ""
146
158
  });
159
+ runtimeConfig.oauth.vk = defu(runtimeConfig.oauth.vk, {
160
+ clientId: "",
161
+ clientSecret: "",
162
+ redirectURL: ""
163
+ });
147
164
  runtimeConfig.oauth.yandex = defu(runtimeConfig.oauth.yandex, {
148
165
  clientId: "",
149
166
  clientSecret: "",
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -1,2 +1,5 @@
1
- declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<import("#auth-utils").UserSession>>;
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
2
+ [key: string]: unknown;
3
+ user?: import("#auth-utils").User;
4
+ }>>;
2
5
  export default _default;
@@ -5,5 +5,6 @@ export default eventHandler(async (event) => {
5
5
  if (session.user) {
6
6
  await sessionHooks.callHookParallel("fetch", session, event);
7
7
  }
8
- return session;
8
+ const { secure, ...data } = session;
9
+ return data;
9
10
  });
@@ -0,0 +1,47 @@
1
+ import type { OAuthConfig } from '#auth-utils';
2
+ export interface OAuthGitLabConfig {
3
+ /**
4
+ * GitLab OAuth Client ID
5
+ * @default process.env.NUXT_OAUTH_GITLAB_CLIENT_ID
6
+ */
7
+ clientId?: string;
8
+ /**
9
+ * GitLab OAuth Client Secret
10
+ * @default process.env.NUXT_OAUTH_GITLAB_CLIENT_SECRET
11
+ */
12
+ clientSecret?: string;
13
+ /**
14
+ * GitLab OAuth Scope
15
+ * @default ['read_user']
16
+ * @see https://docs.gitlab.com/ee/integration/oauth_provider.html#view-all-authorized-applications
17
+ * @example ['read_user']
18
+ */
19
+ scope?: string[];
20
+ /**
21
+ * Require email from user, adds the ['email'] scope if not present
22
+ * @default false
23
+ */
24
+ emailRequired?: boolean;
25
+ /**
26
+ * GitLab OAuth Authorization URL
27
+ * @default 'https://gitlab.com/oauth/authorize'
28
+ */
29
+ authorizationURL?: string;
30
+ /**
31
+ * GitLab OAuth Token URL
32
+ * @default 'https://gitlab.com/oauth/token'
33
+ */
34
+ tokenURL?: string;
35
+ /**
36
+ * Extra authorization parameters to provide to the authorization URL
37
+ * @see https://docs.gitlab.com/ee/integration/oauth_provider.html#view-all-authorized-applications
38
+ * @example { allow_signup: 'true' }
39
+ */
40
+ authorizationParams?: Record<string, string>;
41
+ /**
42
+ * Redirect URL to to allow overriding for situations like prod failing to determine public hostname
43
+ * @default process.env.NUXT_OAUTH_GITLAB_REDIRECT_URL
44
+ */
45
+ redirectURL?: string;
46
+ }
47
+ export declare function oauthGitLabEventHandler({ config, onSuccess, onError, }: OAuthConfig<OAuthGitLabConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
@@ -0,0 +1,83 @@
1
+ import { eventHandler, getQuery, sendRedirect } from "h3";
2
+ import { withQuery } from "ufo";
3
+ import { defu } from "defu";
4
+ import {
5
+ handleMissingConfiguration,
6
+ handleAccessTokenErrorResponse,
7
+ getOAuthRedirectURL,
8
+ requestAccessToken
9
+ } from "../utils.js";
10
+ import { useRuntimeConfig, createError } from "#imports";
11
+ export function oauthGitLabEventHandler({
12
+ config,
13
+ onSuccess,
14
+ onError
15
+ }) {
16
+ return eventHandler(async (event) => {
17
+ config = defu(config, useRuntimeConfig(event).oauth?.gitlab, {
18
+ authorizationURL: "https://gitlab.com/oauth/authorize",
19
+ tokenURL: "https://gitlab.com/oauth/token",
20
+ authorizationParams: {}
21
+ });
22
+ const query = getQuery(event);
23
+ if (query.error) {
24
+ const error = createError({
25
+ statusCode: 401,
26
+ message: `GitLab login failed: ${query.error || "Unknown error"}`,
27
+ data: query
28
+ });
29
+ if (!onError) throw error;
30
+ return onError(event, error);
31
+ }
32
+ if (!config.clientId || !config.clientSecret) {
33
+ return handleMissingConfiguration(
34
+ event,
35
+ "gitlab",
36
+ ["clientId", "clientSecret"],
37
+ onError
38
+ );
39
+ }
40
+ const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
41
+ if (!query.code) {
42
+ config.scope = config.scope || [];
43
+ if (!config.scope.length) {
44
+ config.scope.push("read_user");
45
+ }
46
+ if (config.emailRequired && !config.scope.includes("email")) {
47
+ config.scope.push("email");
48
+ }
49
+ return sendRedirect(
50
+ event,
51
+ withQuery(config.authorizationURL, {
52
+ response_type: "code",
53
+ client_id: config.clientId,
54
+ redirect_uri: redirectURL,
55
+ scope: config.scope.join(" "),
56
+ ...config.authorizationParams
57
+ })
58
+ );
59
+ }
60
+ const tokens = await requestAccessToken(config.tokenURL, {
61
+ body: {
62
+ grant_type: "authorization_code",
63
+ client_id: config.clientId,
64
+ client_secret: config.clientSecret,
65
+ redirect_uri: redirectURL,
66
+ code: query.code
67
+ }
68
+ });
69
+ if (tokens.error) {
70
+ return handleAccessTokenErrorResponse(event, "gitlab", tokens, onError);
71
+ }
72
+ const accessToken = tokens.access_token;
73
+ const user = await $fetch("https://gitlab.com/api/v4/user", {
74
+ headers: {
75
+ Authorization: `Bearer ${accessToken}`
76
+ }
77
+ });
78
+ return onSuccess(event, {
79
+ user,
80
+ tokens
81
+ });
82
+ });
83
+ }
@@ -0,0 +1,48 @@
1
+ import type { OAuthConfig } from '#auth-utils';
2
+ export interface OAuthInstagramConfig {
3
+ /**
4
+ * Instagram OAuth Client ID
5
+ * @default process.env.NUXT_OAUTH_INSTAGRAM_CLIENT_ID
6
+ */
7
+ clientId?: string;
8
+ /**
9
+ * Instagram OAuth Client Secret
10
+ * @default process.env.NUXT_OAUTH_INSTAGRAM_CLIENT_SECRET
11
+ */
12
+ clientSecret?: string;
13
+ /**
14
+ * Instagram OAuth Scope
15
+ * @default [ 'user_profile' ]
16
+ * @see https://developers.facebook.com/docs/instagram-basic-display-api/overview#permissions
17
+ * @example [ 'user_profile', 'user_media' ],
18
+ */
19
+ scope?: string[];
20
+ /**
21
+ * Instagram OAuth User Fields
22
+ * @default [ 'id', 'username'],
23
+ * @see https://developers.facebook.com/docs/instagram-basic-display-api/reference/user#fields
24
+ * @example [ 'id', 'username', 'account_type', 'media_count' ],
25
+ */
26
+ fields?: string[];
27
+ /**
28
+ * Instagram OAuth Authorization URL
29
+ * @default 'https://api.instagram.com/oauth/authorize'
30
+ */
31
+ authorizationURL?: string;
32
+ /**
33
+ * Instagram OAuth Token URL
34
+ * @default 'https://api.instagram.com/oauth/access_token'
35
+ */
36
+ tokenURL?: string;
37
+ /**
38
+ * Extra authorization parameters to provide to the authorization URL
39
+ * @see https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow/
40
+ */
41
+ authorizationParams?: Record<string, string>;
42
+ /**
43
+ * Redirect URL to to allow overriding for situations like prod failing to determine public hostname
44
+ * @default process.env.NUXT_OAUTH_INSTAGRAM_REDIRECT_URL or current URL
45
+ */
46
+ redirectURL?: string;
47
+ }
48
+ export declare function oauthInstagramEventHandler({ config, onSuccess, onError, }: OAuthConfig<OAuthInstagramConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
@@ -0,0 +1,70 @@
1
+ import { eventHandler, getQuery, sendRedirect } from "h3";
2
+ import { withQuery } from "ufo";
3
+ import { defu } from "defu";
4
+ import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
5
+ import { useRuntimeConfig, createError } from "#imports";
6
+ export function oauthInstagramEventHandler({
7
+ config,
8
+ onSuccess,
9
+ onError
10
+ }) {
11
+ return eventHandler(async (event) => {
12
+ config = defu(config, useRuntimeConfig(event).oauth?.instagram, {
13
+ scope: ["user_profile"],
14
+ authorizationURL: "https://api.instagram.com/oauth/authorize",
15
+ tokenURL: "https://api.instagram.com/oauth/access_token",
16
+ authorizationParams: {}
17
+ });
18
+ const query = getQuery(event);
19
+ if (query.error) {
20
+ const error = createError({
21
+ statusCode: 401,
22
+ message: `Instagram login failed: ${query.error || "Unknown error"}`,
23
+ data: query
24
+ });
25
+ if (!onError) throw error;
26
+ return onError(event, error);
27
+ }
28
+ if (!config.clientId) {
29
+ return handleMissingConfiguration(event, "instagram", ["clientId"], onError);
30
+ }
31
+ const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
32
+ if (!query.code) {
33
+ config.scope = config.scope || [];
34
+ return sendRedirect(
35
+ event,
36
+ withQuery(config.authorizationURL, {
37
+ client_id: config.clientId,
38
+ redirect_uri: redirectURL,
39
+ scope: config.scope.join(" "),
40
+ response_type: "code"
41
+ })
42
+ );
43
+ }
44
+ const tokens = await requestAccessToken(config.tokenURL, {
45
+ body: {
46
+ client_id: config.clientId,
47
+ client_secret: config.clientSecret,
48
+ grant_type: "authorization_code",
49
+ redirect_uri: redirectURL,
50
+ code: query.code
51
+ }
52
+ });
53
+ if (tokens.error) {
54
+ return handleAccessTokenErrorResponse(event, "instagram", tokens, onError);
55
+ }
56
+ const accessToken = tokens.access_token;
57
+ config.fields = config.fields || ["id", "username"];
58
+ const fields = config.fields.join(",");
59
+ const user = await $fetch(
60
+ `https://graph.instagram.com/v20.0/me?fields=${fields}&access_token=${accessToken}`
61
+ );
62
+ if (!user) {
63
+ throw new Error("Instagram login failed: no user found");
64
+ }
65
+ return onSuccess(event, {
66
+ user,
67
+ tokens
68
+ });
69
+ });
70
+ }
@@ -0,0 +1,46 @@
1
+ import type { OAuthConfig } from '#auth-utils';
2
+ export interface OAuthVKConfig {
3
+ /**
4
+ * VK OAuth Client ID
5
+ * @default process.env.NUXT_OAUTH_VK_CLIENT_ID
6
+ */
7
+ clientId?: string;
8
+ /**
9
+ * VK OAuth Client Secret
10
+ * @default process.env.NUXT_OAUTH_VK_CLIENT_SECRET
11
+ */
12
+ clientSecret?: string;
13
+ /**
14
+ * VK OAuth Scope
15
+ * @default []
16
+ * @see https://id.vk.com/about/business/go/docs/en/vkid/latest/vk-id/connection/api-integration/api-description#App-access-to-user-data
17
+ * @example ["email", "phone"]
18
+ */
19
+ scope?: string[];
20
+ /**
21
+ * Require email from user, adds the ['login:email'] scope if not present
22
+ * @default false
23
+ */
24
+ emailRequired?: boolean;
25
+ /**
26
+ * VK OAuth Authorization URL
27
+ * @default 'https://id.vk.com/authorize'
28
+ */
29
+ authorizationURL?: string;
30
+ /**
31
+ * VK OAuth Token URL
32
+ * @default 'https://id.vk.com/oauth2/auth'
33
+ */
34
+ tokenURL?: string;
35
+ /**
36
+ * VK OAuth User URL
37
+ * @default 'https://id.vk.com/oauth2/user_info'
38
+ */
39
+ userURL?: string;
40
+ /**
41
+ * Redirect URL to to allow overriding for situations like prod failing to determine public hostname
42
+ * @default process.env.NUXT_OAUTH_VK_REDIRECT_URL or current URL
43
+ */
44
+ redirectURL?: string;
45
+ }
46
+ export declare function oauthVKEventHandler({ config, onSuccess, onError, }: OAuthConfig<OAuthVKConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
@@ -0,0 +1,87 @@
1
+ import crypto from "node:crypto";
2
+ import { eventHandler, getQuery, sendRedirect } from "h3";
3
+ import { withQuery } from "ufo";
4
+ import { defu } from "defu";
5
+ import {
6
+ handleMissingConfiguration,
7
+ handleAccessTokenErrorResponse,
8
+ getOAuthRedirectURL,
9
+ requestAccessToken
10
+ } from "../utils.js";
11
+ import { useRuntimeConfig, createError } from "#imports";
12
+ export function oauthVKEventHandler({
13
+ config,
14
+ onSuccess,
15
+ onError
16
+ }) {
17
+ return eventHandler(async (event) => {
18
+ config = defu(config, useRuntimeConfig(event).oauth?.vk, {
19
+ authorizationURL: "https://id.vk.com/authorize",
20
+ tokenURL: "https://id.vk.com/oauth2/auth",
21
+ userURL: "https://id.vk.com/oauth2/user_info"
22
+ });
23
+ const query = getQuery(event);
24
+ if (!config.clientId || !config.clientSecret) {
25
+ return handleMissingConfiguration(
26
+ event,
27
+ "vk",
28
+ ["clientId", "clientSecret"],
29
+ onError
30
+ );
31
+ }
32
+ const codeVerifier = "verify";
33
+ const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
34
+ if (!query.code) {
35
+ config.scope = config.scope || [];
36
+ if (config.emailRequired && !config.scope.includes("email")) {
37
+ config.scope.push("email");
38
+ }
39
+ return sendRedirect(
40
+ event,
41
+ withQuery(config.authorizationURL, {
42
+ response_type: "code",
43
+ client_id: config.clientId,
44
+ code_challenge: crypto.createHash("sha256").update(codeVerifier).digest("base64url"),
45
+ code_challenge_method: "s256",
46
+ state: crypto.randomUUID(),
47
+ redirect_uri: redirectURL,
48
+ scope: config.scope.join(" ")
49
+ })
50
+ );
51
+ }
52
+ const tokens = await requestAccessToken(config.tokenURL, {
53
+ body: {
54
+ grant_type: "authorization_code",
55
+ code: query.code,
56
+ code_verifier: codeVerifier,
57
+ client_id: config.clientId,
58
+ device_id: query.device_id,
59
+ redirect_uri: redirectURL
60
+ }
61
+ });
62
+ if (tokens.error) {
63
+ return handleAccessTokenErrorResponse(event, "vk", tokens, onError);
64
+ }
65
+ const accessToken = tokens.access_token;
66
+ const user = await $fetch(config.userURL, {
67
+ method: "POST",
68
+ body: {
69
+ access_token: accessToken,
70
+ client_id: config.clientId
71
+ }
72
+ });
73
+ if (!user) {
74
+ const error = createError({
75
+ statusCode: 500,
76
+ message: "Could not get VK user",
77
+ data: tokens
78
+ });
79
+ if (!onError) throw error;
80
+ return onError(event, error);
81
+ }
82
+ return onSuccess(event, {
83
+ tokens,
84
+ user
85
+ });
86
+ });
87
+ }
@@ -1,12 +1,13 @@
1
- import {
2
- eventHandler,
3
- getQuery,
4
- sendRedirect
5
- } from "h3";
1
+ import { eventHandler, getQuery, sendRedirect } from "h3";
6
2
  import { withQuery } from "ufo";
7
3
  import { defu } from "defu";
8
4
  import { randomUUID } from "uncrypto";
9
- import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
5
+ import {
6
+ handleMissingConfiguration,
7
+ handleAccessTokenErrorResponse,
8
+ getOAuthRedirectURL,
9
+ requestAccessToken
10
+ } from "../utils.js";
10
11
  import { useRuntimeConfig } from "#imports";
11
12
  export function oauthXEventHandler({
12
13
  config,
@@ -29,7 +30,11 @@ export function oauthXEventHandler({
29
30
  }
30
31
  const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
31
32
  if (!query.code) {
32
- config.scope = config.scope || ["tweet.read", "users.read", "offline.access"];
33
+ config.scope = config.scope || [
34
+ "tweet.read",
35
+ "users.read",
36
+ "offline.access"
37
+ ];
33
38
  return sendRedirect(
34
39
  event,
35
40
  withQuery(config.authorizationURL, {
@@ -44,7 +49,9 @@ export function oauthXEventHandler({
44
49
  }
45
50
  const tokens = await requestAccessToken(config.tokenURL, {
46
51
  headers: {
47
- Authorization: `Basic ${Buffer.from(`${config.clientId}:${config.clientSecret}`).toString("base64")}`
52
+ Authorization: `Basic ${Buffer.from(
53
+ `${config.clientId}:${config.clientSecret}`
54
+ ).toString("base64")}`
48
55
  },
49
56
  params: {
50
57
  grant_type: "authorization_code",
@@ -57,17 +64,14 @@ export function oauthXEventHandler({
57
64
  return handleAccessTokenErrorResponse(event, "x", tokens, onError);
58
65
  }
59
66
  const accessToken = tokens.access_token;
60
- const user = await $fetch(
61
- config.userURL,
62
- {
63
- headers: {
64
- Authorization: `Bearer ${accessToken}`
65
- },
66
- query: {
67
- "user.fields": "description,id,name,profile_image_url,username,verified,verified_type"
68
- }
67
+ const user = await $fetch(config.userURL, {
68
+ headers: {
69
+ Authorization: `Bearer ${accessToken}`
70
+ },
71
+ query: {
72
+ "user.fields": "description,id,name,profile_image_url,username,verified,verified_type"
69
73
  }
70
- ).catch((error) => {
74
+ }).catch((error) => {
71
75
  return error;
72
76
  });
73
77
  return onSuccess(event, {
@@ -1,7 +1,12 @@
1
1
  import { eventHandler, getQuery, sendRedirect } from "h3";
2
2
  import { withQuery } from "ufo";
3
3
  import { defu } from "defu";
4
- import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
4
+ import {
5
+ handleMissingConfiguration,
6
+ handleAccessTokenErrorResponse,
7
+ getOAuthRedirectURL,
8
+ requestAccessToken
9
+ } from "../utils.js";
5
10
  import { useRuntimeConfig } from "#imports";
6
11
  export function oauthYandexEventHandler({
7
12
  config,
@@ -16,7 +21,12 @@ export function oauthYandexEventHandler({
16
21
  });
17
22
  const query = getQuery(event);
18
23
  if (!config.clientId || !config.clientSecret) {
19
- return handleMissingConfiguration(event, "yandex", ["clientId", "clientSecret"], onError);
24
+ return handleMissingConfiguration(
25
+ event,
26
+ "yandex",
27
+ ["clientId", "clientSecret"],
28
+ onError
29
+ );
20
30
  }
21
31
  const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
22
32
  if (!query.code) {
@@ -1,6 +1,6 @@
1
1
  import { defineNitroPlugin } from "nitropack/runtime";
2
2
  export default defineNitroPlugin((nitroApp) => {
3
- if (process.env.NUXT_OAUTH_FACEBOOK_CLIENT_ID && process.env.NUXT_OAUTH_FACEBOOK_CLIENT_SECRET) {
3
+ if (process.env.NUXT_OAUTH_FACEBOOK_CLIENT_ID && process.env.NUXT_OAUTH_FACEBOOK_CLIENT_SECRET || process.env.NUXT_OAUTH_INSTAGRAM_CLIENT_ID && process.env.NUXT_OAUTH_INSTAGRAM_CLIENT_SECRET) {
4
4
  nitroApp.hooks.hook("render:html", (html) => {
5
5
  html.head.unshift(`
6
6
  <script>
@@ -1,4 +1,4 @@
1
- import type { H3Event } from 'h3';
1
+ import type { H3Event, SessionConfig } from 'h3';
2
2
  import type { UserSession, UserSessionRequired } from '#auth-utils';
3
3
  export interface SessionHooks {
4
4
  /**
@@ -25,13 +25,13 @@ export declare function getUserSession(event: H3Event): Promise<UserSession>;
25
25
  * @param data User session data, please only store public information since it can be decoded with API calls
26
26
  * @see https://github.com/atinux/nuxt-auth-utils
27
27
  */
28
- export declare function setUserSession(event: H3Event, data: UserSession): Promise<UserSession>;
28
+ export declare function setUserSession(event: H3Event, data: UserSession, config?: Partial<SessionConfig>): Promise<UserSession>;
29
29
  /**
30
30
  * Replace a user session
31
31
  * @param event The Request (h3) event
32
32
  * @param data User session data, please only store public information since it can be decoded with API calls
33
33
  */
34
- export declare function replaceUserSession(event: H3Event, data: UserSession): Promise<UserSession>;
34
+ export declare function replaceUserSession(event: H3Event, data: UserSession, config?: Partial<SessionConfig>): Promise<UserSession>;
35
35
  /**
36
36
  * Clear the user session and removing the session cookie
37
37
  * @param event The Request (h3) event
@@ -6,13 +6,13 @@ export const sessionHooks = createHooks();
6
6
  export async function getUserSession(event) {
7
7
  return (await _useSession(event)).data;
8
8
  }
9
- export async function setUserSession(event, data) {
10
- const session = await _useSession(event);
9
+ export async function setUserSession(event, data, config) {
10
+ const session = await _useSession(event, config);
11
11
  await session.update(defu(data, session.data));
12
12
  return session.data;
13
13
  }
14
- export async function replaceUserSession(event, data) {
15
- const session = await _useSession(event);
14
+ export async function replaceUserSession(event, data, config) {
15
+ const session = await _useSession(event, config);
16
16
  await session.clear();
17
17
  await session.update(data);
18
18
  return session.data;
@@ -34,11 +34,12 @@ export async function requireUserSession(event, opts = {}) {
34
34
  return userSession;
35
35
  }
36
36
  let sessionConfig;
37
- function _useSession(event) {
37
+ function _useSession(event, config = {}) {
38
38
  if (!sessionConfig) {
39
39
  const runtimeConfig = useRuntimeConfig(event);
40
40
  const envSessionPassword = `${runtimeConfig.nitro?.envPrefix || "NUXT_"}SESSION_PASSWORD`;
41
41
  sessionConfig = defu({ password: process.env[envSessionPassword] }, runtimeConfig.session);
42
42
  }
43
- return useSession(event, sessionConfig);
43
+ const finalConfig = defu(config, sessionConfig);
44
+ return useSession(event, finalConfig);
44
45
  }
@@ -1,2 +1,2 @@
1
- export type { User, UserSession, UserSessionRequired, UserSessionComposable } from './session.js';
1
+ export type { User, UserSession, UserSessionRequired, UserSessionComposable, SecureSessionData } from './session.js';
2
2
  export type { OAuthConfig, OAuthProvider, OnError } from './oauth-config.js';
@@ -1,5 +1,5 @@
1
1
  import type { H3Event, H3Error } from 'h3';
2
- export type OAuthProvider = 'auth0' | 'battledotnet' | 'cognito' | 'discord' | 'facebook' | 'github' | 'google' | 'keycloak' | 'linkedin' | 'microsoft' | 'paypal' | 'spotify' | 'steam' | 'tiktok' | 'twitch' | 'x' | 'xsuaa' | 'yandex' | (string & {});
2
+ export type OAuthProvider = 'auth0' | 'battledotnet' | 'cognito' | 'discord' | 'facebook' | 'github' | 'gitlab' | 'google' | 'instagram' | 'keycloak' | 'linkedin' | 'microsoft' | 'paypal' | 'spotify' | 'steam' | 'tiktok' | 'twitch' | 'vk' | 'x' | 'xsuaa' | 'yandex' | (string & {});
3
3
  export type OnError = (event: H3Event, error: H3Error) => Promise<void> | void;
4
4
  export interface OAuthConfig<TConfig, TUser = any, TTokens = any> {
5
5
  config?: TConfig;
@@ -1,8 +1,21 @@
1
1
  import type { ComputedRef, Ref } from 'vue';
2
2
  export interface User {
3
3
  }
4
+ export interface SecureSessionData {
5
+ }
4
6
  export interface UserSession {
7
+ /**
8
+ * User session data, available on client and server
9
+ */
5
10
  user?: User;
11
+ /**
12
+ * Private session data, only available on server/ code
13
+ */
14
+ secure?: SecureSessionData;
15
+ /**
16
+ * Extra session data, available on client and server
17
+ */
18
+ [key: string]: unknown;
6
19
  }
7
20
  export interface UserSessionRequired extends UserSession {
8
21
  user: User;
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "nuxt-auth-utils",
3
- "version": "0.3.7",
3
+ "version": "0.3.9",
4
4
  "description": "Minimalist Auth module for Nuxt with SSR",
5
5
  "repository": "Atinux/nuxt-auth-utils",
6
6
  "license": "MIT",
7
7
  "type": "module",
8
- "packageManager": "pnpm@9.9.0",
8
+ "packageManager": "pnpm@9.10.0",
9
9
  "exports": {
10
10
  ".": {
11
11
  "types": "./dist/types.d.ts",
@@ -32,29 +32,29 @@
32
32
  "test:watch": "vitest watch"
33
33
  },
34
34
  "dependencies": {
35
- "@nuxt/kit": "^3.13.1",
35
+ "@nuxt/kit": "^3.13.2",
36
36
  "defu": "^6.1.4",
37
37
  "hookable": "^5.5.3",
38
38
  "ofetch": "^1.3.4",
39
- "ohash": "^1.1.3",
39
+ "ohash": "^1.1.4",
40
40
  "pathe": "^1.1.2",
41
41
  "scule": "^1.3.0",
42
42
  "uncrypto": "^0.1.3"
43
43
  },
44
44
  "devDependencies": {
45
- "@iconify-json/simple-icons": "^1.2.1",
45
+ "@iconify-json/simple-icons": "^1.2.3",
46
46
  "@nuxt/devtools": "latest",
47
- "@nuxt/eslint-config": "^0.5.6",
48
- "@nuxt/module-builder": "^0.8.3",
49
- "@nuxt/schema": "^3.13.1",
50
- "@nuxt/test-utils": "^3.14.1",
51
- "@nuxt/ui": "^2.18.4",
52
- "@nuxt/ui-pro": "^1.4.1",
53
- "changelogen": "^0.5.5",
47
+ "@nuxt/eslint-config": "^0.5.7",
48
+ "@nuxt/module-builder": "^0.8.4",
49
+ "@nuxt/schema": "^3.13.2",
50
+ "@nuxt/test-utils": "^3.14.2",
51
+ "@nuxt/ui": "^2.18.5",
52
+ "@nuxt/ui-pro": "^1.4.2",
53
+ "changelogen": "^0.5.7",
54
54
  "eslint": "^9.10.0",
55
- "nuxt": "^3.13.1",
56
- "typescript": "^5.5.4",
57
- "vitest": "^2.0.5",
55
+ "nuxt": "^3.13.2",
56
+ "typescript": "^5.6.2",
57
+ "vitest": "^2.1.1",
58
58
  "vue-tsc": "^2.1.6"
59
59
  }
60
60
  }