nuxt-auth-utils 0.0.22 → 0.0.24
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 +2 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +5 -1
- package/dist/runtime/composables/session.mjs +2 -1
- package/dist/runtime/server/lib/oauth/battledotnet.mjs +3 -3
- package/dist/runtime/server/lib/oauth/discord.mjs +1 -1
- package/dist/runtime/server/lib/oauth/facebook.d.ts +36 -0
- package/dist/runtime/server/lib/oauth/facebook.mjs +86 -0
- package/dist/runtime/server/lib/oauth/github.mjs +2 -2
- package/dist/runtime/server/lib/oauth/linkedin.mjs +1 -1
- package/dist/runtime/server/lib/oauth/spotify.mjs +1 -1
- package/dist/runtime/server/lib/oauth/twitch.d.ts +3 -3
- package/dist/runtime/server/lib/oauth/twitch.mjs +1 -1
- package/dist/runtime/server/utils/oauth.d.ts +2 -0
- package/dist/runtime/server/utils/oauth.mjs +3 -1
- package/dist/runtime/server/utils/session.d.ts +13 -3
- package/dist/runtime/server/utils/session.mjs +3 -3
- package/package.json +14 -14
package/README.md
CHANGED
|
@@ -59,7 +59,7 @@ const { loggedIn, user, session, clear } = useUserSession()
|
|
|
59
59
|
</div>
|
|
60
60
|
<div v-else>
|
|
61
61
|
<h1>Not logged in</h1>
|
|
62
|
-
<a href="/
|
|
62
|
+
<a href="/auth/github">Login with GitHub</a>
|
|
63
63
|
</div>
|
|
64
64
|
</template>
|
|
65
65
|
```
|
|
@@ -146,6 +146,7 @@ It can also be set using environment variables:
|
|
|
146
146
|
- AWS Cognito
|
|
147
147
|
- Battle.net
|
|
148
148
|
- Discord
|
|
149
|
+
- Facebook
|
|
149
150
|
- GitHub
|
|
150
151
|
- Google
|
|
151
152
|
- Keycloak
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
1
2
|
import { defineNuxtModule, createResolver, addImportsDir, addPlugin, addServerHandler } from '@nuxt/kit';
|
|
2
3
|
import { join } from 'pathe';
|
|
3
4
|
import { defu } from 'defu';
|
|
4
5
|
import { randomUUID } from 'uncrypto';
|
|
5
|
-
import { readFile, writeFile } from 'node:fs/promises';
|
|
6
6
|
|
|
7
7
|
const module = defineNuxtModule({
|
|
8
8
|
meta: {
|
|
@@ -119,6 +119,10 @@ const module = defineNuxtModule({
|
|
|
119
119
|
region: "",
|
|
120
120
|
userPoolId: ""
|
|
121
121
|
});
|
|
122
|
+
runtimeConfig.oauth.facebook = defu(runtimeConfig.oauth.facebook, {
|
|
123
|
+
clientId: "",
|
|
124
|
+
clientSecret: ""
|
|
125
|
+
});
|
|
122
126
|
}
|
|
123
127
|
});
|
|
124
128
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
|
|
2
3
|
import { ofetch } from "ofetch";
|
|
3
4
|
import { withQuery, parsePath } from "ufo";
|
|
4
5
|
import { defu } from "defu";
|
|
5
6
|
import { useRuntimeConfig } from "#imports";
|
|
6
|
-
import { randomUUID } from "crypto";
|
|
7
7
|
export function battledotnetEventHandler({ config, onSuccess, onError }) {
|
|
8
8
|
return eventHandler(async (event) => {
|
|
9
9
|
config = defu(config, useRuntimeConfig(event).oauth?.battledotnet, {
|
|
@@ -65,7 +65,7 @@ export function battledotnetEventHandler({ config, onSuccess, onError }) {
|
|
|
65
65
|
method: "POST",
|
|
66
66
|
headers: {
|
|
67
67
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
68
|
-
Authorization: `Basic ${authCode}`
|
|
68
|
+
"Authorization": `Basic ${authCode}`
|
|
69
69
|
},
|
|
70
70
|
params: {
|
|
71
71
|
code,
|
|
@@ -91,7 +91,7 @@ export function battledotnetEventHandler({ config, onSuccess, onError }) {
|
|
|
91
91
|
const user = await ofetch("https://oauth.battle.net/userinfo", {
|
|
92
92
|
headers: {
|
|
93
93
|
"User-Agent": `Battledotnet-OAuth-${config.clientId}`,
|
|
94
|
-
Authorization: `Bearer ${accessToken}`
|
|
94
|
+
"Authorization": `Bearer ${accessToken}`
|
|
95
95
|
}
|
|
96
96
|
});
|
|
97
97
|
if (!user) {
|
|
@@ -75,7 +75,7 @@ export function discordEventHandler({ config, onSuccess, onError }) {
|
|
|
75
75
|
const user = await ofetch("https://discord.com/api/users/@me", {
|
|
76
76
|
headers: {
|
|
77
77
|
"user-agent": "Nuxt Auth Utils",
|
|
78
|
-
Authorization: `Bearer ${accessToken}`
|
|
78
|
+
"Authorization": `Bearer ${accessToken}`
|
|
79
79
|
}
|
|
80
80
|
});
|
|
81
81
|
return onSuccess(event, {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { OAuthConfig } from '#auth-utils';
|
|
2
|
+
export interface OAuthFacebookConfig {
|
|
3
|
+
/**
|
|
4
|
+
* Facebook OAuth Client ID
|
|
5
|
+
* @default process.env.NUXT_OAUTH_FACEBOOK_CLIENT_ID
|
|
6
|
+
*/
|
|
7
|
+
clientId?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Facebook OAuth Client Secret
|
|
10
|
+
* @default process.env.NUXT_OAUTH_FACEBOOK_CLIENT_SECRET
|
|
11
|
+
*/
|
|
12
|
+
clientSecret?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Facebook OAuth Scope
|
|
15
|
+
* @default []
|
|
16
|
+
* @see https://developers.facebook.com/docs/permissions
|
|
17
|
+
* @example [ 'email' ],
|
|
18
|
+
*/
|
|
19
|
+
scope?: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Facebook OAuth Authorization URL
|
|
22
|
+
* @default 'https://www.facebook.com/v19.0/dialog/oauth'
|
|
23
|
+
*/
|
|
24
|
+
authorizationURL?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Facebook OAuth Token URL
|
|
27
|
+
* @default 'https://graph.facebook.com/v19.0/oauth/access_token'
|
|
28
|
+
*/
|
|
29
|
+
tokenURL?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Extra authorization parameters to provide to the authorization URL
|
|
32
|
+
* @see https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow/
|
|
33
|
+
*/
|
|
34
|
+
authorizationParams?: Record<string, string>;
|
|
35
|
+
}
|
|
36
|
+
export declare function facebookEventHandler({ config, onSuccess, onError, }: OAuthConfig<OAuthFacebookConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {
|
|
2
|
+
eventHandler,
|
|
3
|
+
createError,
|
|
4
|
+
getQuery,
|
|
5
|
+
getRequestURL,
|
|
6
|
+
sendRedirect
|
|
7
|
+
} from "h3";
|
|
8
|
+
import { ofetch } from "ofetch";
|
|
9
|
+
import { withQuery } from "ufo";
|
|
10
|
+
import { defu } from "defu";
|
|
11
|
+
import { useRuntimeConfig } from "#imports";
|
|
12
|
+
export function facebookEventHandler({
|
|
13
|
+
config,
|
|
14
|
+
onSuccess,
|
|
15
|
+
onError
|
|
16
|
+
}) {
|
|
17
|
+
return eventHandler(async (event) => {
|
|
18
|
+
config = defu(config, useRuntimeConfig(event).oauth?.facebook, {
|
|
19
|
+
authorizationURL: "https://www.facebook.com/v19.0/dialog/oauth",
|
|
20
|
+
tokenURL: "https://graph.facebook.com/v19.0/oauth/access_token",
|
|
21
|
+
authorizationParams: {}
|
|
22
|
+
});
|
|
23
|
+
const query = getQuery(event);
|
|
24
|
+
if (query.error) {
|
|
25
|
+
const error = createError({
|
|
26
|
+
statusCode: 401,
|
|
27
|
+
message: `Facebook login failed: ${query.error || "Unknown error"}`,
|
|
28
|
+
data: query
|
|
29
|
+
});
|
|
30
|
+
if (!onError)
|
|
31
|
+
throw error;
|
|
32
|
+
return onError(event, error);
|
|
33
|
+
}
|
|
34
|
+
if (!config.clientId) {
|
|
35
|
+
const error = createError({
|
|
36
|
+
statusCode: 500,
|
|
37
|
+
message: "Missing NUXT_OAUTH_FACEBOOK_CLIENT_ID or NUXT_OAUTH_FACEBOOK_CLIENT_SECRET env variables."
|
|
38
|
+
});
|
|
39
|
+
if (!onError)
|
|
40
|
+
throw error;
|
|
41
|
+
return onError(event, error);
|
|
42
|
+
}
|
|
43
|
+
const redirectUrl = getRequestURL(event).href;
|
|
44
|
+
if (!query.code) {
|
|
45
|
+
config.scope = config.scope || [];
|
|
46
|
+
return sendRedirect(
|
|
47
|
+
event,
|
|
48
|
+
withQuery(config.authorizationURL, {
|
|
49
|
+
client_id: config.clientId,
|
|
50
|
+
redirect_uri: redirectUrl,
|
|
51
|
+
scope: config.scope.join(" ")
|
|
52
|
+
})
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const tokens = await $fetch(config.tokenURL, {
|
|
56
|
+
method: "POST",
|
|
57
|
+
body: {
|
|
58
|
+
client_id: config.clientId,
|
|
59
|
+
client_secret: config.clientSecret,
|
|
60
|
+
redirect_uri: redirectUrl,
|
|
61
|
+
code: query.code
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
if (tokens.error) {
|
|
65
|
+
const error = createError({
|
|
66
|
+
statusCode: 401,
|
|
67
|
+
message: `Facebook login failed: ${tokens.error || "Unknown error"}`,
|
|
68
|
+
data: tokens
|
|
69
|
+
});
|
|
70
|
+
if (!onError)
|
|
71
|
+
throw error;
|
|
72
|
+
return onError(event, error);
|
|
73
|
+
}
|
|
74
|
+
const accessToken = tokens.access_token;
|
|
75
|
+
const user = await ofetch(
|
|
76
|
+
`https://graph.facebook.com/v19.0/me?fields=id,name&access_token=${accessToken}`
|
|
77
|
+
);
|
|
78
|
+
if (!user) {
|
|
79
|
+
throw new Error("Facebook login failed: no user found");
|
|
80
|
+
}
|
|
81
|
+
return onSuccess(event, {
|
|
82
|
+
user,
|
|
83
|
+
tokens
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
}
|
|
@@ -71,14 +71,14 @@ export function githubEventHandler({ config, onSuccess, onError }) {
|
|
|
71
71
|
const user = await ofetch("https://api.github.com/user", {
|
|
72
72
|
headers: {
|
|
73
73
|
"User-Agent": `Github-OAuth-${config.clientId}`,
|
|
74
|
-
Authorization: `token ${accessToken}`
|
|
74
|
+
"Authorization": `token ${accessToken}`
|
|
75
75
|
}
|
|
76
76
|
});
|
|
77
77
|
if (!user.email && config.emailRequired) {
|
|
78
78
|
const emails = await ofetch("https://api.github.com/user/emails", {
|
|
79
79
|
headers: {
|
|
80
80
|
"User-Agent": `Github-OAuth-${config.clientId}`,
|
|
81
|
-
Authorization: `token ${accessToken}`
|
|
81
|
+
"Authorization": `token ${accessToken}`
|
|
82
82
|
}
|
|
83
83
|
});
|
|
84
84
|
const primaryEmail = emails.find((email) => email.primary);
|
|
@@ -74,7 +74,7 @@ export function linkedinEventHandler({ config, onSuccess, onError }) {
|
|
|
74
74
|
const user = await ofetch("https://api.linkedin.com/v2/userinfo", {
|
|
75
75
|
headers: {
|
|
76
76
|
"user-agent": "Nuxt Auth Utils",
|
|
77
|
-
Authorization: `Bearer ${accessToken}`
|
|
77
|
+
"Authorization": `Bearer ${accessToken}`
|
|
78
78
|
}
|
|
79
79
|
});
|
|
80
80
|
return onSuccess(event, {
|
|
@@ -43,7 +43,7 @@ export function spotifyEventHandler({ config, onSuccess, onError }) {
|
|
|
43
43
|
{
|
|
44
44
|
method: "POST",
|
|
45
45
|
headers: {
|
|
46
|
-
Authorization: `Basic ${authCode}`,
|
|
46
|
+
"Authorization": `Basic ${authCode}`,
|
|
47
47
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
48
48
|
},
|
|
49
49
|
params: {
|
|
@@ -6,9 +6,9 @@ export interface OAuthTwitchConfig {
|
|
|
6
6
|
*/
|
|
7
7
|
clientId?: string;
|
|
8
8
|
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
* Twitch OAuth Client Secret
|
|
10
|
+
* @default process.env.NUXT_OAUTH_TWITCH_CLIENT_SECRET
|
|
11
|
+
*/
|
|
12
12
|
clientSecret?: string;
|
|
13
13
|
/**
|
|
14
14
|
* Twitch OAuth Scope
|
|
@@ -69,7 +69,7 @@ export function twitchEventHandler({ config, onSuccess, onError }) {
|
|
|
69
69
|
const users = await ofetch("https://api.twitch.tv/helix/users", {
|
|
70
70
|
headers: {
|
|
71
71
|
"Client-ID": config.clientId,
|
|
72
|
-
Authorization: `Bearer ${accessToken}`
|
|
72
|
+
"Authorization": `Bearer ${accessToken}`
|
|
73
73
|
}
|
|
74
74
|
});
|
|
75
75
|
const user = users.data?.[0];
|
|
@@ -9,6 +9,7 @@ import { battledotnetEventHandler } from '../lib/oauth/battledotnet';
|
|
|
9
9
|
import { keycloakEventHandler } from '../lib/oauth/keycloak';
|
|
10
10
|
import { linkedinEventHandler } from '../lib/oauth/linkedin';
|
|
11
11
|
import { cognitoEventHandler } from '../lib/oauth/cognito';
|
|
12
|
+
import { facebookEventHandler } from '../lib/oauth/facebook';
|
|
12
13
|
export declare const oauth: {
|
|
13
14
|
githubEventHandler: typeof githubEventHandler;
|
|
14
15
|
spotifyEventHandler: typeof spotifyEventHandler;
|
|
@@ -21,4 +22,5 @@ export declare const oauth: {
|
|
|
21
22
|
keycloakEventHandler: typeof keycloakEventHandler;
|
|
22
23
|
linkedinEventHandler: typeof linkedinEventHandler;
|
|
23
24
|
cognitoEventHandler: typeof cognitoEventHandler;
|
|
25
|
+
facebookEventHandler: typeof facebookEventHandler;
|
|
24
26
|
};
|
|
@@ -9,6 +9,7 @@ import { battledotnetEventHandler } from "../lib/oauth/battledotnet.mjs";
|
|
|
9
9
|
import { keycloakEventHandler } from "../lib/oauth/keycloak.mjs";
|
|
10
10
|
import { linkedinEventHandler } from "../lib/oauth/linkedin.mjs";
|
|
11
11
|
import { cognitoEventHandler } from "../lib/oauth/cognito.mjs";
|
|
12
|
+
import { facebookEventHandler } from "../lib/oauth/facebook.mjs";
|
|
12
13
|
export const oauth = {
|
|
13
14
|
githubEventHandler,
|
|
14
15
|
spotifyEventHandler,
|
|
@@ -20,5 +21,6 @@ export const oauth = {
|
|
|
20
21
|
battledotnetEventHandler,
|
|
21
22
|
keycloakEventHandler,
|
|
22
23
|
linkedinEventHandler,
|
|
23
|
-
cognitoEventHandler
|
|
24
|
+
cognitoEventHandler,
|
|
25
|
+
facebookEventHandler
|
|
24
26
|
};
|
|
@@ -6,11 +6,11 @@ export interface SessionHooks {
|
|
|
6
6
|
* - Add extra properties to the session
|
|
7
7
|
* - Throw an error if the session could not be verified (with a database for example)
|
|
8
8
|
*/
|
|
9
|
-
|
|
9
|
+
fetch: (session: UserSessionRequired, event: H3Event) => void | Promise<void>;
|
|
10
10
|
/**
|
|
11
11
|
* Called before clearing the session
|
|
12
12
|
*/
|
|
13
|
-
|
|
13
|
+
clear: (session: UserSession, event: H3Event) => void | Promise<void>;
|
|
14
14
|
}
|
|
15
15
|
export declare const sessionHooks: import("hookable").Hookable<SessionHooks, import("hookable").HookKeys<SessionHooks>>;
|
|
16
16
|
export declare function getUserSession(event: H3Event): Promise<UserSession>;
|
|
@@ -27,4 +27,14 @@ export declare function setUserSession(event: H3Event, data: UserSession): Promi
|
|
|
27
27
|
*/
|
|
28
28
|
export declare function replaceUserSession(event: H3Event, data: UserSession): Promise<UserSession>;
|
|
29
29
|
export declare function clearUserSession(event: H3Event): Promise<boolean>;
|
|
30
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Require a user session
|
|
32
|
+
* @param event
|
|
33
|
+
* @param opts Options to customize the error message and status code
|
|
34
|
+
* @param opts.statusCode The status code to use for the error (defaults to 401)
|
|
35
|
+
* @param opts.message The message to use for the error (defaults to Unauthorized)
|
|
36
|
+
*/
|
|
37
|
+
export declare function requireUserSession(event: H3Event, opts?: {
|
|
38
|
+
statusCode?: number;
|
|
39
|
+
message?: string;
|
|
40
|
+
}): Promise<UserSessionRequired>;
|
|
@@ -23,12 +23,12 @@ export async function clearUserSession(event) {
|
|
|
23
23
|
await session.clear();
|
|
24
24
|
return true;
|
|
25
25
|
}
|
|
26
|
-
export async function requireUserSession(event) {
|
|
26
|
+
export async function requireUserSession(event, opts = {}) {
|
|
27
27
|
const userSession = await getUserSession(event);
|
|
28
28
|
if (!userSession.user) {
|
|
29
29
|
throw createError({
|
|
30
|
-
statusCode: 401,
|
|
31
|
-
message: "Unauthorized"
|
|
30
|
+
statusCode: opts.statusCode || 401,
|
|
31
|
+
message: opts.message || "Unauthorized"
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
34
|
return userSession;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-auth-utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.24",
|
|
4
4
|
"description": "Minimalist Auth module for Nuxt with SSR",
|
|
5
5
|
"repository": "Atinux/nuxt-auth-utils",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"test:watch": "vitest watch"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@nuxt/kit": "^3.11.
|
|
33
|
+
"@nuxt/kit": "^3.11.2",
|
|
34
34
|
"defu": "^6.1.4",
|
|
35
35
|
"hookable": "^5.5.3",
|
|
36
36
|
"ofetch": "^1.3.4",
|
|
@@ -39,20 +39,20 @@
|
|
|
39
39
|
"uncrypto": "^0.1.3"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@iconify-json/simple-icons": "^1.1.
|
|
42
|
+
"@iconify-json/simple-icons": "^1.1.99",
|
|
43
43
|
"@nuxt/devtools": "latest",
|
|
44
|
-
"@nuxt/eslint-config": "^0.
|
|
44
|
+
"@nuxt/eslint-config": "^0.3.8",
|
|
45
45
|
"@nuxt/module-builder": "^0.5.5",
|
|
46
|
-
"@nuxt/schema": "^3.11.
|
|
47
|
-
"@nuxt/test-utils": "^3.12.
|
|
48
|
-
"@nuxt/ui": "^2.
|
|
49
|
-
"@nuxt/ui-pro": "^1.0
|
|
50
|
-
"@types/node": "^20.
|
|
46
|
+
"@nuxt/schema": "^3.11.2",
|
|
47
|
+
"@nuxt/test-utils": "^3.12.1",
|
|
48
|
+
"@nuxt/ui": "^2.15.2",
|
|
49
|
+
"@nuxt/ui-pro": "^1.1.0",
|
|
50
|
+
"@types/node": "^20.12.7",
|
|
51
51
|
"changelogen": "^0.5.5",
|
|
52
|
-
"eslint": "^
|
|
53
|
-
"nuxt": "^3.11.
|
|
54
|
-
"typescript": "^5.4.
|
|
55
|
-
"vitest": "^1.
|
|
56
|
-
"vue-tsc": "^2.0.
|
|
52
|
+
"eslint": "^9.0.0",
|
|
53
|
+
"nuxt": "^3.11.2",
|
|
54
|
+
"typescript": "^5.4.5",
|
|
55
|
+
"vitest": "^1.5.0",
|
|
56
|
+
"vue-tsc": "^2.0.13"
|
|
57
57
|
}
|
|
58
58
|
}
|