nuxt-auth-utils 0.5.17 → 0.5.19

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
@@ -16,7 +16,7 @@ Add Authentication to Nuxt applications with secured & sealed cookies sessions.
16
16
  ## Features
17
17
 
18
18
  - [Hybrid Rendering](#hybrid-rendering) support (SSR / CSR / SWR / Prerendering)
19
- - [30+ OAuth Providers](#supported-oauth-providers)
19
+ - [40+ OAuth Providers](#supported-oauth-providers)
20
20
  - [Password Hashing](#password-hashing)
21
21
  - [WebAuthn (passkey)](#webauthn-passkey)
22
22
  - [`useUserSession()` Vue composable](#vue-composable)
@@ -226,6 +226,7 @@ It can also be set using environment variables:
226
226
  - GitLab
227
227
  - Gitea
228
228
  - Google
229
+ - Heroku
229
230
  - Hubspot
230
231
  - Instagram
231
232
  - Kick
@@ -237,7 +238,9 @@ It can also be set using environment variables:
237
238
  - Microsoft
238
239
  - PayPal
239
240
  - Polar
241
+ - Salesforce
240
242
  - Seznam
243
+ - Slack
241
244
  - Spotify
242
245
  - Steam
243
246
  - Strava
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "auth-utils",
3
3
  "configKey": "auth",
4
- "version": "0.5.17",
4
+ "version": "0.5.19",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "0.8.4",
7
7
  "unbuild": "2.0.0"
package/dist/module.mjs CHANGED
@@ -334,6 +334,25 @@ const module = defineNuxtModule({
334
334
  clientId: "",
335
335
  clientSecret: ""
336
336
  });
337
+ runtimeConfig.oauth.salesforce = defu(runtimeConfig.oauth.salesforce, {
338
+ clientId: "",
339
+ clientSecret: "",
340
+ redirectURL: "",
341
+ baseURL: "",
342
+ scope: ""
343
+ });
344
+ runtimeConfig.oauth.slack = defu(runtimeConfig.oauth.slack, {
345
+ clientId: "",
346
+ clientSecret: "",
347
+ redirectURL: "",
348
+ scope: ""
349
+ });
350
+ runtimeConfig.oauth.heroku = defu(runtimeConfig.oauth.heroku, {
351
+ clientId: "",
352
+ clientSecret: "",
353
+ redirectURL: "",
354
+ scope: ""
355
+ });
337
356
  }
338
357
  });
339
358
 
@@ -1,5 +1,6 @@
1
1
  declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
2
- id: any;
2
+ [key: string]: unknown;
3
+ id?: string;
3
4
  user?: import("../../types").User;
4
5
  }>>;
5
6
  export default _default;
@@ -40,7 +40,7 @@ export interface OAuthFacebookConfig {
40
40
  */
41
41
  authorizationParams?: Record<string, string>;
42
42
  /**
43
- * Redirect URL to to allow overriding for situations like prod failing to determine public hostname
43
+ * Redirect URL to allow overriding for situations like prod failing to determine public hostname
44
44
  * @default process.env.NUXT_OAUTH_FACEBOOK_REDIRECT_URL or current URL
45
45
  */
46
46
  redirectURL?: string;
@@ -35,7 +35,9 @@ export function defineOAuthFacebookEventHandler({
35
35
  withQuery(config.authorizationURL, {
36
36
  client_id: config.clientId,
37
37
  redirect_uri: redirectURL,
38
- scope: config.scope.join(" ")
38
+ scope: config.scope.join(" "),
39
+ state: query.state || "",
40
+ ...config.authorizationParams
39
41
  })
40
42
  );
41
43
  }
@@ -32,6 +32,11 @@ export interface OAuthGitHubConfig {
32
32
  * @default 'https://github.com/login/oauth/access_token'
33
33
  */
34
34
  tokenURL?: string;
35
+ /**
36
+ * GitHub API URL
37
+ * @default 'https://api.github.com'
38
+ */
39
+ apiURL?: string;
35
40
  /**
36
41
  * Extra authorization parameters to provide to the authorization URL
37
42
  * @see https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#1-request-a-users-github-identity
@@ -8,6 +8,7 @@ export function defineOAuthGitHubEventHandler({ config, onSuccess, onError }) {
8
8
  config = defu(config, useRuntimeConfig(event).oauth?.github, {
9
9
  authorizationURL: "https://github.com/login/oauth/authorize",
10
10
  tokenURL: "https://github.com/login/oauth/access_token",
11
+ apiURL: "https://api.github.com",
11
12
  authorizationParams: {}
12
13
  });
13
14
  const query = getQuery(event);
@@ -52,14 +53,14 @@ export function defineOAuthGitHubEventHandler({ config, onSuccess, onError }) {
52
53
  return handleAccessTokenErrorResponse(event, "github", tokens, onError);
53
54
  }
54
55
  const accessToken = tokens.access_token;
55
- const user = await $fetch("https://api.github.com/user", {
56
+ const user = await $fetch(`${config.apiURL}/user`, {
56
57
  headers: {
57
58
  "User-Agent": `Github-OAuth-${config.clientId}`,
58
59
  "Authorization": `token ${accessToken}`
59
60
  }
60
61
  });
61
62
  if (!user.email && config.emailRequired) {
62
- const emails = await $fetch("https://api.github.com/user/emails", {
63
+ const emails = await $fetch(`${config.apiURL}/user/emails`, {
63
64
  headers: {
64
65
  "User-Agent": `Github-OAuth-${config.clientId}`,
65
66
  "Authorization": `token ${accessToken}`
@@ -0,0 +1,41 @@
1
+ import type { OAuthConfig } from '#auth-utils';
2
+ export interface OAuthHerokuConfig {
3
+ /**
4
+ * Heroku OAuth Client ID
5
+ * @default process.env.NUXT_OAUTH_HEROKU_CLIENT_ID
6
+ */
7
+ clientId?: string;
8
+ /**
9
+ * Heroku OAuth Client Secret
10
+ * @default process.env.NUXT_OAUTH_HEROKU_CLIENT_SECRET
11
+ */
12
+ clientSecret?: string;
13
+ /**
14
+ * Heroku OAuth Scope
15
+ * @default ['identity']
16
+ * @see https://devcenter.heroku.com/articles/oauth#scopes
17
+ * @example ['identity']
18
+ */
19
+ scope?: string[];
20
+ /**
21
+ * Heroku OAuth Authorization URL
22
+ * @default 'https://id.heroku.com/oauth/authorize'
23
+ */
24
+ authorizationURL?: string;
25
+ /**
26
+ * Heroku OAuth Authorization URL
27
+ * @default 'https://id.heroku.com/oauth/token'
28
+ */
29
+ tokenURL?: string;
30
+ /**
31
+ * Extra authorization parameters to provide to the authorization URL
32
+ * @default {}
33
+ */
34
+ authorizationParams?: Record<string, string>;
35
+ /**
36
+ * Redirect URL to allow overriding for situations like prod failing to determine public hostname
37
+ * @default process.env.NUXT_OAUTH_HEROKU_REDIRECT_URL or current URL
38
+ */
39
+ redirectURL?: string;
40
+ }
41
+ export declare function defineOAuthHerokuEventHandler({ config, onSuccess, onError, }: OAuthConfig<OAuthHerokuConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
@@ -0,0 +1,75 @@
1
+ import { eventHandler, getQuery, sendRedirect } from "h3";
2
+ import { withQuery } from "ufo";
3
+ import { defu } from "defu";
4
+ import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken, handleState, handleInvalidState } from "../utils.js";
5
+ import { useRuntimeConfig, createError } from "#imports";
6
+ export function defineOAuthHerokuEventHandler({
7
+ config,
8
+ onSuccess,
9
+ onError
10
+ }) {
11
+ return eventHandler(async (event) => {
12
+ const runtimeConfig = useRuntimeConfig(event).oauth?.heroku;
13
+ const baseURL = "https://id.heroku.com";
14
+ config = defu(config, runtimeConfig, {
15
+ authorizationURL: `${baseURL}/oauth/authorize`,
16
+ tokenURL: `${baseURL}/oauth/token`,
17
+ authorizationParams: {}
18
+ });
19
+ const query = getQuery(event);
20
+ if (query.error) {
21
+ const error = createError({
22
+ statusCode: 401,
23
+ message: `Heroku login failed: ${query.error || "Unknown error"}`,
24
+ data: query
25
+ });
26
+ if (!onError) throw error;
27
+ return onError(event, error);
28
+ }
29
+ if (!config.clientId || !config.clientSecret) {
30
+ return handleMissingConfiguration(event, "heroku", ["clientId", "clientSecret"], onError);
31
+ }
32
+ const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
33
+ const state = await handleState(event);
34
+ if (!query.code) {
35
+ config.scope = config.scope || ["identity"];
36
+ return sendRedirect(
37
+ event,
38
+ withQuery(config.authorizationURL, {
39
+ response_type: "code",
40
+ client_id: config.clientId,
41
+ redirect_uri: redirectURL,
42
+ scope: config.scope.join(" "),
43
+ state,
44
+ ...config.authorizationParams
45
+ })
46
+ );
47
+ }
48
+ if (query.state !== state) {
49
+ handleInvalidState(event, "heroku", onError);
50
+ }
51
+ const tokens = await requestAccessToken(config.tokenURL, {
52
+ body: {
53
+ grant_type: "authorization_code",
54
+ client_id: config.clientId,
55
+ client_secret: config.clientSecret,
56
+ redirect_uri: redirectURL,
57
+ code: query.code
58
+ }
59
+ });
60
+ if (tokens.error) {
61
+ return handleAccessTokenErrorResponse(event, "heroku", tokens, onError);
62
+ }
63
+ const accessToken = tokens.access_token;
64
+ const user = await $fetch(`https://api.heroku.com/account`, {
65
+ headers: {
66
+ Accept: "application/vnd.heroku+json; version=3",
67
+ Authorization: `Bearer ${accessToken}`
68
+ }
69
+ });
70
+ return onSuccess(event, {
71
+ user,
72
+ tokens
73
+ });
74
+ });
75
+ }
@@ -25,6 +25,7 @@ export function defineOAuthMicrosoftEventHandler({ config, onSuccess, onError })
25
25
  response_type: "code",
26
26
  redirect_uri: redirectURL,
27
27
  scope: config.scope.join(" "),
28
+ state: query.state || "",
28
29
  ...config.authorizationParams
29
30
  })
30
31
  );
@@ -0,0 +1,46 @@
1
+ import type { OAuthConfig } from '#auth-utils';
2
+ export interface OAuthSalesforceConfig {
3
+ /**
4
+ * Salesforce OAuth Client ID
5
+ * @default process.env.NUXT_OAUTH_SALESFORCE_CLIENT_ID
6
+ */
7
+ clientId?: string;
8
+ /**
9
+ * Salesforce OAuth Client Secret
10
+ * @default process.env.NUXT_OAUTH_SALESFORCE_CLIENT_SECRET
11
+ */
12
+ clientSecret?: string;
13
+ /**
14
+ * Salesforce OAuth Scope
15
+ * @default ['id']
16
+ * @see https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_tokens_scopes.htm
17
+ * @example ['id']
18
+ */
19
+ scope?: string[];
20
+ /**
21
+ * Salesforce MyDomain URL
22
+ * @default 'https://login.salesforce.com'
23
+ */
24
+ baseURL?: string;
25
+ /**
26
+ * Salesforce OAuth Authorization URL
27
+ * @default 'https://login.salesforce.com/services/oauth2/authorize'
28
+ */
29
+ authorizationURL?: string;
30
+ /**
31
+ * Salesforce OAuth Authorization URL
32
+ * @default 'https://login.salesforce.com/services/oauth2/token'
33
+ */
34
+ tokenURL?: string;
35
+ /**
36
+ * Extra authorization parameters to provide to the authorization URL
37
+ * @default {}
38
+ */
39
+ authorizationParams?: Record<string, string>;
40
+ /**
41
+ * Redirect URL to allow overriding for situations like prod failing to determine public hostname
42
+ * @default process.env.NUXT_OAUTH_SALESFORCE_REDIRECT_URL or current URL
43
+ */
44
+ redirectURL?: string;
45
+ }
46
+ export declare function defineOAuthSalesforceEventHandler({ config, onSuccess, onError, }: OAuthConfig<OAuthSalesforceConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
@@ -0,0 +1,74 @@
1
+ import { eventHandler, getQuery, sendRedirect } from "h3";
2
+ import { withQuery } from "ufo";
3
+ import { defu } from "defu";
4
+ import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken, handleState, handleInvalidState } from "../utils.js";
5
+ import { useRuntimeConfig, createError } from "#imports";
6
+ export function defineOAuthSalesforceEventHandler({
7
+ config,
8
+ onSuccess,
9
+ onError
10
+ }) {
11
+ return eventHandler(async (event) => {
12
+ const runtimeConfig = useRuntimeConfig(event).oauth?.salesforce;
13
+ const baseURL = config?.baseURL || "https://login.salesforce.com";
14
+ config = defu(config, runtimeConfig, {
15
+ authorizationURL: `${baseURL}/services/oauth2/authorize`,
16
+ tokenURL: `${baseURL}/services/oauth2/token`,
17
+ authorizationParams: {}
18
+ });
19
+ const query = getQuery(event);
20
+ if (query.error) {
21
+ const error = createError({
22
+ statusCode: 401,
23
+ message: `Salesforce login failed: ${query.error || "Unknown error"}`,
24
+ data: query
25
+ });
26
+ if (!onError) throw error;
27
+ return onError(event, error);
28
+ }
29
+ if (!config.clientId || !config.clientSecret) {
30
+ return handleMissingConfiguration(event, "salesforce", ["clientId", "clientSecret"], onError);
31
+ }
32
+ const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
33
+ const state = await handleState(event);
34
+ if (!query.code) {
35
+ config.scope = config.scope || ["id"];
36
+ return sendRedirect(
37
+ event,
38
+ withQuery(config.authorizationURL, {
39
+ response_type: "code",
40
+ client_id: config.clientId,
41
+ redirect_uri: redirectURL,
42
+ scope: config.scope.join(" "),
43
+ state,
44
+ ...config.authorizationParams
45
+ })
46
+ );
47
+ }
48
+ if (query.state !== state) {
49
+ handleInvalidState(event, "salesforce", onError);
50
+ }
51
+ const tokens = await requestAccessToken(config.tokenURL, {
52
+ body: {
53
+ grant_type: "authorization_code",
54
+ client_id: config.clientId,
55
+ client_secret: config.clientSecret,
56
+ redirect_uri: redirectURL,
57
+ code: query.code
58
+ }
59
+ });
60
+ if (tokens.error) {
61
+ return handleAccessTokenErrorResponse(event, "salesforce", tokens, onError);
62
+ }
63
+ const accessToken = tokens.access_token;
64
+ const user = await $fetch(`${baseURL}/services/oauth2/userinfo`, {
65
+ headers: {
66
+ Authorization: `Bearer ${accessToken}`
67
+ }
68
+ });
69
+ return onSuccess(event, {
70
+ user,
71
+ tokens
72
+ });
73
+ });
74
+ }
@@ -0,0 +1,41 @@
1
+ import type { OAuthConfig } from '#auth-utils';
2
+ export interface OAuthSlackConfig {
3
+ /**
4
+ * Slack OAuth Client ID
5
+ * @default process.env.NUXT_OAUTH_SLACK_CLIENT_ID
6
+ */
7
+ clientId?: string;
8
+ /**
9
+ * Slack OAuth Client Secret
10
+ * @default process.env.NUXT_OAUTH_SLACK_CLIENT_SECRET
11
+ */
12
+ clientSecret?: string;
13
+ /**
14
+ * Slack OAuth Scope
15
+ * @default ['openid', 'email', 'profile']
16
+ * @see https://api.slack.com/scopes?filter=user
17
+ * @example ['openid']
18
+ */
19
+ scope?: string[];
20
+ /**
21
+ * Slack OAuth Authorization URL
22
+ * @default 'https://slack.com/openid/connect/authorize'
23
+ */
24
+ authorizationURL?: string;
25
+ /**
26
+ * Slack OAuth Authorization URL
27
+ * @default 'https://slack.com/api/openid.connect.token'
28
+ */
29
+ tokenURL?: string;
30
+ /**
31
+ * Extra authorization parameters to provide to the authorization URL
32
+ * @default {}
33
+ */
34
+ authorizationParams?: Record<string, string>;
35
+ /**
36
+ * Redirect URL to allow overriding for situations like prod failing to determine public hostname
37
+ * @default process.env.NUXT_OAUTH_SLACK_REDIRECT_URL or current URL
38
+ */
39
+ redirectURL?: string;
40
+ }
41
+ export declare function defineOAuthSlackEventHandler({ config, onSuccess, onError, }: OAuthConfig<OAuthSlackConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
@@ -0,0 +1,74 @@
1
+ import { eventHandler, getQuery, sendRedirect } from "h3";
2
+ import { withQuery } from "ufo";
3
+ import { defu } from "defu";
4
+ import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken, handleState, handleInvalidState } from "../utils.js";
5
+ import { useRuntimeConfig, createError } from "#imports";
6
+ export function defineOAuthSlackEventHandler({
7
+ config,
8
+ onSuccess,
9
+ onError
10
+ }) {
11
+ return eventHandler(async (event) => {
12
+ const runtimeConfig = useRuntimeConfig(event).oauth?.slack;
13
+ const baseURL = "https://slack.com";
14
+ config = defu(config, runtimeConfig, {
15
+ authorizationURL: `${baseURL}/openid/connect/authorize`,
16
+ tokenURL: `${baseURL}/api/openid.connect.token`,
17
+ authorizationParams: {}
18
+ });
19
+ const query = getQuery(event);
20
+ if (query.error) {
21
+ const error = createError({
22
+ statusCode: 401,
23
+ message: `Slack login failed: ${query.error || "Unknown error"}`,
24
+ data: query
25
+ });
26
+ if (!onError) throw error;
27
+ return onError(event, error);
28
+ }
29
+ if (!config.clientId || !config.clientSecret) {
30
+ return handleMissingConfiguration(event, "slack", ["clientId", "clientSecret"], onError);
31
+ }
32
+ const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
33
+ const state = await handleState(event);
34
+ if (!query.code) {
35
+ config.scope = config.scope || ["openid", "email", "profile"];
36
+ return sendRedirect(
37
+ event,
38
+ withQuery(config.authorizationURL, {
39
+ response_type: "code",
40
+ client_id: config.clientId,
41
+ redirect_uri: redirectURL,
42
+ scope: config.scope.join(" "),
43
+ state,
44
+ ...config.authorizationParams
45
+ })
46
+ );
47
+ }
48
+ if (query.state !== state) {
49
+ handleInvalidState(event, "slack", onError);
50
+ }
51
+ const tokens = await requestAccessToken(config.tokenURL, {
52
+ body: {
53
+ grant_type: "authorization_code",
54
+ client_id: config.clientId,
55
+ client_secret: config.clientSecret,
56
+ redirect_uri: redirectURL,
57
+ code: query.code
58
+ }
59
+ });
60
+ if (tokens.error) {
61
+ return handleAccessTokenErrorResponse(event, "slack", tokens, onError);
62
+ }
63
+ const accessToken = tokens.access_token;
64
+ const user = await $fetch(`${baseURL}/api/openid.connect.userInfo`, {
65
+ headers: {
66
+ Authorization: `Bearer ${accessToken}`
67
+ }
68
+ });
69
+ return onSuccess(event, {
70
+ user,
71
+ tokens
72
+ });
73
+ });
74
+ }
@@ -20,11 +20,7 @@ export declare const sessionHooks: import("hookable").Hookable<SessionHooks, imp
20
20
  * @param event The Request (h3) event
21
21
  * @returns The user session
22
22
  */
23
- export declare function getUserSession(event: UseSessionEvent): Promise<{
24
- id: any;
25
- user?: import("#auth-utils").User;
26
- secure?: import("#auth-utils").SecureSessionData;
27
- }>;
23
+ export declare function getUserSession(event: UseSessionEvent): Promise<UserSession>;
28
24
  /**
29
25
  * Set a user session
30
26
  * @param event The Request (h3) event
@@ -1,6 +1,6 @@
1
1
  import type { H3Event, H3Error } from 'h3';
2
2
  export type ATProtoProvider = 'bluesky';
3
- export type OAuthProvider = ATProtoProvider | 'atlassian' | 'auth0' | 'authentik' | 'azureb2c' | 'battledotnet' | 'cognito' | 'discord' | 'dropbox' | 'facebook' | 'gitea' | 'github' | 'gitlab' | 'google' | 'hubspot' | 'instagram' | 'kick' | 'keycloak' | 'line' | 'linear' | 'linkedin' | 'microsoft' | 'paypal' | 'polar' | 'spotify' | 'seznam' | 'steam' | 'strava' | 'tiktok' | 'twitch' | 'vk' | 'workos' | 'x' | 'xsuaa' | 'yandex' | 'zitadel' | 'apple' | 'livechat' | (string & {});
3
+ export type OAuthProvider = ATProtoProvider | 'atlassian' | 'auth0' | 'authentik' | 'azureb2c' | 'battledotnet' | 'cognito' | 'discord' | 'dropbox' | 'facebook' | 'gitea' | 'github' | 'gitlab' | 'google' | 'hubspot' | 'instagram' | 'kick' | 'keycloak' | 'line' | 'linear' | 'linkedin' | 'microsoft' | 'paypal' | 'polar' | 'spotify' | 'seznam' | 'steam' | 'strava' | 'tiktok' | 'twitch' | 'vk' | 'workos' | 'x' | 'xsuaa' | 'yandex' | 'zitadel' | 'apple' | 'livechat' | 'salesforce' | 'slack' | 'heroku' | (string & {});
4
4
  export type OnError = (event: H3Event, error: H3Error) => Promise<void> | void;
5
5
  export interface OAuthConfig<TConfig, TUser = any, TTokens = any> {
6
6
  config?: TConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-auth-utils",
3
- "version": "0.5.17",
3
+ "version": "0.5.19",
4
4
  "description": "Add Authentication to Nuxt applications with secured & sealed cookies sessions.",
5
5
  "repository": {
6
6
  "type": "git",