nuxt-auth-utils 0.3.5 → 0.3.7
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 -0
- package/dist/runtime/server/lib/oauth/auth0.js +16 -22
- package/dist/runtime/server/lib/oauth/battledotnet.js +16 -25
- package/dist/runtime/server/lib/oauth/cognito.js +15 -15
- package/dist/runtime/server/lib/oauth/discord.js +13 -24
- package/dist/runtime/server/lib/oauth/facebook.js +6 -12
- package/dist/runtime/server/lib/oauth/github.js +12 -14
- package/dist/runtime/server/lib/oauth/google.js +14 -23
- package/dist/runtime/server/lib/oauth/keycloak.js +12 -25
- package/dist/runtime/server/lib/oauth/linkedin.d.ts +2 -11
- package/dist/runtime/server/lib/oauth/linkedin.js +13 -24
- package/dist/runtime/server/lib/oauth/microsoft.d.ts +2 -11
- package/dist/runtime/server/lib/oauth/microsoft.js +14 -23
- package/dist/runtime/server/lib/oauth/paypal.js +16 -23
- package/dist/runtime/server/lib/oauth/spotify.js +15 -22
- 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 +14 -23
- package/dist/runtime/server/lib/oauth/x.js +14 -21
- package/dist/runtime/server/lib/oauth/xsuaa.js +13 -22
- package/dist/runtime/server/lib/oauth/yandex.js +12 -23
- package/dist/runtime/server/lib/utils.d.ts +28 -1
- package/dist/runtime/server/lib/utils.js +25 -0
- package/dist/runtime/types/oauth-config.d.ts +1 -1
- package/package.json +8 -8
|
@@ -1,21 +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 { handleAccessTokenErrorResponse,
|
|
5
|
-
import { useRuntimeConfig } from "#imports";
|
|
4
|
+
import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
|
|
5
|
+
import { useRuntimeConfig, createError } from "#imports";
|
|
6
6
|
export function oauthMicrosoftEventHandler({ config, onSuccess, onError }) {
|
|
7
7
|
return eventHandler(async (event) => {
|
|
8
8
|
config = defu(config, useRuntimeConfig(event).oauth?.microsoft, {
|
|
9
9
|
authorizationParams: {}
|
|
10
10
|
});
|
|
11
|
-
const
|
|
11
|
+
const query = getQuery(event);
|
|
12
12
|
if (!config.clientId || !config.clientSecret || !config.tenant) {
|
|
13
13
|
return handleMissingConfiguration(event, "microsoft", ["clientId", "clientSecret", "tenant"], onError);
|
|
14
14
|
}
|
|
15
15
|
const authorizationURL = config.authorizationURL || `https://login.microsoftonline.com/${config.tenant}/oauth2/v2.0/authorize`;
|
|
16
16
|
const tokenURL = config.tokenURL || `https://login.microsoftonline.com/${config.tenant}/oauth2/v2.0/token`;
|
|
17
|
-
const redirectURL = config.redirectURL ||
|
|
18
|
-
if (!code) {
|
|
17
|
+
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
18
|
+
if (!query.code) {
|
|
19
19
|
const scope = config.scope && config.scope.length > 0 ? config.scope : ["User.Read"];
|
|
20
20
|
return sendRedirect(
|
|
21
21
|
event,
|
|
@@ -28,23 +28,14 @@ export function oauthMicrosoftEventHandler({ config, onSuccess, onError }) {
|
|
|
28
28
|
})
|
|
29
29
|
);
|
|
30
30
|
}
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
tokenURL,
|
|
39
|
-
{
|
|
40
|
-
method: "POST",
|
|
41
|
-
headers: {
|
|
42
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
43
|
-
},
|
|
44
|
-
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
|
|
45
38
|
}
|
|
46
|
-
).catch((error) => {
|
|
47
|
-
return { error };
|
|
48
39
|
});
|
|
49
40
|
if (tokens.error) {
|
|
50
41
|
return handleAccessTokenErrorResponse(event, "microsoft", tokens, onError);
|
|
@@ -1,8 +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 { handleAccessTokenErrorResponse,
|
|
5
|
-
import { useRuntimeConfig } from "#imports";
|
|
4
|
+
import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
|
|
5
|
+
import { useRuntimeConfig, createError } from "#imports";
|
|
6
6
|
export function oauthPaypalEventHandler({ config, onSuccess, onError }) {
|
|
7
7
|
return eventHandler(async (event) => {
|
|
8
8
|
config = defu(config, useRuntimeConfig(event).oauth?.paypal, {
|
|
@@ -11,7 +11,7 @@ export function oauthPaypalEventHandler({ config, onSuccess, onError }) {
|
|
|
11
11
|
tokenURL: "https://api-m.paypal.com/v1/oauth2/token",
|
|
12
12
|
authorizationParams: {}
|
|
13
13
|
});
|
|
14
|
-
const
|
|
14
|
+
const query = getQuery(event);
|
|
15
15
|
if (!config.clientId) {
|
|
16
16
|
return handleMissingConfiguration(event, "paypal", ["clientId"], onError);
|
|
17
17
|
}
|
|
@@ -21,8 +21,8 @@ export function oauthPaypalEventHandler({ config, onSuccess, onError }) {
|
|
|
21
21
|
config.authorizationURL = "https://www.sandbox.paypal.com/signin/authorize";
|
|
22
22
|
config.tokenURL = `https://${paypalAPI}/v1/oauth2/token`;
|
|
23
23
|
}
|
|
24
|
-
const redirectURL = config.redirectURL ||
|
|
25
|
-
if (!code) {
|
|
24
|
+
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
25
|
+
if (!query.code) {
|
|
26
26
|
config.scope = config.scope || [];
|
|
27
27
|
if (!config.scope.includes("openid")) {
|
|
28
28
|
config.scope.push("openid");
|
|
@@ -42,23 +42,16 @@ export function oauthPaypalEventHandler({ config, onSuccess, onError }) {
|
|
|
42
42
|
})
|
|
43
43
|
);
|
|
44
44
|
}
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
params: {
|
|
55
|
-
grant_type: "authorization_code",
|
|
56
|
-
redirect_uri: encodeURIComponent(parsePath(redirectURL).pathname),
|
|
57
|
-
code
|
|
58
|
-
}
|
|
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: encodeURIComponent(redirectURL),
|
|
52
|
+
// <- PayPal requires to be URL encoded
|
|
53
|
+
code: query.code
|
|
59
54
|
}
|
|
60
|
-
).catch((error) => {
|
|
61
|
-
return { error };
|
|
62
55
|
});
|
|
63
56
|
if (tokens.error) {
|
|
64
57
|
return handleAccessTokenErrorResponse(event, "paypal", tokens, onError);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { eventHandler, getQuery,
|
|
2
|
-
import { withQuery
|
|
1
|
+
import { eventHandler, getQuery, sendRedirect } from "h3";
|
|
2
|
+
import { withQuery } from "ufo";
|
|
3
3
|
import { defu } from "defu";
|
|
4
|
-
import { handleAccessTokenErrorResponse,
|
|
4
|
+
import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
|
|
5
5
|
import { useRuntimeConfig } from "#imports";
|
|
6
6
|
export function oauthSpotifyEventHandler({ config, onSuccess, onError }) {
|
|
7
7
|
return eventHandler(async (event) => {
|
|
@@ -10,12 +10,12 @@ export function oauthSpotifyEventHandler({ config, onSuccess, onError }) {
|
|
|
10
10
|
tokenURL: "https://accounts.spotify.com/api/token",
|
|
11
11
|
authorizationParams: {}
|
|
12
12
|
});
|
|
13
|
-
const
|
|
13
|
+
const query = getQuery(event);
|
|
14
14
|
if (!config.clientId || !config.clientSecret) {
|
|
15
15
|
return handleMissingConfiguration(event, "spotify", ["clientId", "clientSecret"], onError);
|
|
16
16
|
}
|
|
17
|
-
const redirectURL = config.redirectURL ||
|
|
18
|
-
if (!code) {
|
|
17
|
+
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
18
|
+
if (!query.code) {
|
|
19
19
|
config.scope = config.scope || [];
|
|
20
20
|
if (config.emailRequired && !config.scope.includes("user-read-email")) {
|
|
21
21
|
config.scope.push("user-read-email");
|
|
@@ -31,23 +31,16 @@ export function oauthSpotifyEventHandler({ config, onSuccess, onError }) {
|
|
|
31
31
|
})
|
|
32
32
|
);
|
|
33
33
|
}
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
params: {
|
|
44
|
-
grant_type: "authorization_code",
|
|
45
|
-
redirect_uri: parsePath(redirectURL).pathname,
|
|
46
|
-
code
|
|
47
|
-
}
|
|
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
|
|
48
43
|
}
|
|
49
|
-
).catch((error) => {
|
|
50
|
-
return { error };
|
|
51
44
|
});
|
|
52
45
|
if (tokens.error) {
|
|
53
46
|
return handleAccessTokenErrorResponse(event, "spotify", tokens, onError);
|
|
@@ -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,8 +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 { handleAccessTokenErrorResponse,
|
|
5
|
-
import { useRuntimeConfig } from "#imports";
|
|
4
|
+
import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
|
|
5
|
+
import { useRuntimeConfig, createError } from "#imports";
|
|
6
6
|
export function oauthTwitchEventHandler({ config, onSuccess, onError }) {
|
|
7
7
|
return eventHandler(async (event) => {
|
|
8
8
|
config = defu(config, useRuntimeConfig(event).oauth?.twitch, {
|
|
@@ -10,12 +10,12 @@ export function oauthTwitchEventHandler({ config, onSuccess, onError }) {
|
|
|
10
10
|
tokenURL: "https://id.twitch.tv/oauth2/token",
|
|
11
11
|
authorizationParams: {}
|
|
12
12
|
});
|
|
13
|
-
const
|
|
13
|
+
const query = getQuery(event);
|
|
14
14
|
if (!config.clientId) {
|
|
15
15
|
return handleMissingConfiguration(event, "twitch", ["clientId"], onError);
|
|
16
16
|
}
|
|
17
|
-
const redirectURL = config.redirectURL ||
|
|
18
|
-
if (!code) {
|
|
17
|
+
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
18
|
+
if (!query.code) {
|
|
19
19
|
config.scope = config.scope || [];
|
|
20
20
|
if (config.emailRequired && !config.scope.includes("user:read:email")) {
|
|
21
21
|
config.scope.push("user:read:email");
|
|
@@ -31,23 +31,14 @@ export function oauthTwitchEventHandler({ config, onSuccess, onError }) {
|
|
|
31
31
|
})
|
|
32
32
|
);
|
|
33
33
|
}
|
|
34
|
-
const tokens = await
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
params: {
|
|
42
|
-
grant_type: "authorization_code",
|
|
43
|
-
redirect_uri: parsePath(redirectURL).pathname,
|
|
44
|
-
client_id: config.clientId,
|
|
45
|
-
client_secret: config.clientSecret,
|
|
46
|
-
code
|
|
47
|
-
}
|
|
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
|
|
48
41
|
}
|
|
49
|
-
).catch((error) => {
|
|
50
|
-
return { error };
|
|
51
42
|
});
|
|
52
43
|
if (tokens.error) {
|
|
53
44
|
return handleAccessTokenErrorResponse(event, "twitch", tokens, onError);
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
1
|
import {
|
|
3
2
|
eventHandler,
|
|
4
3
|
getQuery,
|
|
5
|
-
getRequestURL,
|
|
6
4
|
sendRedirect
|
|
7
5
|
} from "h3";
|
|
8
|
-
import { withQuery
|
|
6
|
+
import { withQuery } from "ufo";
|
|
9
7
|
import { defu } from "defu";
|
|
10
|
-
import {
|
|
8
|
+
import { randomUUID } from "uncrypto";
|
|
9
|
+
import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
|
|
11
10
|
import { useRuntimeConfig } from "#imports";
|
|
12
11
|
export function oauthXEventHandler({
|
|
13
12
|
config,
|
|
@@ -24,12 +23,12 @@ export function oauthXEventHandler({
|
|
|
24
23
|
code_challenge: randomUUID()
|
|
25
24
|
}
|
|
26
25
|
});
|
|
27
|
-
const
|
|
26
|
+
const query = getQuery(event);
|
|
28
27
|
if (!config.clientId) {
|
|
29
28
|
return handleMissingConfiguration(event, "x", ["clientId"], onError);
|
|
30
29
|
}
|
|
31
|
-
const redirectURL = config.redirectURL ||
|
|
32
|
-
if (!code) {
|
|
30
|
+
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
31
|
+
if (!query.code) {
|
|
33
32
|
config.scope = config.scope || ["tweet.read", "users.read", "offline.access"];
|
|
34
33
|
return sendRedirect(
|
|
35
34
|
event,
|
|
@@ -43,22 +42,16 @@ export function oauthXEventHandler({
|
|
|
43
42
|
})
|
|
44
43
|
);
|
|
45
44
|
}
|
|
46
|
-
const
|
|
47
|
-
grant_type: "authorization_code",
|
|
48
|
-
code_verifier: config.authorizationParams?.code_challenge,
|
|
49
|
-
redirect_uri: parsePath(redirectURL).pathname,
|
|
50
|
-
code
|
|
51
|
-
};
|
|
52
|
-
const authCode = Buffer.from(`${config.clientId}:${config.clientSecret}`).toString("base64");
|
|
53
|
-
const tokens = await $fetch(config.tokenURL, {
|
|
54
|
-
method: "POST",
|
|
45
|
+
const tokens = await requestAccessToken(config.tokenURL, {
|
|
55
46
|
headers: {
|
|
56
|
-
|
|
57
|
-
"Authorization": `Basic ${authCode}`
|
|
47
|
+
Authorization: `Basic ${Buffer.from(`${config.clientId}:${config.clientSecret}`).toString("base64")}`
|
|
58
48
|
},
|
|
59
|
-
params
|
|
60
|
-
|
|
61
|
-
|
|
49
|
+
params: {
|
|
50
|
+
grant_type: "authorization_code",
|
|
51
|
+
code_verifier: config.authorizationParams?.code_challenge,
|
|
52
|
+
redirect_uri: redirectURL,
|
|
53
|
+
code: query.code
|
|
54
|
+
}
|
|
62
55
|
});
|
|
63
56
|
if (tokens.error) {
|
|
64
57
|
return handleAccessTokenErrorResponse(event, "x", tokens, onError);
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { eventHandler, getQuery,
|
|
2
|
-
import { withQuery
|
|
1
|
+
import { eventHandler, getQuery, sendRedirect } from "h3";
|
|
2
|
+
import { withQuery } from "ufo";
|
|
3
3
|
import { defu } from "defu";
|
|
4
|
-
import { handleAccessTokenErrorResponse,
|
|
4
|
+
import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
|
|
5
5
|
import { useRuntimeConfig } from "#imports";
|
|
6
6
|
export function oauthXSUAAEventHandler({ config, onSuccess, onError }) {
|
|
7
7
|
return eventHandler(async (event) => {
|
|
8
8
|
config = defu(config, useRuntimeConfig(event).oauth?.xsuaa);
|
|
9
|
-
const
|
|
9
|
+
const query = getQuery(event);
|
|
10
10
|
if (!config.clientId || !config.clientSecret || !config.domain) {
|
|
11
11
|
return handleMissingConfiguration(event, "xsuaa", ["clientId", "clientSecret", "domain"], onError);
|
|
12
12
|
}
|
|
13
13
|
const authorizationURL = `https://${config.domain}/oauth/authorize`;
|
|
14
14
|
const tokenURL = `https://${config.domain}/oauth/token`;
|
|
15
|
-
const redirectURL = config.redirectURL ||
|
|
16
|
-
if (!code) {
|
|
15
|
+
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
16
|
+
if (!query.code) {
|
|
17
17
|
config.scope = config.scope || [];
|
|
18
18
|
return sendRedirect(
|
|
19
19
|
event,
|
|
@@ -25,23 +25,14 @@ export function oauthXSUAAEventHandler({ config, onSuccess, onError }) {
|
|
|
25
25
|
})
|
|
26
26
|
);
|
|
27
27
|
}
|
|
28
|
-
const tokens = await
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
body: new URLSearchParams({
|
|
36
|
-
grant_type: "authorization_code",
|
|
37
|
-
client_id: config.clientId,
|
|
38
|
-
client_secret: config.clientSecret,
|
|
39
|
-
redirect_uri: parsePath(redirectURL).pathname,
|
|
40
|
-
code: `${code}`
|
|
41
|
-
})
|
|
28
|
+
const tokens = await requestAccessToken(tokenURL, {
|
|
29
|
+
body: {
|
|
30
|
+
grant_type: "authorization_code",
|
|
31
|
+
client_id: config.clientId,
|
|
32
|
+
client_secret: config.clientSecret,
|
|
33
|
+
redirect_uri: redirectURL,
|
|
34
|
+
code: query.code
|
|
42
35
|
}
|
|
43
|
-
).catch((error) => {
|
|
44
|
-
return { error };
|
|
45
36
|
});
|
|
46
37
|
if (tokens.error) {
|
|
47
38
|
return handleAccessTokenErrorResponse(event, "auth0", tokens, onError);
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
getQuery,
|
|
4
|
-
getRequestURL,
|
|
5
|
-
sendRedirect
|
|
6
|
-
} from "h3";
|
|
7
|
-
import { withQuery, parseURL, stringifyParsedURL } from "ufo";
|
|
1
|
+
import { eventHandler, getQuery, sendRedirect } from "h3";
|
|
2
|
+
import { withQuery } from "ufo";
|
|
8
3
|
import { defu } from "defu";
|
|
9
|
-
import { handleAccessTokenErrorResponse,
|
|
4
|
+
import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
|
|
10
5
|
import { useRuntimeConfig } from "#imports";
|
|
11
6
|
export function oauthYandexEventHandler({
|
|
12
7
|
config,
|
|
@@ -19,12 +14,12 @@ export function oauthYandexEventHandler({
|
|
|
19
14
|
tokenURL: "https://oauth.yandex.ru/token",
|
|
20
15
|
userURL: "https://login.yandex.ru/info"
|
|
21
16
|
});
|
|
22
|
-
const
|
|
17
|
+
const query = getQuery(event);
|
|
23
18
|
if (!config.clientId || !config.clientSecret) {
|
|
24
19
|
return handleMissingConfiguration(event, "yandex", ["clientId", "clientSecret"], onError);
|
|
25
20
|
}
|
|
26
|
-
const redirectURL = config.redirectURL ||
|
|
27
|
-
if (!code) {
|
|
21
|
+
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
22
|
+
if (!query.code) {
|
|
28
23
|
config.scope = config.scope || [];
|
|
29
24
|
if (config.emailRequired && !config.scope.includes("login:email")) {
|
|
30
25
|
config.scope.push("login:email");
|
|
@@ -39,20 +34,14 @@ export function oauthYandexEventHandler({
|
|
|
39
34
|
})
|
|
40
35
|
);
|
|
41
36
|
}
|
|
42
|
-
const tokens = await
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
},
|
|
47
|
-
body: new URLSearchParams({
|
|
37
|
+
const tokens = await requestAccessToken(config.tokenURL, {
|
|
38
|
+
body: {
|
|
39
|
+
grant_type: "authorization_code",
|
|
40
|
+
code: query.code,
|
|
48
41
|
client_id: config.clientId,
|
|
49
42
|
client_secret: config.clientSecret,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
code
|
|
53
|
-
}).toString()
|
|
54
|
-
}).catch((error) => {
|
|
55
|
-
return { error };
|
|
43
|
+
redirect_uri: redirectURL
|
|
44
|
+
}
|
|
56
45
|
});
|
|
57
46
|
if (tokens.error) {
|
|
58
47
|
return handleAccessTokenErrorResponse(event, "yandex", tokens, onError);
|
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
import type { H3Event } from 'h3';
|
|
2
|
-
import type {
|
|
2
|
+
import type { OAuthProvider, OnError } from '#auth-utils';
|
|
3
|
+
export declare function getOAuthRedirectURL(event: H3Event): string;
|
|
4
|
+
/**
|
|
5
|
+
* Request an access token body.
|
|
6
|
+
*
|
|
7
|
+
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3
|
|
8
|
+
*/
|
|
9
|
+
export interface RequestAccessTokenBody {
|
|
10
|
+
grant_type: 'authorization_code';
|
|
11
|
+
code: string;
|
|
12
|
+
redirect_uri: string;
|
|
13
|
+
client_id: string;
|
|
14
|
+
client_secret?: string;
|
|
15
|
+
}
|
|
16
|
+
interface RequestAccessTokenOptions {
|
|
17
|
+
body?: RequestAccessTokenBody;
|
|
18
|
+
params?: Record<string, string | undefined>;
|
|
19
|
+
headers?: Record<string, string>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Request an access token from the OAuth provider.
|
|
23
|
+
*
|
|
24
|
+
* When an error occurs, only the error data is returned.
|
|
25
|
+
*
|
|
26
|
+
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3
|
|
27
|
+
*/
|
|
28
|
+
export declare function requestAccessToken(url: string, options: RequestAccessTokenOptions): Promise<any>;
|
|
3
29
|
/**
|
|
4
30
|
* Handle OAuth access token error response
|
|
5
31
|
*
|
|
@@ -7,3 +33,4 @@ import type { OnError, OAuthProvider } from '#auth-utils';
|
|
|
7
33
|
*/
|
|
8
34
|
export declare function handleAccessTokenErrorResponse(event: H3Event, oauthProvider: OAuthProvider, oauthError: any, onError?: OnError): void | Promise<void>;
|
|
9
35
|
export declare function handleMissingConfiguration(event: H3Event, provider: OAuthProvider, missingKeys: string[], onError?: OnError): void | Promise<void>;
|
|
36
|
+
export {};
|
|
@@ -1,5 +1,30 @@
|
|
|
1
|
+
import { getRequestURL } from "h3";
|
|
2
|
+
import { FetchError } from "ofetch";
|
|
1
3
|
import { snakeCase, upperFirst } from "scule";
|
|
2
4
|
import { createError } from "#imports";
|
|
5
|
+
export function getOAuthRedirectURL(event) {
|
|
6
|
+
const requestURL = getRequestURL(event);
|
|
7
|
+
return `${requestURL.protocol}//${requestURL.host}${requestURL.pathname}`;
|
|
8
|
+
}
|
|
9
|
+
export async function requestAccessToken(url, options) {
|
|
10
|
+
const headers = {
|
|
11
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
12
|
+
...options.headers
|
|
13
|
+
};
|
|
14
|
+
const body = headers["Content-Type"] === "application/x-www-form-urlencoded" ? new URLSearchParams(
|
|
15
|
+
options.body || options.params || {}
|
|
16
|
+
).toString() : options.body;
|
|
17
|
+
return $fetch(url, {
|
|
18
|
+
method: "POST",
|
|
19
|
+
headers,
|
|
20
|
+
body
|
|
21
|
+
}).catch((error) => {
|
|
22
|
+
if (error instanceof FetchError && error.status === 401) {
|
|
23
|
+
return error.data;
|
|
24
|
+
}
|
|
25
|
+
throw error;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
3
28
|
export function handleAccessTokenErrorResponse(event, oauthProvider, oauthError, onError) {
|
|
4
29
|
const message = `${upperFirst(oauthProvider)} login failed: ${oauthError.error_description || oauthError.error || "Unknown error"}`;
|
|
5
30
|
const error = createError({
|
|
@@ -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' | 'twitch' | 'x' | 'xsuaa' | 'yandex' | (string & {});
|
|
2
|
+
export type OAuthProvider = 'auth0' | 'battledotnet' | 'cognito' | 'discord' | 'facebook' | 'github' | 'google' | 'keycloak' | 'linkedin' | 'microsoft' | 'paypal' | 'spotify' | 'steam' | 'tiktok' | 'twitch' | '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;
|