nuxt-auth-utils 0.3.4 → 0.3.6
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.d.mts +1 -1
- package/dist/module.d.ts +1 -1
- package/dist/module.json +2 -2
- package/dist/module.mjs +5 -0
- package/dist/runtime/app/plugins/session.client.js +3 -1
- package/dist/runtime/server/lib/oauth/auth0.js +18 -34
- package/dist/runtime/server/lib/oauth/battledotnet.js +23 -37
- package/dist/runtime/server/lib/oauth/cognito.d.ts +5 -0
- package/dist/runtime/server/lib/oauth/cognito.js +21 -30
- package/dist/runtime/server/lib/oauth/discord.js +15 -36
- package/dist/runtime/server/lib/oauth/facebook.js +8 -24
- package/dist/runtime/server/lib/oauth/github.js +14 -26
- package/dist/runtime/server/lib/oauth/google.js +16 -36
- package/dist/runtime/server/lib/oauth/keycloak.js +14 -37
- package/dist/runtime/server/lib/oauth/linkedin.d.ts +2 -11
- package/dist/runtime/server/lib/oauth/linkedin.js +15 -36
- package/dist/runtime/server/lib/oauth/microsoft.d.ts +2 -11
- package/dist/runtime/server/lib/oauth/microsoft.js +16 -35
- package/dist/runtime/server/lib/oauth/paypal.js +17 -35
- package/dist/runtime/server/lib/oauth/spotify.js +17 -34
- package/dist/runtime/server/lib/oauth/steam.js +2 -6
- package/dist/runtime/server/lib/oauth/tiktok.d.ts +45 -0
- package/dist/runtime/server/lib/oauth/tiktok.js +84 -0
- package/dist/runtime/server/lib/oauth/twitch.js +16 -35
- package/dist/runtime/server/lib/oauth/x.d.ts +1 -6
- package/dist/runtime/server/lib/oauth/x.js +19 -60
- package/dist/runtime/server/lib/oauth/xsuaa.js +15 -34
- package/dist/runtime/server/lib/oauth/yandex.js +14 -36
- package/dist/runtime/server/lib/utils.d.ts +36 -0
- package/dist/runtime/server/lib/utils.js +46 -0
- package/dist/runtime/types/index.d.ts +1 -1
- package/dist/runtime/types/oauth-config.d.ts +3 -1
- package/package.json +12 -11
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
createError,
|
|
4
|
-
getQuery,
|
|
5
|
-
getRequestURL,
|
|
6
|
-
sendRedirect
|
|
7
|
-
} from "h3";
|
|
8
|
-
import { withQuery, parsePath } from "ufo";
|
|
1
|
+
import { eventHandler, getQuery, sendRedirect } from "h3";
|
|
2
|
+
import { withQuery } from "ufo";
|
|
9
3
|
import { defu } from "defu";
|
|
10
|
-
import {
|
|
4
|
+
import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
|
|
5
|
+
import { useRuntimeConfig, createError } from "#imports";
|
|
11
6
|
export function oauthKeycloakEventHandler({
|
|
12
7
|
config,
|
|
13
8
|
onSuccess,
|
|
@@ -18,7 +13,6 @@ export function oauthKeycloakEventHandler({
|
|
|
18
13
|
authorizationParams: {}
|
|
19
14
|
});
|
|
20
15
|
const query = getQuery(event);
|
|
21
|
-
const { code } = query;
|
|
22
16
|
if (query.error) {
|
|
23
17
|
const error = createError({
|
|
24
18
|
statusCode: 401,
|
|
@@ -29,18 +23,13 @@ export function oauthKeycloakEventHandler({
|
|
|
29
23
|
return onError(event, error);
|
|
30
24
|
}
|
|
31
25
|
if (!config.clientId || !config.clientSecret || !config.serverUrl || !config.realm) {
|
|
32
|
-
|
|
33
|
-
statusCode: 500,
|
|
34
|
-
message: "Missing NUXT_OAUTH_KEYCLOAK_CLIENT_ID or NUXT_OAUTH_KEYCLOAK_CLIENT_SECRET or NUXT_OAUTH_KEYCLOAK_SERVER_URL or NUXT_OAUTH_KEYCLOAK_REALM env variables."
|
|
35
|
-
});
|
|
36
|
-
if (!onError) throw error;
|
|
37
|
-
return onError(event, error);
|
|
26
|
+
return handleMissingConfiguration(event, "keycloak", ["clientId", "clientSecret", "serverUrl", "realm"], onError);
|
|
38
27
|
}
|
|
39
28
|
const realmURL = `${config.serverUrl}/realms/${config.realm}`;
|
|
40
29
|
const authorizationURL = `${realmURL}/protocol/openid-connect/auth`;
|
|
41
30
|
const tokenURL = `${realmURL}/protocol/openid-connect/token`;
|
|
42
|
-
const redirectURL = config.redirectURL ||
|
|
43
|
-
if (!code) {
|
|
31
|
+
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
32
|
+
if (!query.code) {
|
|
44
33
|
config.scope = config.scope || ["openid"];
|
|
45
34
|
return sendRedirect(
|
|
46
35
|
event,
|
|
@@ -57,29 +46,17 @@ export function oauthKeycloakEventHandler({
|
|
|
57
46
|
if (!config.scope.includes("openid")) {
|
|
58
47
|
config.scope.push("openid");
|
|
59
48
|
}
|
|
60
|
-
const tokens = await
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
64
|
-
},
|
|
65
|
-
body: new URLSearchParams({
|
|
49
|
+
const tokens = await requestAccessToken(tokenURL, {
|
|
50
|
+
body: {
|
|
51
|
+
grant_type: "authorization_code",
|
|
66
52
|
client_id: config.clientId,
|
|
67
53
|
client_secret: config.clientSecret,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}).toString()
|
|
72
|
-
}).catch((error) => {
|
|
73
|
-
return { error };
|
|
54
|
+
redirect_uri: redirectURL,
|
|
55
|
+
code: query.code
|
|
56
|
+
}
|
|
74
57
|
});
|
|
75
58
|
if (tokens.error) {
|
|
76
|
-
|
|
77
|
-
statusCode: 401,
|
|
78
|
-
message: `Keycloak login failed: ${tokens.error?.data?.error_description || "Unknown error"}`,
|
|
79
|
-
data: tokens
|
|
80
|
-
});
|
|
81
|
-
if (!onError) throw error;
|
|
82
|
-
return onError(event, error);
|
|
59
|
+
return handleAccessTokenErrorResponse(event, "keycloak", tokens, onError);
|
|
83
60
|
}
|
|
84
61
|
const accessToken = tokens.access_token;
|
|
85
62
|
const user = await $fetch(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { OAuthConfig } from '#auth-utils';
|
|
2
2
|
export interface OAuthLinkedInConfig {
|
|
3
3
|
/**
|
|
4
4
|
* LinkedIn OAuth Client ID
|
|
@@ -42,13 +42,4 @@ export interface OAuthLinkedInConfig {
|
|
|
42
42
|
*/
|
|
43
43
|
redirectURL?: string;
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
config?: OAuthLinkedInConfig;
|
|
47
|
-
onSuccess: (event: H3Event, result: {
|
|
48
|
-
user: any;
|
|
49
|
-
tokens: any;
|
|
50
|
-
}) => Promise<void> | void;
|
|
51
|
-
onError?: (event: H3Event, error: H3Error) => Promise<void> | void;
|
|
52
|
-
}
|
|
53
|
-
export declare function oauthLinkedInEventHandler({ config, onSuccess, onError }: OAuthConfig): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
54
|
-
export {};
|
|
45
|
+
export declare function oauthLinkedInEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthLinkedInConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { eventHandler,
|
|
2
|
-
import { withQuery
|
|
1
|
+
import { eventHandler, getQuery, sendRedirect } from "h3";
|
|
2
|
+
import { withQuery } from "ufo";
|
|
3
3
|
import { defu } from "defu";
|
|
4
|
+
import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
|
|
4
5
|
import { useRuntimeConfig } from "#imports";
|
|
5
6
|
export function oauthLinkedInEventHandler({ config, onSuccess, onError }) {
|
|
6
7
|
return eventHandler(async (event) => {
|
|
@@ -9,17 +10,12 @@ export function oauthLinkedInEventHandler({ config, onSuccess, onError }) {
|
|
|
9
10
|
tokenURL: "https://www.linkedin.com/oauth/v2/accessToken",
|
|
10
11
|
authorizationParams: {}
|
|
11
12
|
});
|
|
12
|
-
const
|
|
13
|
+
const query = getQuery(event);
|
|
13
14
|
if (!config.clientId || !config.clientSecret) {
|
|
14
|
-
|
|
15
|
-
statusCode: 500,
|
|
16
|
-
message: "Missing NUXT_OAUTH_LINKEDIN_CLIENT_ID or NUXT_OAUTH_LINKEDIN_CLIENT_SECRET env variables."
|
|
17
|
-
});
|
|
18
|
-
if (!onError) throw error;
|
|
19
|
-
return onError(event, error);
|
|
15
|
+
return handleMissingConfiguration(event, "linkedin", ["clientId", "clientSecret"], onError);
|
|
20
16
|
}
|
|
21
|
-
const redirectURL = config.redirectURL ||
|
|
22
|
-
if (!code) {
|
|
17
|
+
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
18
|
+
if (!query.code) {
|
|
23
19
|
config.scope = config.scope || [];
|
|
24
20
|
if (!config.scope.length) {
|
|
25
21
|
config.scope.push("profile", "openid", "email");
|
|
@@ -38,34 +34,17 @@ export function oauthLinkedInEventHandler({ config, onSuccess, onError }) {
|
|
|
38
34
|
})
|
|
39
35
|
);
|
|
40
36
|
}
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
49
|
-
},
|
|
50
|
-
body: new URLSearchParams({
|
|
51
|
-
grant_type: "authorization_code",
|
|
52
|
-
code,
|
|
53
|
-
client_id: config.clientId,
|
|
54
|
-
client_secret: config.clientSecret,
|
|
55
|
-
redirect_uri: stringifyParsedURL(parsedRedirectUrl)
|
|
56
|
-
}).toString()
|
|
37
|
+
const tokens = await requestAccessToken(config.tokenURL, {
|
|
38
|
+
body: {
|
|
39
|
+
grant_type: "authorization_code",
|
|
40
|
+
code: query.code,
|
|
41
|
+
client_id: config.clientId,
|
|
42
|
+
client_secret: config.clientSecret,
|
|
43
|
+
redirect_uri: redirectURL
|
|
57
44
|
}
|
|
58
|
-
).catch((error) => {
|
|
59
|
-
return { error };
|
|
60
45
|
});
|
|
61
46
|
if (tokens.error) {
|
|
62
|
-
|
|
63
|
-
statusCode: 401,
|
|
64
|
-
message: `LinkedIn login failed: ${tokens.error?.data?.error_description || "Unknown error"}`,
|
|
65
|
-
data: tokens
|
|
66
|
-
});
|
|
67
|
-
if (!onError) throw error;
|
|
68
|
-
return onError(event, error);
|
|
47
|
+
return handleAccessTokenErrorResponse(event, "linkedin", tokens, onError);
|
|
69
48
|
}
|
|
70
49
|
const accessToken = tokens.access_token;
|
|
71
50
|
const user = await $fetch("https://api.linkedin.com/v2/userinfo", {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { OAuthConfig } from '#auth-utils';
|
|
2
2
|
export interface OAuthMicrosoftConfig {
|
|
3
3
|
/**
|
|
4
4
|
* Microsoft OAuth Client ID
|
|
@@ -51,13 +51,4 @@ export interface OAuthMicrosoftConfig {
|
|
|
51
51
|
*/
|
|
52
52
|
redirectURL?: string;
|
|
53
53
|
}
|
|
54
|
-
|
|
55
|
-
config?: OAuthMicrosoftConfig;
|
|
56
|
-
onSuccess: (event: H3Event, result: {
|
|
57
|
-
user: any;
|
|
58
|
-
tokens: any;
|
|
59
|
-
}) => Promise<void> | void;
|
|
60
|
-
onError?: (event: H3Event, error: H3Error) => Promise<void> | void;
|
|
61
|
-
}
|
|
62
|
-
export declare function oauthMicrosoftEventHandler({ config, onSuccess, onError }: OAuthConfig): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
63
|
-
export {};
|
|
54
|
+
export declare function oauthMicrosoftEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthMicrosoftConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
@@ -1,25 +1,21 @@
|
|
|
1
|
-
import { eventHandler,
|
|
2
|
-
import { withQuery
|
|
1
|
+
import { eventHandler, getQuery, sendRedirect } from "h3";
|
|
2
|
+
import { withQuery } from "ufo";
|
|
3
3
|
import { defu } from "defu";
|
|
4
|
-
import {
|
|
4
|
+
import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
|
|
5
|
+
import { useRuntimeConfig, createError } from "#imports";
|
|
5
6
|
export function oauthMicrosoftEventHandler({ config, onSuccess, onError }) {
|
|
6
7
|
return eventHandler(async (event) => {
|
|
7
8
|
config = defu(config, useRuntimeConfig(event).oauth?.microsoft, {
|
|
8
9
|
authorizationParams: {}
|
|
9
10
|
});
|
|
10
|
-
const
|
|
11
|
+
const query = getQuery(event);
|
|
11
12
|
if (!config.clientId || !config.clientSecret || !config.tenant) {
|
|
12
|
-
|
|
13
|
-
statusCode: 500,
|
|
14
|
-
message: "Missing NUXT_OAUTH_MICROSOFT_CLIENT_ID or NUXT_OAUTH_MICROSOFT_CLIENT_SECRET or NUXT_OAUTH_MICROSOFT_TENANT env variables."
|
|
15
|
-
});
|
|
16
|
-
if (!onError) throw error;
|
|
17
|
-
return onError(event, error);
|
|
13
|
+
return handleMissingConfiguration(event, "microsoft", ["clientId", "clientSecret", "tenant"], onError);
|
|
18
14
|
}
|
|
19
15
|
const authorizationURL = config.authorizationURL || `https://login.microsoftonline.com/${config.tenant}/oauth2/v2.0/authorize`;
|
|
20
16
|
const tokenURL = config.tokenURL || `https://login.microsoftonline.com/${config.tenant}/oauth2/v2.0/token`;
|
|
21
|
-
const redirectURL = config.redirectURL ||
|
|
22
|
-
if (!code) {
|
|
17
|
+
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
18
|
+
if (!query.code) {
|
|
23
19
|
const scope = config.scope && config.scope.length > 0 ? config.scope : ["User.Read"];
|
|
24
20
|
return sendRedirect(
|
|
25
21
|
event,
|
|
@@ -32,32 +28,17 @@ export function oauthMicrosoftEventHandler({ config, onSuccess, onError }) {
|
|
|
32
28
|
})
|
|
33
29
|
);
|
|
34
30
|
}
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
tokenURL,
|
|
43
|
-
{
|
|
44
|
-
method: "POST",
|
|
45
|
-
headers: {
|
|
46
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
47
|
-
},
|
|
48
|
-
body: data
|
|
31
|
+
const tokens = await requestAccessToken(tokenURL, {
|
|
32
|
+
body: {
|
|
33
|
+
grant_type: "authorization_code",
|
|
34
|
+
code: query.code,
|
|
35
|
+
client_id: config.clientId,
|
|
36
|
+
client_secret: config.clientSecret,
|
|
37
|
+
redirect_uri: redirectURL
|
|
49
38
|
}
|
|
50
|
-
).catch((error) => {
|
|
51
|
-
return { error };
|
|
52
39
|
});
|
|
53
40
|
if (tokens.error) {
|
|
54
|
-
|
|
55
|
-
statusCode: 401,
|
|
56
|
-
message: `Microsoft login failed: ${tokens.error?.data?.error_description || "Unknown error"}`,
|
|
57
|
-
data: tokens
|
|
58
|
-
});
|
|
59
|
-
if (!onError) throw error;
|
|
60
|
-
return onError(event, error);
|
|
41
|
+
return handleAccessTokenErrorResponse(event, "microsoft", tokens, onError);
|
|
61
42
|
}
|
|
62
43
|
const tokenType = tokens.token_type;
|
|
63
44
|
const accessToken = tokens.access_token;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { eventHandler,
|
|
2
|
-
import { withQuery
|
|
1
|
+
import { eventHandler, getQuery, sendRedirect } from "h3";
|
|
2
|
+
import { withQuery } from "ufo";
|
|
3
3
|
import { defu } from "defu";
|
|
4
|
-
import {
|
|
4
|
+
import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
|
|
5
|
+
import { useRuntimeConfig, createError } from "#imports";
|
|
5
6
|
export function oauthPaypalEventHandler({ config, onSuccess, onError }) {
|
|
6
7
|
return eventHandler(async (event) => {
|
|
7
8
|
config = defu(config, useRuntimeConfig(event).oauth?.paypal, {
|
|
@@ -10,14 +11,9 @@ export function oauthPaypalEventHandler({ config, onSuccess, onError }) {
|
|
|
10
11
|
tokenURL: "https://api-m.paypal.com/v1/oauth2/token",
|
|
11
12
|
authorizationParams: {}
|
|
12
13
|
});
|
|
13
|
-
const
|
|
14
|
+
const query = getQuery(event);
|
|
14
15
|
if (!config.clientId) {
|
|
15
|
-
|
|
16
|
-
statusCode: 500,
|
|
17
|
-
message: "Missing NUXT_OAUTH_PAYPAL_CLIENT_ID env variables."
|
|
18
|
-
});
|
|
19
|
-
if (!onError) throw error;
|
|
20
|
-
return onError(event, error);
|
|
16
|
+
return handleMissingConfiguration(event, "paypal", ["clientId"], onError);
|
|
21
17
|
}
|
|
22
18
|
let paypalAPI = "api-m.paypal.com";
|
|
23
19
|
if (config.sandbox) {
|
|
@@ -25,8 +21,8 @@ export function oauthPaypalEventHandler({ config, onSuccess, onError }) {
|
|
|
25
21
|
config.authorizationURL = "https://www.sandbox.paypal.com/signin/authorize";
|
|
26
22
|
config.tokenURL = `https://${paypalAPI}/v1/oauth2/token`;
|
|
27
23
|
}
|
|
28
|
-
const redirectURL = config.redirectURL ||
|
|
29
|
-
if (!code) {
|
|
24
|
+
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
25
|
+
if (!query.code) {
|
|
30
26
|
config.scope = config.scope || [];
|
|
31
27
|
if (!config.scope.includes("openid")) {
|
|
32
28
|
config.scope.push("openid");
|
|
@@ -46,32 +42,18 @@ export function oauthPaypalEventHandler({ config, onSuccess, onError }) {
|
|
|
46
42
|
})
|
|
47
43
|
);
|
|
48
44
|
}
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
},
|
|
58
|
-
params: {
|
|
59
|
-
grant_type: "authorization_code",
|
|
60
|
-
redirect_uri: encodeURIComponent(parsePath(redirectURL).pathname),
|
|
61
|
-
code
|
|
62
|
-
}
|
|
45
|
+
const tokens = await requestAccessToken(config.tokenURL, {
|
|
46
|
+
headers: {
|
|
47
|
+
Authorization: `Basic ${Buffer.from(`${config.clientId}:${config.clientSecret}`).toString("base64")}`
|
|
48
|
+
},
|
|
49
|
+
params: {
|
|
50
|
+
grant_type: "authorization_code",
|
|
51
|
+
redirect_uri: redirectURL,
|
|
52
|
+
code: query.code
|
|
63
53
|
}
|
|
64
|
-
).catch((error) => {
|
|
65
|
-
return { error };
|
|
66
54
|
});
|
|
67
55
|
if (tokens.error) {
|
|
68
|
-
|
|
69
|
-
statusCode: 401,
|
|
70
|
-
message: `PayPal login failed: ${tokens.error?.data?.error_description || "Unknown error"}`,
|
|
71
|
-
data: tokens
|
|
72
|
-
});
|
|
73
|
-
if (!onError) throw error;
|
|
74
|
-
return onError(event, error);
|
|
56
|
+
return handleAccessTokenErrorResponse(event, "paypal", tokens, onError);
|
|
75
57
|
}
|
|
76
58
|
const accessToken = tokens.access_token;
|
|
77
59
|
const users = await $fetch(`https://${paypalAPI}/v1/identity/openidconnect/userinfo`, {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { eventHandler,
|
|
2
|
-
import { withQuery
|
|
1
|
+
import { eventHandler, getQuery, sendRedirect } from "h3";
|
|
2
|
+
import { withQuery } from "ufo";
|
|
3
3
|
import { defu } from "defu";
|
|
4
|
+
import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
|
|
4
5
|
import { useRuntimeConfig } from "#imports";
|
|
5
6
|
export function oauthSpotifyEventHandler({ config, onSuccess, onError }) {
|
|
6
7
|
return eventHandler(async (event) => {
|
|
@@ -9,17 +10,12 @@ export function oauthSpotifyEventHandler({ config, onSuccess, onError }) {
|
|
|
9
10
|
tokenURL: "https://accounts.spotify.com/api/token",
|
|
10
11
|
authorizationParams: {}
|
|
11
12
|
});
|
|
12
|
-
const
|
|
13
|
+
const query = getQuery(event);
|
|
13
14
|
if (!config.clientId || !config.clientSecret) {
|
|
14
|
-
|
|
15
|
-
statusCode: 500,
|
|
16
|
-
message: "Missing NUXT_OAUTH_SPOTIFY_CLIENT_ID or NUXT_OAUTH_SPOTIFY_CLIENT_SECRET env variables."
|
|
17
|
-
});
|
|
18
|
-
if (!onError) throw error;
|
|
19
|
-
return onError(event, error);
|
|
15
|
+
return handleMissingConfiguration(event, "spotify", ["clientId", "clientSecret"], onError);
|
|
20
16
|
}
|
|
21
|
-
const redirectURL = config.redirectURL ||
|
|
22
|
-
if (!code) {
|
|
17
|
+
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
18
|
+
if (!query.code) {
|
|
23
19
|
config.scope = config.scope || [];
|
|
24
20
|
if (config.emailRequired && !config.scope.includes("user-read-email")) {
|
|
25
21
|
config.scope.push("user-read-email");
|
|
@@ -35,32 +31,19 @@ export function oauthSpotifyEventHandler({ config, onSuccess, onError }) {
|
|
|
35
31
|
})
|
|
36
32
|
);
|
|
37
33
|
}
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
params: {
|
|
48
|
-
grant_type: "authorization_code",
|
|
49
|
-
redirect_uri: parsePath(redirectURL).pathname,
|
|
50
|
-
code
|
|
51
|
-
}
|
|
34
|
+
const tokens = await requestAccessToken(config.tokenURL, {
|
|
35
|
+
headers: {
|
|
36
|
+
Authorization: `Basic ${Buffer.from(`${config.clientId}:${config.clientSecret}`).toString("base64")}`
|
|
37
|
+
},
|
|
38
|
+
body: {
|
|
39
|
+
client_id: config.clientId,
|
|
40
|
+
grant_type: "authorization_code",
|
|
41
|
+
redirect_uri: redirectURL,
|
|
42
|
+
code: query.code
|
|
52
43
|
}
|
|
53
|
-
).catch((error) => {
|
|
54
|
-
return { error };
|
|
55
44
|
});
|
|
56
45
|
if (tokens.error) {
|
|
57
|
-
|
|
58
|
-
statusCode: 401,
|
|
59
|
-
message: `Spotify login failed: ${tokens.error?.data?.error_description || "Unknown error"}`,
|
|
60
|
-
data: tokens
|
|
61
|
-
});
|
|
62
|
-
if (!onError) throw error;
|
|
63
|
-
return onError(event, error);
|
|
46
|
+
return handleAccessTokenErrorResponse(event, "spotify", tokens, onError);
|
|
64
47
|
}
|
|
65
48
|
const accessToken = tokens.access_token;
|
|
66
49
|
const user = await $fetch("https://api.spotify.com/v1/me", {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
|
|
2
2
|
import { withQuery } from "ufo";
|
|
3
3
|
import { defu } from "defu";
|
|
4
|
+
import { handleMissingConfiguration } from "../utils.js";
|
|
4
5
|
import { useRuntimeConfig } from "#imports";
|
|
5
6
|
export function oauthSteamEventHandler({ config, onSuccess, onError }) {
|
|
6
7
|
return eventHandler(async (event) => {
|
|
@@ -9,12 +10,7 @@ export function oauthSteamEventHandler({ config, onSuccess, onError }) {
|
|
|
9
10
|
});
|
|
10
11
|
const query = getQuery(event);
|
|
11
12
|
if (!config.apiKey) {
|
|
12
|
-
|
|
13
|
-
statusCode: 500,
|
|
14
|
-
message: "Missing NUXT_OAUTH_STEAM_API_KEY env variable."
|
|
15
|
-
});
|
|
16
|
-
if (!onError) throw error;
|
|
17
|
-
return onError(event, error);
|
|
13
|
+
return handleMissingConfiguration(event, "steam", ["apiKey"], onError);
|
|
18
14
|
}
|
|
19
15
|
if (!query["openid.claimed_id"]) {
|
|
20
16
|
const redirectURL = config.redirectURL || getRequestURL(event).href;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { OAuthConfig } from '#auth-utils';
|
|
2
|
+
export interface OAuthTikTokConfig {
|
|
3
|
+
/**
|
|
4
|
+
* TikTok Client Key
|
|
5
|
+
* @default process.env.NUXT_OAUTH_TIKTOK_CLIENT_KEY
|
|
6
|
+
*/
|
|
7
|
+
clientKey?: string;
|
|
8
|
+
/**
|
|
9
|
+
* TikTok OAuth Client Secret
|
|
10
|
+
* @default process.env.NUXT_OAUTH_TIKTOK_CLIENT_SECRET
|
|
11
|
+
*/
|
|
12
|
+
clientSecret?: string;
|
|
13
|
+
/**
|
|
14
|
+
* TikTok OAuth Scope
|
|
15
|
+
* @default []
|
|
16
|
+
* @see https://developers.tiktok.com/doc/tiktok-api-scopes/
|
|
17
|
+
* @example ['user.info.basic']
|
|
18
|
+
*/
|
|
19
|
+
scope?: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Use TikTok sandbox environment.
|
|
22
|
+
* If true it will use Login Kit for Desktop, if false it will use Login Kit for Web.
|
|
23
|
+
* This is because Login Kit for Web doesn't support localhost or IP addresses as redirect URIs.
|
|
24
|
+
* @see https://developers.tiktok.com/doc/login-kit-web/
|
|
25
|
+
* @see https://developers.tiktok.com/doc/login-kit-desktop/
|
|
26
|
+
* @default import.meta.dev // true in development, false in production
|
|
27
|
+
*/
|
|
28
|
+
sandbox?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* TikTok OAuth Authorization URL
|
|
31
|
+
* @default 'https://www.tiktok.com/v2/auth/authorize/'
|
|
32
|
+
*/
|
|
33
|
+
authorizationURL?: string;
|
|
34
|
+
/**
|
|
35
|
+
* TikTok OAuth Token URL
|
|
36
|
+
* @default 'https://open.tiktokapis.com/v2/oauth/token/'
|
|
37
|
+
*/
|
|
38
|
+
tokenURL?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Redirect URL to to allow overriding for situations like prod failing to determine public hostname
|
|
41
|
+
* @default process.env.NUXT_OAUTH_TIKTOK_REDIRECT_URL or current URL
|
|
42
|
+
*/
|
|
43
|
+
redirectURL?: string;
|
|
44
|
+
}
|
|
45
|
+
export declare function oauthTikTokEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthTikTokConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { eventHandler, getQuery, sendRedirect } from "h3";
|
|
2
|
+
import { withQuery } from "ufo";
|
|
3
|
+
import { defu } from "defu";
|
|
4
|
+
import { sha256 } from "ohash";
|
|
5
|
+
import { handleAccessTokenErrorResponse, handleMissingConfiguration, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
|
|
6
|
+
import { useRuntimeConfig, createError } from "#imports";
|
|
7
|
+
export function oauthTikTokEventHandler({ config, onSuccess, onError }) {
|
|
8
|
+
return eventHandler(async (event) => {
|
|
9
|
+
config = defu(config, useRuntimeConfig(event).oauth?.tiktok, {
|
|
10
|
+
sandbox: import.meta.dev,
|
|
11
|
+
authorizationURL: "https://www.tiktok.com/v2/auth/authorize/",
|
|
12
|
+
tokenURL: "https://open.tiktokapis.com/v2/oauth/token/"
|
|
13
|
+
});
|
|
14
|
+
const query = getQuery(event);
|
|
15
|
+
if (!config.clientKey || !config.clientSecret) {
|
|
16
|
+
return handleMissingConfiguration(event, "tiktok", ["clientKey", "clientSecret"], onError);
|
|
17
|
+
}
|
|
18
|
+
const codeVerifier = "verify";
|
|
19
|
+
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
20
|
+
if (!query.code) {
|
|
21
|
+
config.scope = config.scope || [];
|
|
22
|
+
if (!config.scope.includes("user.info.basic")) {
|
|
23
|
+
config.scope.push("user.info.basic");
|
|
24
|
+
}
|
|
25
|
+
return sendRedirect(
|
|
26
|
+
event,
|
|
27
|
+
withQuery(config.authorizationURL, {
|
|
28
|
+
response_type: "code",
|
|
29
|
+
client_key: config.clientKey,
|
|
30
|
+
redirect_uri: redirectURL,
|
|
31
|
+
scope: config.scope.join(","),
|
|
32
|
+
...config.sandbox ? {
|
|
33
|
+
code_verifier: codeVerifier,
|
|
34
|
+
code_challenge: sha256(codeVerifier),
|
|
35
|
+
code_challenge_method: "S256"
|
|
36
|
+
} : {}
|
|
37
|
+
})
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
const tokens = await requestAccessToken(
|
|
41
|
+
config.tokenURL,
|
|
42
|
+
{
|
|
43
|
+
body: {
|
|
44
|
+
grant_type: "authorization_code",
|
|
45
|
+
redirect_uri: redirectURL,
|
|
46
|
+
client_key: config.clientKey,
|
|
47
|
+
client_secret: config.clientSecret,
|
|
48
|
+
code: query.code,
|
|
49
|
+
...config.sandbox ? { code_verifier: codeVerifier } : {}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
if (tokens.error) {
|
|
54
|
+
return handleAccessTokenErrorResponse(event, "tiktok", tokens, onError);
|
|
55
|
+
}
|
|
56
|
+
const accessToken = tokens.access_token;
|
|
57
|
+
const userInfoFieldsByScope = {
|
|
58
|
+
"user.info.basic": ["open_id", "union_id", "avatar_url", "avatar_url_100", "avatar_large_url", "display_name"],
|
|
59
|
+
"user.info.profile": ["bio_description", "profile_deep_link", "is_verified", "username"],
|
|
60
|
+
"user.info.stats": ["follower_count", "following_count", "likes_count", "video_count"]
|
|
61
|
+
};
|
|
62
|
+
const userInfo = await $fetch(withQuery("https://open.tiktokapis.com/v2/user/info/", {
|
|
63
|
+
fields: config.scope?.map((scope) => userInfoFieldsByScope[scope]).flat().join(",")
|
|
64
|
+
}), {
|
|
65
|
+
headers: {
|
|
66
|
+
Authorization: `Bearer ${accessToken}`
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
const user = userInfo?.data?.user;
|
|
70
|
+
if (!user) {
|
|
71
|
+
const error = createError({
|
|
72
|
+
statusCode: 500,
|
|
73
|
+
message: "Could not get TikTok user",
|
|
74
|
+
data: tokens
|
|
75
|
+
});
|
|
76
|
+
if (!onError) throw error;
|
|
77
|
+
return onError(event, error);
|
|
78
|
+
}
|
|
79
|
+
return onSuccess(event, {
|
|
80
|
+
tokens,
|
|
81
|
+
user
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { eventHandler,
|
|
2
|
-
import { withQuery
|
|
1
|
+
import { eventHandler, getQuery, sendRedirect } from "h3";
|
|
2
|
+
import { withQuery } from "ufo";
|
|
3
3
|
import { defu } from "defu";
|
|
4
|
-
import {
|
|
4
|
+
import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
|
|
5
|
+
import { useRuntimeConfig, createError } from "#imports";
|
|
5
6
|
export function oauthTwitchEventHandler({ config, onSuccess, onError }) {
|
|
6
7
|
return eventHandler(async (event) => {
|
|
7
8
|
config = defu(config, useRuntimeConfig(event).oauth?.twitch, {
|
|
@@ -9,17 +10,12 @@ export function oauthTwitchEventHandler({ config, onSuccess, onError }) {
|
|
|
9
10
|
tokenURL: "https://id.twitch.tv/oauth2/token",
|
|
10
11
|
authorizationParams: {}
|
|
11
12
|
});
|
|
12
|
-
const
|
|
13
|
+
const query = getQuery(event);
|
|
13
14
|
if (!config.clientId) {
|
|
14
|
-
|
|
15
|
-
statusCode: 500,
|
|
16
|
-
message: "Missing NUXT_OAUTH_TWITCH_CLIENT_ID env variables."
|
|
17
|
-
});
|
|
18
|
-
if (!onError) throw error;
|
|
19
|
-
return onError(event, error);
|
|
15
|
+
return handleMissingConfiguration(event, "twitch", ["clientId"], onError);
|
|
20
16
|
}
|
|
21
|
-
const redirectURL = config.redirectURL ||
|
|
22
|
-
if (!code) {
|
|
17
|
+
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
18
|
+
if (!query.code) {
|
|
23
19
|
config.scope = config.scope || [];
|
|
24
20
|
if (config.emailRequired && !config.scope.includes("user:read:email")) {
|
|
25
21
|
config.scope.push("user:read:email");
|
|
@@ -35,32 +31,17 @@ export function oauthTwitchEventHandler({ config, onSuccess, onError }) {
|
|
|
35
31
|
})
|
|
36
32
|
);
|
|
37
33
|
}
|
|
38
|
-
const tokens = await
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
params: {
|
|
46
|
-
grant_type: "authorization_code",
|
|
47
|
-
redirect_uri: parsePath(redirectURL).pathname,
|
|
48
|
-
client_id: config.clientId,
|
|
49
|
-
client_secret: config.clientSecret,
|
|
50
|
-
code
|
|
51
|
-
}
|
|
34
|
+
const tokens = await requestAccessToken(config.tokenURL, {
|
|
35
|
+
body: {
|
|
36
|
+
grant_type: "authorization_code",
|
|
37
|
+
redirect_uri: redirectURL,
|
|
38
|
+
client_id: config.clientId,
|
|
39
|
+
client_secret: config.clientSecret,
|
|
40
|
+
code: query.code
|
|
52
41
|
}
|
|
53
|
-
).catch((error) => {
|
|
54
|
-
return { error };
|
|
55
42
|
});
|
|
56
43
|
if (tokens.error) {
|
|
57
|
-
|
|
58
|
-
statusCode: 401,
|
|
59
|
-
message: `Twitch login failed: ${tokens.error?.data?.error_description || "Unknown error"}`,
|
|
60
|
-
data: tokens
|
|
61
|
-
});
|
|
62
|
-
if (!onError) throw error;
|
|
63
|
-
return onError(event, error);
|
|
44
|
+
return handleAccessTokenErrorResponse(event, "twitch", tokens, onError);
|
|
64
45
|
}
|
|
65
46
|
const accessToken = tokens.access_token;
|
|
66
47
|
const users = await $fetch("https://api.twitch.tv/helix/users", {
|