nuxt-auth-utils 0.1.0 → 0.2.1
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 +18 -6
- package/dist/module.json +1 -1
- package/dist/module.mjs +25 -1
- package/dist/runtime/app/components/AuthState.vue +3 -4
- package/dist/runtime/server/lib/oauth/auth0.js +2 -3
- package/dist/runtime/server/lib/oauth/battledotnet.js +1 -2
- package/dist/runtime/server/lib/oauth/cognito.js +2 -3
- package/dist/runtime/server/lib/oauth/discord.js +2 -3
- package/dist/runtime/server/lib/oauth/facebook.js +1 -2
- package/dist/runtime/server/lib/oauth/github.js +2 -3
- package/dist/runtime/server/lib/oauth/google.js +2 -3
- package/dist/runtime/server/lib/oauth/keycloak.js +2 -3
- package/dist/runtime/server/lib/oauth/linkedin.js +2 -3
- package/dist/runtime/server/lib/oauth/microsoft.js +2 -3
- package/dist/runtime/server/lib/oauth/paypal.d.ts +47 -0
- package/dist/runtime/server/lib/oauth/paypal.js +104 -0
- package/dist/runtime/server/lib/oauth/spotify.js +2 -3
- package/dist/runtime/server/lib/oauth/steam.d.ts +15 -0
- package/dist/runtime/server/lib/oauth/steam.js +54 -0
- package/dist/runtime/server/lib/oauth/twitch.js +2 -3
- package/dist/runtime/server/lib/oauth/x.d.ts +46 -0
- package/dist/runtime/server/lib/oauth/x.js +122 -0
- package/dist/runtime/server/lib/oauth/xsuaa.d.ts +26 -0
- package/dist/runtime/server/lib/oauth/xsuaa.js +73 -0
- package/dist/runtime/server/lib/oauth/yandex.d.ts +41 -0
- package/dist/runtime/server/lib/oauth/yandex.js +84 -0
- package/dist/runtime/server/utils/oauth.d.ts +10 -0
- package/dist/runtime/server/utils/oauth.js +11 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[![License][license-src]][license-href]
|
|
6
6
|
[![Nuxt][nuxt-src]][nuxt-href]
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Add Authentication to Nuxt applications with secured & sealed cookies sessions.
|
|
9
9
|
|
|
10
10
|
- [Release Notes](/CHANGELOG.md)
|
|
11
11
|
- [Demo](https://github.com/atinux/nuxt-todos-edge)
|
|
@@ -14,13 +14,20 @@ Minimalist Authentication module for Nuxt exposing Vue composables and server ut
|
|
|
14
14
|
|
|
15
15
|
## Features
|
|
16
16
|
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
- [
|
|
17
|
+
- [Hybrid Rendering](#hybrid-rendering) support (SSR / CSR / SWR / Prerendering)
|
|
18
|
+
- [15+ OAuth Providers](#supported-oauth-providers)
|
|
19
|
+
- [Vue composable](#vue-composable)
|
|
20
|
+
- [Server utils](#server-utils)
|
|
21
|
+
- [`<AuthState>` component](#authstate-component)
|
|
22
|
+
- [Extendable with hooks](#extend-session)
|
|
20
23
|
|
|
21
24
|
## Requirements
|
|
22
25
|
|
|
23
|
-
This module only works with
|
|
26
|
+
This module only works with a Nuxt server running as it uses server API routes (`nuxt build`).
|
|
27
|
+
|
|
28
|
+
This means that you cannot use this module with `nuxt generate`.
|
|
29
|
+
|
|
30
|
+
You can anyway use [Hybrid Rendering](#hybrid-rendering) to pre-render pages of your application or disable server-side rendering completely.
|
|
24
31
|
|
|
25
32
|
## Quick Setup
|
|
26
33
|
|
|
@@ -41,7 +48,7 @@ Nuxt Auth Utils generates one for you when running Nuxt in development the first
|
|
|
41
48
|
|
|
42
49
|
3. That's it! You can now add authentication to your Nuxt app ✨
|
|
43
50
|
|
|
44
|
-
## Vue
|
|
51
|
+
## Vue Composable
|
|
45
52
|
|
|
46
53
|
Nuxt Auth Utils automatically adds some plugins to fetch the current user session to let you access it from your Vue components.
|
|
47
54
|
|
|
@@ -153,8 +160,13 @@ It can also be set using environment variables:
|
|
|
153
160
|
- Keycloak
|
|
154
161
|
- LinkedIn
|
|
155
162
|
- Microsoft
|
|
163
|
+
- PayPal
|
|
156
164
|
- Spotify
|
|
165
|
+
- Steam
|
|
157
166
|
- Twitch
|
|
167
|
+
- X (Twitter)
|
|
168
|
+
- XSUAA
|
|
169
|
+
- Yandex
|
|
158
170
|
|
|
159
171
|
You can add your favorite provider by creating a new file in [src/runtime/server/lib/oauth/](./src/runtime/server/lib/oauth/).
|
|
160
172
|
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -43,7 +43,11 @@ const module = defineNuxtModule({
|
|
|
43
43
|
const envPath = join(nuxt.options.rootDir, ".env");
|
|
44
44
|
const envContent = await readFile(envPath, "utf-8").catch(() => "");
|
|
45
45
|
if (!envContent.includes("NUXT_SESSION_PASSWORD")) {
|
|
46
|
-
await writeFile(
|
|
46
|
+
await writeFile(
|
|
47
|
+
envPath,
|
|
48
|
+
`${envContent ? envContent + "\n" : envContent}NUXT_SESSION_PASSWORD=${runtimeConfig.session.password}`,
|
|
49
|
+
"utf-8"
|
|
50
|
+
);
|
|
47
51
|
}
|
|
48
52
|
}
|
|
49
53
|
runtimeConfig.oauth = defu(runtimeConfig.oauth, {});
|
|
@@ -106,6 +110,26 @@ const module = defineNuxtModule({
|
|
|
106
110
|
clientId: "",
|
|
107
111
|
clientSecret: ""
|
|
108
112
|
});
|
|
113
|
+
runtimeConfig.oauth.paypal = defu(runtimeConfig.oauth.paypal, {
|
|
114
|
+
clientId: "",
|
|
115
|
+
clientSecret: ""
|
|
116
|
+
});
|
|
117
|
+
runtimeConfig.oauth.steam = defu(runtimeConfig.oauth.steam, {
|
|
118
|
+
apiKey: ""
|
|
119
|
+
});
|
|
120
|
+
runtimeConfig.oauth.x = defu(runtimeConfig.oauth.x, {
|
|
121
|
+
clientId: "",
|
|
122
|
+
clientSecret: ""
|
|
123
|
+
});
|
|
124
|
+
runtimeConfig.oauth.xsuaa = defu(runtimeConfig.oauth.xsuaa, {
|
|
125
|
+
clientId: "",
|
|
126
|
+
clientSecret: "",
|
|
127
|
+
domain: ""
|
|
128
|
+
});
|
|
129
|
+
runtimeConfig.oauth.yandex = defu(runtimeConfig.oauth.yandex, {
|
|
130
|
+
clientId: "",
|
|
131
|
+
clientSecret: ""
|
|
132
|
+
});
|
|
109
133
|
}
|
|
110
134
|
});
|
|
111
135
|
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
<script setup>
|
|
1
|
+
<script lang="ts" setup>
|
|
2
2
|
import { useUserSession } from '#imports'
|
|
3
3
|
|
|
4
|
-
const
|
|
5
|
-
const { ready } = userSession
|
|
4
|
+
const { loggedIn, user, clear, ready } = useUserSession()
|
|
6
5
|
</script>
|
|
7
6
|
|
|
8
7
|
<template>
|
|
9
8
|
<slot
|
|
10
9
|
v-if="ready"
|
|
11
|
-
v-bind="
|
|
10
|
+
v-bind="{ loggedIn, user, clear }"
|
|
12
11
|
/>
|
|
13
12
|
<slot
|
|
14
13
|
v-else
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
|
|
2
2
|
import { withQuery, parsePath } from "ufo";
|
|
3
|
-
import { ofetch } from "ofetch";
|
|
4
3
|
import { defu } from "defu";
|
|
5
4
|
import { useRuntimeConfig } from "#imports";
|
|
6
5
|
export function auth0EventHandler({ config, onSuccess, onError }) {
|
|
@@ -40,7 +39,7 @@ export function auth0EventHandler({ config, onSuccess, onError }) {
|
|
|
40
39
|
})
|
|
41
40
|
);
|
|
42
41
|
}
|
|
43
|
-
const tokens = await
|
|
42
|
+
const tokens = await $fetch(
|
|
44
43
|
tokenURL,
|
|
45
44
|
{
|
|
46
45
|
method: "POST",
|
|
@@ -70,7 +69,7 @@ export function auth0EventHandler({ config, onSuccess, onError }) {
|
|
|
70
69
|
}
|
|
71
70
|
const tokenType = tokens.token_type;
|
|
72
71
|
const accessToken = tokens.access_token;
|
|
73
|
-
const user = await
|
|
72
|
+
const user = await $fetch(`https://${config.domain}/userinfo`, {
|
|
74
73
|
headers: {
|
|
75
74
|
Authorization: `${tokenType} ${accessToken}`
|
|
76
75
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
|
|
3
|
-
import { ofetch } from "ofetch";
|
|
4
3
|
import { withQuery, parsePath } from "ufo";
|
|
5
4
|
import { defu } from "defu";
|
|
6
5
|
import { useRuntimeConfig } from "#imports";
|
|
@@ -88,7 +87,7 @@ export function battledotnetEventHandler({ config, onSuccess, onError }) {
|
|
|
88
87
|
return onError(event, error);
|
|
89
88
|
}
|
|
90
89
|
const accessToken = tokens.access_token;
|
|
91
|
-
const user = await
|
|
90
|
+
const user = await $fetch("https://oauth.battle.net/userinfo", {
|
|
92
91
|
headers: {
|
|
93
92
|
"User-Agent": `Battledotnet-OAuth-${config.clientId}`,
|
|
94
93
|
"Authorization": `Bearer ${accessToken}`
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
|
|
2
2
|
import { withQuery, parsePath } from "ufo";
|
|
3
|
-
import { ofetch } from "ofetch";
|
|
4
3
|
import { defu } from "defu";
|
|
5
4
|
import { useRuntimeConfig } from "#imports";
|
|
6
5
|
export function cognitoEventHandler({ config, onSuccess, onError }) {
|
|
@@ -34,7 +33,7 @@ export function cognitoEventHandler({ config, onSuccess, onError }) {
|
|
|
34
33
|
})
|
|
35
34
|
);
|
|
36
35
|
}
|
|
37
|
-
const tokens = await
|
|
36
|
+
const tokens = await $fetch(
|
|
38
37
|
tokenURL,
|
|
39
38
|
{
|
|
40
39
|
method: "POST",
|
|
@@ -58,7 +57,7 @@ export function cognitoEventHandler({ config, onSuccess, onError }) {
|
|
|
58
57
|
}
|
|
59
58
|
const tokenType = tokens.token_type;
|
|
60
59
|
const accessToken = tokens.access_token;
|
|
61
|
-
const user = await
|
|
60
|
+
const user = await $fetch(`https://${config.userPoolId}.auth.${config.region}.amazoncognito.com/oauth2/userInfo`, {
|
|
62
61
|
headers: {
|
|
63
62
|
Authorization: `${tokenType} ${accessToken}`
|
|
64
63
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
|
|
2
2
|
import { withQuery, parseURL, stringifyParsedURL } from "ufo";
|
|
3
|
-
import { ofetch } from "ofetch";
|
|
4
3
|
import { defu } from "defu";
|
|
5
4
|
import { useRuntimeConfig } from "#imports";
|
|
6
5
|
export function discordEventHandler({ config, onSuccess, onError }) {
|
|
@@ -43,7 +42,7 @@ export function discordEventHandler({ config, onSuccess, onError }) {
|
|
|
43
42
|
}
|
|
44
43
|
const parsedRedirectUrl = parseURL(redirectUrl);
|
|
45
44
|
parsedRedirectUrl.search = "";
|
|
46
|
-
const tokens = await
|
|
45
|
+
const tokens = await $fetch(
|
|
47
46
|
config.tokenURL,
|
|
48
47
|
{
|
|
49
48
|
method: "POST",
|
|
@@ -72,7 +71,7 @@ export function discordEventHandler({ config, onSuccess, onError }) {
|
|
|
72
71
|
return onError(event, error);
|
|
73
72
|
}
|
|
74
73
|
const accessToken = tokens.access_token;
|
|
75
|
-
const user = await
|
|
74
|
+
const user = await $fetch("https://discord.com/api/users/@me", {
|
|
76
75
|
headers: {
|
|
77
76
|
"user-agent": "Nuxt Auth Utils",
|
|
78
77
|
"Authorization": `Bearer ${accessToken}`
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
getRequestURL,
|
|
6
6
|
sendRedirect
|
|
7
7
|
} from "h3";
|
|
8
|
-
import { ofetch } from "ofetch";
|
|
9
8
|
import { withQuery } from "ufo";
|
|
10
9
|
import { defu } from "defu";
|
|
11
10
|
import { useRuntimeConfig } from "#imports";
|
|
@@ -74,7 +73,7 @@ export function facebookEventHandler({
|
|
|
74
73
|
const accessToken = tokens.access_token;
|
|
75
74
|
config.fields = config.fields || ["id", "name"];
|
|
76
75
|
const fields = config.fields.join(",");
|
|
77
|
-
const user = await
|
|
76
|
+
const user = await $fetch(
|
|
78
77
|
`https://graph.facebook.com/v19.0/me?fields=${fields}&access_token=${accessToken}`
|
|
79
78
|
);
|
|
80
79
|
if (!user) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
|
|
2
|
-
import { ofetch } from "ofetch";
|
|
3
2
|
import { withQuery } from "ufo";
|
|
4
3
|
import { defu } from "defu";
|
|
5
4
|
import { useRuntimeConfig } from "#imports";
|
|
@@ -68,14 +67,14 @@ export function githubEventHandler({ config, onSuccess, onError }) {
|
|
|
68
67
|
return onError(event, error);
|
|
69
68
|
}
|
|
70
69
|
const accessToken = tokens.access_token;
|
|
71
|
-
const user = await
|
|
70
|
+
const user = await $fetch("https://api.github.com/user", {
|
|
72
71
|
headers: {
|
|
73
72
|
"User-Agent": `Github-OAuth-${config.clientId}`,
|
|
74
73
|
"Authorization": `token ${accessToken}`
|
|
75
74
|
}
|
|
76
75
|
});
|
|
77
76
|
if (!user.email && config.emailRequired) {
|
|
78
|
-
const emails = await
|
|
77
|
+
const emails = await $fetch("https://api.github.com/user/emails", {
|
|
79
78
|
headers: {
|
|
80
79
|
"User-Agent": `Github-OAuth-${config.clientId}`,
|
|
81
80
|
"Authorization": `token ${accessToken}`
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
sendRedirect
|
|
7
7
|
} from "h3";
|
|
8
8
|
import { withQuery, parsePath } from "ufo";
|
|
9
|
-
import { ofetch } from "ofetch";
|
|
10
9
|
import { defu } from "defu";
|
|
11
10
|
import { useRuntimeConfig } from "#imports";
|
|
12
11
|
export function googleEventHandler({
|
|
@@ -52,7 +51,7 @@ export function googleEventHandler({
|
|
|
52
51
|
client_secret: config.clientSecret,
|
|
53
52
|
code
|
|
54
53
|
};
|
|
55
|
-
const tokens = await
|
|
54
|
+
const tokens = await $fetch(config.tokenURL, {
|
|
56
55
|
method: "POST",
|
|
57
56
|
body
|
|
58
57
|
}).catch((error) => {
|
|
@@ -69,7 +68,7 @@ export function googleEventHandler({
|
|
|
69
68
|
return onError(event, error);
|
|
70
69
|
}
|
|
71
70
|
const accessToken = tokens.access_token;
|
|
72
|
-
const user = await
|
|
71
|
+
const user = await $fetch(
|
|
73
72
|
config.userURL,
|
|
74
73
|
{
|
|
75
74
|
headers: {
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
getRequestURL,
|
|
6
6
|
sendRedirect
|
|
7
7
|
} from "h3";
|
|
8
|
-
import { ofetch } from "ofetch";
|
|
9
8
|
import { withQuery, parsePath } from "ufo";
|
|
10
9
|
import { defu } from "defu";
|
|
11
10
|
import { useRuntimeConfig } from "#imports";
|
|
@@ -60,7 +59,7 @@ export function keycloakEventHandler({
|
|
|
60
59
|
if (!config.scope.includes("openid")) {
|
|
61
60
|
config.scope.push("openid");
|
|
62
61
|
}
|
|
63
|
-
const tokens = await
|
|
62
|
+
const tokens = await $fetch(tokenURL, {
|
|
64
63
|
method: "POST",
|
|
65
64
|
headers: {
|
|
66
65
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
@@ -86,7 +85,7 @@ export function keycloakEventHandler({
|
|
|
86
85
|
return onError(event, error);
|
|
87
86
|
}
|
|
88
87
|
const accessToken = tokens.access_token;
|
|
89
|
-
const user = await
|
|
88
|
+
const user = await $fetch(
|
|
90
89
|
`${realmURL}/protocol/openid-connect/userinfo`,
|
|
91
90
|
{
|
|
92
91
|
headers: {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
|
|
2
2
|
import { withQuery, parseURL, stringifyParsedURL } from "ufo";
|
|
3
|
-
import { ofetch } from "ofetch";
|
|
4
3
|
import { defu } from "defu";
|
|
5
4
|
import { useRuntimeConfig } from "#imports";
|
|
6
5
|
export function linkedinEventHandler({ config, onSuccess, onError }) {
|
|
@@ -42,7 +41,7 @@ export function linkedinEventHandler({ config, onSuccess, onError }) {
|
|
|
42
41
|
}
|
|
43
42
|
const parsedRedirectUrl = parseURL(redirectUrl);
|
|
44
43
|
parsedRedirectUrl.search = "";
|
|
45
|
-
const tokens = await
|
|
44
|
+
const tokens = await $fetch(
|
|
46
45
|
config.tokenURL,
|
|
47
46
|
{
|
|
48
47
|
method: "POST",
|
|
@@ -71,7 +70,7 @@ export function linkedinEventHandler({ config, onSuccess, onError }) {
|
|
|
71
70
|
return onError(event, error);
|
|
72
71
|
}
|
|
73
72
|
const accessToken = tokens.access_token;
|
|
74
|
-
const user = await
|
|
73
|
+
const user = await $fetch("https://api.linkedin.com/v2/userinfo", {
|
|
75
74
|
headers: {
|
|
76
75
|
"user-agent": "Nuxt Auth Utils",
|
|
77
76
|
"Authorization": `Bearer ${accessToken}`
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
|
|
2
2
|
import { withQuery, parsePath } from "ufo";
|
|
3
|
-
import { ofetch } from "ofetch";
|
|
4
3
|
import { defu } from "defu";
|
|
5
4
|
import { useRuntimeConfig } from "#imports";
|
|
6
5
|
export function microsoftEventHandler({ config, onSuccess, onError }) {
|
|
@@ -40,7 +39,7 @@ export function microsoftEventHandler({ config, onSuccess, onError }) {
|
|
|
40
39
|
data.append("client_secret", config.clientSecret);
|
|
41
40
|
data.append("redirect_uri", parsePath(redirectUrl).pathname);
|
|
42
41
|
data.append("code", String(code));
|
|
43
|
-
const tokens = await
|
|
42
|
+
const tokens = await $fetch(
|
|
44
43
|
tokenURL,
|
|
45
44
|
{
|
|
46
45
|
method: "POST",
|
|
@@ -65,7 +64,7 @@ export function microsoftEventHandler({ config, onSuccess, onError }) {
|
|
|
65
64
|
const tokenType = tokens.token_type;
|
|
66
65
|
const accessToken = tokens.access_token;
|
|
67
66
|
const userURL = config.userURL || "https://graph.microsoft.com/v1.0/me";
|
|
68
|
-
const user = await
|
|
67
|
+
const user = await $fetch(userURL, {
|
|
69
68
|
headers: {
|
|
70
69
|
Authorization: `${tokenType} ${accessToken}`
|
|
71
70
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { OAuthConfig } from '#auth-utils';
|
|
2
|
+
export interface OAuthPaypalConfig {
|
|
3
|
+
/**
|
|
4
|
+
* PayPal Client ID
|
|
5
|
+
* @default process.env.NUXT_OAUTH_PAYPAL_CLIENT_ID
|
|
6
|
+
*/
|
|
7
|
+
clientId?: string;
|
|
8
|
+
/**
|
|
9
|
+
* PayPal OAuth Client Secret
|
|
10
|
+
* @default process.env.NUXT_OAUTH_PAYPAL_CLIENT_SECRET
|
|
11
|
+
*/
|
|
12
|
+
clientSecret?: string;
|
|
13
|
+
/**
|
|
14
|
+
* PayPal OAuth Scope
|
|
15
|
+
* @default []
|
|
16
|
+
* @see https://developer.paypal.com/docs/log-in-with-paypal/integrate/reference/#scope-attributes
|
|
17
|
+
* @example ['email', 'profile']
|
|
18
|
+
*/
|
|
19
|
+
scope?: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Require email from user, adds the ['email'] scope if not present
|
|
22
|
+
* @default false
|
|
23
|
+
*/
|
|
24
|
+
emailRequired?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Use PayPal sandbox environment
|
|
27
|
+
* @default import.meta.dev // true in development, false in production
|
|
28
|
+
*/
|
|
29
|
+
sandbox?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* PayPal OAuth Authorization URL
|
|
32
|
+
* @default 'https://www.paypal.com/signin/authorize'
|
|
33
|
+
*/
|
|
34
|
+
authorizationURL?: string;
|
|
35
|
+
/**
|
|
36
|
+
* PayPal OAuth Token URL
|
|
37
|
+
* @default 'https://api-m.paypal.com/v1/oauth2/token'
|
|
38
|
+
*/
|
|
39
|
+
tokenURL?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Extra authorization parameters to provide to the authorization URL
|
|
42
|
+
* @see https://developer.paypal.com/docs/log-in-with-paypal/integrate/build-button/#link-constructauthorizationendpoint
|
|
43
|
+
* @example { flowEntry: 'static' }
|
|
44
|
+
*/
|
|
45
|
+
authorizationParams?: Record<string, string>;
|
|
46
|
+
}
|
|
47
|
+
export declare function paypalEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthPaypalConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
|
|
2
|
+
import { withQuery, parsePath } from "ufo";
|
|
3
|
+
import { defu } from "defu";
|
|
4
|
+
import { useRuntimeConfig } from "#imports";
|
|
5
|
+
export function paypalEventHandler({ config, onSuccess, onError }) {
|
|
6
|
+
return eventHandler(async (event) => {
|
|
7
|
+
config = defu(config, useRuntimeConfig(event).oauth?.paypal, {
|
|
8
|
+
sandbox: import.meta.dev,
|
|
9
|
+
authorizationURL: "https://www.paypal.com/signin/authorize",
|
|
10
|
+
tokenURL: "https://api-m.paypal.com/v1/oauth2/token",
|
|
11
|
+
authorizationParams: {}
|
|
12
|
+
});
|
|
13
|
+
const { code } = getQuery(event);
|
|
14
|
+
if (!config.clientId) {
|
|
15
|
+
const error = createError({
|
|
16
|
+
statusCode: 500,
|
|
17
|
+
message: "Missing NUXT_OAUTH_PAYPAL_CLIENT_ID env variables."
|
|
18
|
+
});
|
|
19
|
+
if (!onError)
|
|
20
|
+
throw error;
|
|
21
|
+
return onError(event, error);
|
|
22
|
+
}
|
|
23
|
+
let paypalAPI = "api-m.paypal.com";
|
|
24
|
+
if (config.sandbox) {
|
|
25
|
+
paypalAPI = "api-m.sandbox.paypal.com";
|
|
26
|
+
config.authorizationURL = "https://www.sandbox.paypal.com/signin/authorize";
|
|
27
|
+
config.tokenURL = `https://${paypalAPI}/v1/oauth2/token`;
|
|
28
|
+
}
|
|
29
|
+
const redirectUrl = getRequestURL(event).href;
|
|
30
|
+
if (!code) {
|
|
31
|
+
config.scope = config.scope || [];
|
|
32
|
+
if (!config.scope.includes("openid")) {
|
|
33
|
+
config.scope.push("openid");
|
|
34
|
+
}
|
|
35
|
+
if (config.emailRequired && !config.scope.includes("email")) {
|
|
36
|
+
config.scope.push("email");
|
|
37
|
+
}
|
|
38
|
+
return sendRedirect(
|
|
39
|
+
event,
|
|
40
|
+
withQuery(config.authorizationURL, {
|
|
41
|
+
response_type: "code",
|
|
42
|
+
client_id: config.clientId,
|
|
43
|
+
redirect_uri: redirectUrl,
|
|
44
|
+
scope: config.scope.join(" "),
|
|
45
|
+
flowEntry: "static",
|
|
46
|
+
...config.authorizationParams
|
|
47
|
+
})
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
const authCode = Buffer.from(`${config.clientId}:${config.clientSecret}`).toString("base64");
|
|
51
|
+
const tokens = await $fetch(
|
|
52
|
+
config.tokenURL,
|
|
53
|
+
{
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: {
|
|
56
|
+
"Authorization": `Basic ${authCode}`,
|
|
57
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
58
|
+
},
|
|
59
|
+
params: {
|
|
60
|
+
grant_type: "authorization_code",
|
|
61
|
+
redirect_uri: encodeURIComponent(parsePath(redirectUrl).pathname),
|
|
62
|
+
code
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
).catch((error) => {
|
|
66
|
+
return { error };
|
|
67
|
+
});
|
|
68
|
+
if (tokens.error) {
|
|
69
|
+
const error = createError({
|
|
70
|
+
statusCode: 401,
|
|
71
|
+
message: `PayPal login failed: ${tokens.error?.data?.error_description || "Unknown error"}`,
|
|
72
|
+
data: tokens
|
|
73
|
+
});
|
|
74
|
+
if (!onError)
|
|
75
|
+
throw error;
|
|
76
|
+
return onError(event, error);
|
|
77
|
+
}
|
|
78
|
+
const accessToken = tokens.access_token;
|
|
79
|
+
const users = await $fetch(`https://${paypalAPI}/v1/identity/openidconnect/userinfo`, {
|
|
80
|
+
headers: {
|
|
81
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
82
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
83
|
+
},
|
|
84
|
+
params: {
|
|
85
|
+
schema: "openid"
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
const user = users;
|
|
89
|
+
if (!user) {
|
|
90
|
+
const error = createError({
|
|
91
|
+
statusCode: 500,
|
|
92
|
+
message: "Could not get PayPal user",
|
|
93
|
+
data: tokens
|
|
94
|
+
});
|
|
95
|
+
if (!onError)
|
|
96
|
+
throw error;
|
|
97
|
+
return onError(event, error);
|
|
98
|
+
}
|
|
99
|
+
return onSuccess(event, {
|
|
100
|
+
tokens,
|
|
101
|
+
user
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
|
|
2
2
|
import { withQuery, parsePath } from "ufo";
|
|
3
|
-
import { ofetch } from "ofetch";
|
|
4
3
|
import { defu } from "defu";
|
|
5
4
|
import { useRuntimeConfig } from "#imports";
|
|
6
5
|
export function spotifyEventHandler({ config, onSuccess, onError }) {
|
|
@@ -38,7 +37,7 @@ export function spotifyEventHandler({ config, onSuccess, onError }) {
|
|
|
38
37
|
);
|
|
39
38
|
}
|
|
40
39
|
const authCode = Buffer.from(`${config.clientId}:${config.clientSecret}`).toString("base64");
|
|
41
|
-
const tokens = await
|
|
40
|
+
const tokens = await $fetch(
|
|
42
41
|
config.tokenURL,
|
|
43
42
|
{
|
|
44
43
|
method: "POST",
|
|
@@ -66,7 +65,7 @@ export function spotifyEventHandler({ config, onSuccess, onError }) {
|
|
|
66
65
|
return onError(event, error);
|
|
67
66
|
}
|
|
68
67
|
const accessToken = tokens.access_token;
|
|
69
|
-
const user = await
|
|
68
|
+
const user = await $fetch("https://api.spotify.com/v1/me", {
|
|
70
69
|
headers: {
|
|
71
70
|
Authorization: `Bearer ${accessToken}`
|
|
72
71
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { OAuthConfig } from '#auth-utils';
|
|
2
|
+
export interface OAuthSteamConfig {
|
|
3
|
+
/**
|
|
4
|
+
* Steam API Key
|
|
5
|
+
* @default process.env.NUXT_OAUTH_STEAM_API_KEY
|
|
6
|
+
* @see https://steamcommunity.com/dev
|
|
7
|
+
*/
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Steam Open ID OAuth Authorization URL
|
|
11
|
+
* @default 'https://steamcommunity.com/openid/login'
|
|
12
|
+
*/
|
|
13
|
+
authorizationURL?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function steamEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthSteamConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
|
|
2
|
+
import { withQuery } from "ufo";
|
|
3
|
+
import { defu } from "defu";
|
|
4
|
+
import { useRuntimeConfig } from "#imports";
|
|
5
|
+
export function steamEventHandler({ config, onSuccess, onError }) {
|
|
6
|
+
return eventHandler(async (event) => {
|
|
7
|
+
config = defu(config, useRuntimeConfig(event).oauth?.steam, {
|
|
8
|
+
authorizationURL: "https://steamcommunity.com/openid/login"
|
|
9
|
+
});
|
|
10
|
+
const query = getQuery(event);
|
|
11
|
+
if (!config.apiKey) {
|
|
12
|
+
const error = createError({
|
|
13
|
+
statusCode: 500,
|
|
14
|
+
message: "Missing NUXT_OAUTH_STEAM_API_KEY env variable."
|
|
15
|
+
});
|
|
16
|
+
if (!onError)
|
|
17
|
+
throw error;
|
|
18
|
+
return onError(event, error);
|
|
19
|
+
}
|
|
20
|
+
if (!query["openid.claimed_id"]) {
|
|
21
|
+
const redirectUrl = getRequestURL(event).href;
|
|
22
|
+
const steamOpenIdParams = {
|
|
23
|
+
"openid.ns": "http://specs.openid.net/auth/2.0",
|
|
24
|
+
"openid.mode": "checkid_setup",
|
|
25
|
+
"openid.return_to": redirectUrl,
|
|
26
|
+
"openid.identity": "http://specs.openid.net/auth/2.0/identifier_select",
|
|
27
|
+
"openid.claimed_id": "http://specs.openid.net/auth/2.0/identifier_select"
|
|
28
|
+
};
|
|
29
|
+
return sendRedirect(event, withQuery(config.authorizationURL, steamOpenIdParams));
|
|
30
|
+
}
|
|
31
|
+
const validateAuth = await $fetch(withQuery(config.authorizationURL, {
|
|
32
|
+
...query,
|
|
33
|
+
"openid.mode": "check_authentication"
|
|
34
|
+
}));
|
|
35
|
+
if (!validateAuth.includes("is_valid:true")) {
|
|
36
|
+
const error = createError({
|
|
37
|
+
statusCode: 401,
|
|
38
|
+
message: "Steam login failed: Unknown error"
|
|
39
|
+
});
|
|
40
|
+
if (!onError)
|
|
41
|
+
throw error;
|
|
42
|
+
return onError(event, error);
|
|
43
|
+
}
|
|
44
|
+
const steamId = query["openid.claimed_id"].split("/").pop();
|
|
45
|
+
const user = await $fetch(withQuery("https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/", {
|
|
46
|
+
key: config.apiKey,
|
|
47
|
+
steamids: steamId
|
|
48
|
+
}));
|
|
49
|
+
return onSuccess(event, {
|
|
50
|
+
user: user.response.players[0],
|
|
51
|
+
tokens: null
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
|
|
2
2
|
import { withQuery, parsePath } from "ufo";
|
|
3
|
-
import { ofetch } from "ofetch";
|
|
4
3
|
import { defu } from "defu";
|
|
5
4
|
import { useRuntimeConfig } from "#imports";
|
|
6
5
|
export function twitchEventHandler({ config, onSuccess, onError }) {
|
|
@@ -37,7 +36,7 @@ export function twitchEventHandler({ config, onSuccess, onError }) {
|
|
|
37
36
|
})
|
|
38
37
|
);
|
|
39
38
|
}
|
|
40
|
-
const tokens = await
|
|
39
|
+
const tokens = await $fetch(
|
|
41
40
|
config.tokenURL,
|
|
42
41
|
{
|
|
43
42
|
method: "POST",
|
|
@@ -66,7 +65,7 @@ export function twitchEventHandler({ config, onSuccess, onError }) {
|
|
|
66
65
|
return onError(event, error);
|
|
67
66
|
}
|
|
68
67
|
const accessToken = tokens.access_token;
|
|
69
|
-
const users = await
|
|
68
|
+
const users = await $fetch("https://api.twitch.tv/helix/users", {
|
|
70
69
|
headers: {
|
|
71
70
|
"Client-ID": config.clientId,
|
|
72
71
|
"Authorization": `Bearer ${accessToken}`
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { OAuthConfig } from '#auth-utils';
|
|
2
|
+
export interface OAuthXConfig {
|
|
3
|
+
/**
|
|
4
|
+
* X OAuth Client ID
|
|
5
|
+
* @default process.env.NUXT_OAUTH_X_CLIENT_ID
|
|
6
|
+
*/
|
|
7
|
+
clientId?: string;
|
|
8
|
+
/**
|
|
9
|
+
* X OAuth Client Secret
|
|
10
|
+
* @default process.env.NUXT_OAUTH_X_CLIENT_SECRET
|
|
11
|
+
*/
|
|
12
|
+
clientSecret?: string;
|
|
13
|
+
/**
|
|
14
|
+
* X OAuth Scope
|
|
15
|
+
* @default []
|
|
16
|
+
* @see https://developer.x.com/en/docs/authentication/oauth-2-0/user-access-token
|
|
17
|
+
* @example ['tweet.read', 'users.read', 'offline.access'],
|
|
18
|
+
*/
|
|
19
|
+
scope?: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Require email from user
|
|
22
|
+
* @default false
|
|
23
|
+
*/
|
|
24
|
+
emailRequired?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* X OAuth Authorization URL
|
|
27
|
+
* @default 'https://twitter.com/i/oauth2/authorize'
|
|
28
|
+
*/
|
|
29
|
+
authorizationURL?: string;
|
|
30
|
+
/**
|
|
31
|
+
* X OAuth Token URL
|
|
32
|
+
* @default 'https://api.twitter.com/2/oauth2/token'
|
|
33
|
+
*/
|
|
34
|
+
tokenURL?: string;
|
|
35
|
+
/**
|
|
36
|
+
* X OAuth User URL
|
|
37
|
+
* @default 'https://api.twitter.com/2/users/me'
|
|
38
|
+
*/
|
|
39
|
+
userURL?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Extra authorization parameters to provide to the authorization URL
|
|
42
|
+
* @see https://developer.x.com/en/docs/authentication/oauth-2-0/user-access-token
|
|
43
|
+
*/
|
|
44
|
+
authorizationParams: Record<string, string>;
|
|
45
|
+
}
|
|
46
|
+
export declare function xEventHandler({ config, onSuccess, onError, }: OAuthConfig<OAuthXConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
eventHandler,
|
|
4
|
+
createError,
|
|
5
|
+
getQuery,
|
|
6
|
+
getRequestURL,
|
|
7
|
+
sendRedirect
|
|
8
|
+
} from "h3";
|
|
9
|
+
import { withQuery, parsePath } from "ufo";
|
|
10
|
+
import { defu } from "defu";
|
|
11
|
+
import { useRuntimeConfig } from "#imports";
|
|
12
|
+
export function xEventHandler({
|
|
13
|
+
config,
|
|
14
|
+
onSuccess,
|
|
15
|
+
onError
|
|
16
|
+
}) {
|
|
17
|
+
return eventHandler(async (event) => {
|
|
18
|
+
config = defu(config, useRuntimeConfig(event).oauth?.x, {
|
|
19
|
+
authorizationURL: "https://twitter.com/i/oauth2/authorize",
|
|
20
|
+
tokenURL: "https://api.twitter.com/2/oauth2/token",
|
|
21
|
+
userURL: "https://api.twitter.com/2/users/me",
|
|
22
|
+
authorizationParams: {
|
|
23
|
+
state: randomUUID(),
|
|
24
|
+
code_challenge: randomUUID()
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
const { code } = getQuery(event);
|
|
28
|
+
if (!config.clientId) {
|
|
29
|
+
const error = createError({
|
|
30
|
+
statusCode: 500,
|
|
31
|
+
message: "Missing NUXT_OAUTH_X_CLIENT_ID env variables."
|
|
32
|
+
});
|
|
33
|
+
if (!onError)
|
|
34
|
+
throw error;
|
|
35
|
+
return onError(event, error);
|
|
36
|
+
}
|
|
37
|
+
const redirectUrl = getRequestURL(event).href;
|
|
38
|
+
if (!code) {
|
|
39
|
+
config.scope = config.scope || ["tweet.read", "users.read", "offline.access"];
|
|
40
|
+
return sendRedirect(
|
|
41
|
+
event,
|
|
42
|
+
withQuery(config.authorizationURL, {
|
|
43
|
+
response_type: "code",
|
|
44
|
+
client_id: config.clientId,
|
|
45
|
+
code_challenge_method: "plain",
|
|
46
|
+
redirect_uri: redirectUrl,
|
|
47
|
+
scope: config.scope.join(" "),
|
|
48
|
+
...config.authorizationParams
|
|
49
|
+
})
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
const params = {
|
|
53
|
+
grant_type: "authorization_code",
|
|
54
|
+
code_verifier: config.authorizationParams.code_challenge,
|
|
55
|
+
redirect_uri: parsePath(redirectUrl).pathname,
|
|
56
|
+
code
|
|
57
|
+
};
|
|
58
|
+
const authCode = Buffer.from(`${config.clientId}:${config.clientSecret}`).toString("base64");
|
|
59
|
+
const tokens = await $fetch(config.tokenURL, {
|
|
60
|
+
method: "POST",
|
|
61
|
+
headers: {
|
|
62
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
63
|
+
"Authorization": `Basic ${authCode}`
|
|
64
|
+
},
|
|
65
|
+
params
|
|
66
|
+
}).catch((error) => {
|
|
67
|
+
return { error };
|
|
68
|
+
});
|
|
69
|
+
if (tokens.error) {
|
|
70
|
+
const error = createError({
|
|
71
|
+
statusCode: 401,
|
|
72
|
+
message: `X login failed: ${tokens.error?.data?.error_description || "Unknown error"}`,
|
|
73
|
+
data: tokens
|
|
74
|
+
});
|
|
75
|
+
if (!onError)
|
|
76
|
+
throw error;
|
|
77
|
+
return onError(event, error);
|
|
78
|
+
}
|
|
79
|
+
const accessToken = tokens.access_token;
|
|
80
|
+
const user = await $fetch(
|
|
81
|
+
config.userURL,
|
|
82
|
+
{
|
|
83
|
+
headers: {
|
|
84
|
+
Authorization: `Bearer ${accessToken}`
|
|
85
|
+
},
|
|
86
|
+
query: {
|
|
87
|
+
"user.fields": "description,id,name,profile_image_url,username,verified,verified_type"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
).catch((error) => {
|
|
91
|
+
return error;
|
|
92
|
+
});
|
|
93
|
+
if (config.emailRequired) {
|
|
94
|
+
const emailData = await $fetch("https://api.twitter.com/1.1/account/verify_credentials.json", {
|
|
95
|
+
headers: {
|
|
96
|
+
Authorization: `Bearer ${accessToken}`
|
|
97
|
+
},
|
|
98
|
+
query: {
|
|
99
|
+
include_email: "true",
|
|
100
|
+
skip_status: "true"
|
|
101
|
+
}
|
|
102
|
+
}).catch((error) => {
|
|
103
|
+
return error;
|
|
104
|
+
});
|
|
105
|
+
if (emailData && emailData.email) {
|
|
106
|
+
user.email = emailData.email;
|
|
107
|
+
} else {
|
|
108
|
+
const error = createError({
|
|
109
|
+
statusCode: 401,
|
|
110
|
+
message: "Twitter login failed: no user email found"
|
|
111
|
+
});
|
|
112
|
+
if (!onError)
|
|
113
|
+
throw error;
|
|
114
|
+
return onError(event, error);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return onSuccess(event, {
|
|
118
|
+
tokens,
|
|
119
|
+
user: user?.data
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { OAuthConfig } from '#auth-utils';
|
|
2
|
+
export interface OAuthXSUAAConfig {
|
|
3
|
+
/**
|
|
4
|
+
* XSUAA OAuth Client ID
|
|
5
|
+
* @default process.env.NUXT_OAUTH_XSUAA_CLIENT_ID
|
|
6
|
+
*/
|
|
7
|
+
clientId?: string;
|
|
8
|
+
/**
|
|
9
|
+
* XSUAA OAuth Client Secret
|
|
10
|
+
* @default process.env.NUXT_OAUTH_XSUAA_CLIENT_SECRET
|
|
11
|
+
*/
|
|
12
|
+
clientSecret?: string;
|
|
13
|
+
/**
|
|
14
|
+
* XSUAA OAuth Issuer
|
|
15
|
+
* @default process.env.NUXT_OAUTH_XSUAA_DOMAIN
|
|
16
|
+
*/
|
|
17
|
+
domain?: string;
|
|
18
|
+
/**
|
|
19
|
+
* XSUAA OAuth Scope
|
|
20
|
+
* @default []
|
|
21
|
+
* @see https://sap.github.io/cloud-sdk/docs/java/guides/cloud-foundry-xsuaa-service
|
|
22
|
+
* @example ['openid']
|
|
23
|
+
*/
|
|
24
|
+
scope?: string[];
|
|
25
|
+
}
|
|
26
|
+
export declare function xsuaaEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthXSUAAConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
|
|
2
|
+
import { withQuery, parsePath } from "ufo";
|
|
3
|
+
import { defu } from "defu";
|
|
4
|
+
import { useRuntimeConfig } from "#imports";
|
|
5
|
+
export function xsuaaEventHandler({ config, onSuccess, onError }) {
|
|
6
|
+
return eventHandler(async (event) => {
|
|
7
|
+
config = defu(config, useRuntimeConfig(event).oauth?.xsuaa);
|
|
8
|
+
const { code } = getQuery(event);
|
|
9
|
+
if (!config.clientId || !config.clientSecret || !config.domain) {
|
|
10
|
+
const error = createError({
|
|
11
|
+
statusCode: 500,
|
|
12
|
+
message: "Missing NUXT_OAUTH_XSUAA_CLIENT_ID or NUXT_OAUTH_XSUAA_CLIENT_SECRET or NUXT_OAUTH_XSUAA_DOMAIN env variables."
|
|
13
|
+
});
|
|
14
|
+
if (!onError)
|
|
15
|
+
throw error;
|
|
16
|
+
return onError(event, error);
|
|
17
|
+
}
|
|
18
|
+
const authorizationURL = `https://${config.domain}/oauth/authorize`;
|
|
19
|
+
const tokenURL = `https://${config.domain}/oauth/token`;
|
|
20
|
+
const redirectUrl = getRequestURL(event).href;
|
|
21
|
+
if (!code) {
|
|
22
|
+
config.scope = config.scope || [];
|
|
23
|
+
return sendRedirect(
|
|
24
|
+
event,
|
|
25
|
+
withQuery(authorizationURL, {
|
|
26
|
+
response_type: "code",
|
|
27
|
+
client_id: config.clientId,
|
|
28
|
+
redirect_uri: redirectUrl,
|
|
29
|
+
scope: config.scope.join(" ")
|
|
30
|
+
})
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
const tokens = await $fetch(
|
|
34
|
+
tokenURL,
|
|
35
|
+
{
|
|
36
|
+
method: "POST",
|
|
37
|
+
headers: {
|
|
38
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
39
|
+
},
|
|
40
|
+
body: new URLSearchParams({
|
|
41
|
+
grant_type: "authorization_code",
|
|
42
|
+
client_id: config.clientId,
|
|
43
|
+
client_secret: config.clientSecret,
|
|
44
|
+
redirect_uri: parsePath(redirectUrl).pathname,
|
|
45
|
+
code: `${code}`
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
).catch((error) => {
|
|
49
|
+
return { error };
|
|
50
|
+
});
|
|
51
|
+
if (tokens.error) {
|
|
52
|
+
const error = createError({
|
|
53
|
+
statusCode: 401,
|
|
54
|
+
message: `XSUAA login failed: ${tokens.error?.data?.error_description || "Unknown error"}`,
|
|
55
|
+
data: tokens
|
|
56
|
+
});
|
|
57
|
+
if (!onError)
|
|
58
|
+
throw error;
|
|
59
|
+
return onError(event, error);
|
|
60
|
+
}
|
|
61
|
+
const tokenType = tokens.token_type;
|
|
62
|
+
const accessToken = tokens.access_token;
|
|
63
|
+
const user = await $fetch(`https://${config.domain}/userinfo`, {
|
|
64
|
+
headers: {
|
|
65
|
+
Authorization: `${tokenType} ${accessToken}`
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
return onSuccess(event, {
|
|
69
|
+
tokens,
|
|
70
|
+
user
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { OAuthConfig } from '#auth-utils';
|
|
2
|
+
export interface OAuthYandexConfig {
|
|
3
|
+
/**
|
|
4
|
+
* Yandex OAuth Client ID
|
|
5
|
+
* @default process.env.NUXT_OAUTH_YANDEX_CLIENT_ID
|
|
6
|
+
*/
|
|
7
|
+
clientId?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Yandex OAuth Client Secret
|
|
10
|
+
* @default process.env.NUXT_OAUTH_YANDEX_CLIENT_SECRET
|
|
11
|
+
*/
|
|
12
|
+
clientSecret?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Yandex OAuth Scope
|
|
15
|
+
* @default []
|
|
16
|
+
* @see https://yandex.ru/dev/id/doc/en/codes/code-url#optional
|
|
17
|
+
* @example ["login:avatar", "login:birthday", "login:email", "login:info", "login:default_phone"]
|
|
18
|
+
*/
|
|
19
|
+
scope?: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Require email from user, adds the ['login:email'] scope if not present
|
|
22
|
+
* @default false
|
|
23
|
+
*/
|
|
24
|
+
emailRequired?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Yandex OAuth Authorization URL
|
|
27
|
+
* @default 'https://oauth.yandex.ru/authorize'
|
|
28
|
+
*/
|
|
29
|
+
authorizationURL?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Yandex OAuth Token URL
|
|
32
|
+
* @default 'https://oauth.yandex.ru/token'
|
|
33
|
+
*/
|
|
34
|
+
tokenURL?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Yandex OAuth User URL
|
|
37
|
+
* @default 'https://login.yandex.ru/info'
|
|
38
|
+
*/
|
|
39
|
+
userURL?: string;
|
|
40
|
+
}
|
|
41
|
+
export declare function yandexEventHandler({ config, onSuccess, onError, }: OAuthConfig<OAuthYandexConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import {
|
|
2
|
+
eventHandler,
|
|
3
|
+
createError,
|
|
4
|
+
getQuery,
|
|
5
|
+
getRequestURL,
|
|
6
|
+
sendRedirect
|
|
7
|
+
} from "h3";
|
|
8
|
+
import { withQuery, parseURL, stringifyParsedURL } from "ufo";
|
|
9
|
+
import { defu } from "defu";
|
|
10
|
+
import { useRuntimeConfig } from "#imports";
|
|
11
|
+
export function yandexEventHandler({
|
|
12
|
+
config,
|
|
13
|
+
onSuccess,
|
|
14
|
+
onError
|
|
15
|
+
}) {
|
|
16
|
+
return eventHandler(async (event) => {
|
|
17
|
+
config = defu(config, useRuntimeConfig(event).oauth?.yandex, {
|
|
18
|
+
authorizationURL: "https://oauth.yandex.ru/authorize",
|
|
19
|
+
tokenURL: "https://oauth.yandex.ru/token",
|
|
20
|
+
userURL: "https://login.yandex.ru/info"
|
|
21
|
+
});
|
|
22
|
+
const { code } = getQuery(event);
|
|
23
|
+
if (!config.clientId || !config.clientSecret) {
|
|
24
|
+
const error = createError({
|
|
25
|
+
statusCode: 500,
|
|
26
|
+
message: "Missing NUXT_OAUTH_YANDEX_CLIENT_ID or NUXT_OAUTH_YANDEX_CLIENT_SECRET env variables."
|
|
27
|
+
});
|
|
28
|
+
if (!onError)
|
|
29
|
+
throw error;
|
|
30
|
+
return onError(event, error);
|
|
31
|
+
}
|
|
32
|
+
const redirectUrl = getRequestURL(event).href;
|
|
33
|
+
if (!code) {
|
|
34
|
+
config.scope = config.scope || [];
|
|
35
|
+
if (config.emailRequired && !config.scope.includes("login:email")) {
|
|
36
|
+
config.scope.push("login:email");
|
|
37
|
+
}
|
|
38
|
+
return sendRedirect(
|
|
39
|
+
event,
|
|
40
|
+
withQuery(config.authorizationURL, {
|
|
41
|
+
response_type: "code",
|
|
42
|
+
client_id: config.clientId,
|
|
43
|
+
redirect_uri: redirectUrl,
|
|
44
|
+
scope: config.scope.join(" ")
|
|
45
|
+
})
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const tokens = await $fetch(config.tokenURL, {
|
|
49
|
+
method: "POST",
|
|
50
|
+
headers: {
|
|
51
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
52
|
+
},
|
|
53
|
+
body: new URLSearchParams({
|
|
54
|
+
client_id: config.clientId,
|
|
55
|
+
client_secret: config.clientSecret,
|
|
56
|
+
grant_type: "authorization_code",
|
|
57
|
+
redirect_uri: stringifyParsedURL(parseURL(redirectUrl)),
|
|
58
|
+
code
|
|
59
|
+
}).toString()
|
|
60
|
+
}).catch((error) => {
|
|
61
|
+
return { error };
|
|
62
|
+
});
|
|
63
|
+
if (tokens.error) {
|
|
64
|
+
const error = createError({
|
|
65
|
+
statusCode: 401,
|
|
66
|
+
message: `Yandex login failed: ${tokens.error?.data?.error_description || "Unknown error"}`,
|
|
67
|
+
data: tokens
|
|
68
|
+
});
|
|
69
|
+
if (!onError)
|
|
70
|
+
throw error;
|
|
71
|
+
return onError(event, error);
|
|
72
|
+
}
|
|
73
|
+
const accessToken = tokens.access_token;
|
|
74
|
+
const user = await $fetch(config.userURL, {
|
|
75
|
+
headers: {
|
|
76
|
+
Authorization: `OAuth ${accessToken}`
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
return onSuccess(event, {
|
|
80
|
+
tokens,
|
|
81
|
+
user
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
@@ -10,6 +10,11 @@ import { keycloakEventHandler } from '../lib/oauth/keycloak.js';
|
|
|
10
10
|
import { linkedinEventHandler } from '../lib/oauth/linkedin.js';
|
|
11
11
|
import { cognitoEventHandler } from '../lib/oauth/cognito.js';
|
|
12
12
|
import { facebookEventHandler } from '../lib/oauth/facebook.js';
|
|
13
|
+
import { paypalEventHandler } from '../lib/oauth/paypal.js';
|
|
14
|
+
import { steamEventHandler } from '../lib/oauth/steam.js';
|
|
15
|
+
import { xEventHandler } from '../lib/oauth/x.js';
|
|
16
|
+
import { xsuaaEventHandler } from '../lib/oauth/xsuaa.js';
|
|
17
|
+
import { yandexEventHandler } from '../lib/oauth/yandex.js';
|
|
13
18
|
export declare const oauth: {
|
|
14
19
|
githubEventHandler: typeof githubEventHandler;
|
|
15
20
|
spotifyEventHandler: typeof spotifyEventHandler;
|
|
@@ -23,4 +28,9 @@ export declare const oauth: {
|
|
|
23
28
|
linkedinEventHandler: typeof linkedinEventHandler;
|
|
24
29
|
cognitoEventHandler: typeof cognitoEventHandler;
|
|
25
30
|
facebookEventHandler: typeof facebookEventHandler;
|
|
31
|
+
paypalEventHandler: typeof paypalEventHandler;
|
|
32
|
+
steamEventHandler: typeof steamEventHandler;
|
|
33
|
+
xEventHandler: typeof xEventHandler;
|
|
34
|
+
xsuaaEventHandler: typeof xsuaaEventHandler;
|
|
35
|
+
yandexEventHandler: typeof yandexEventHandler;
|
|
26
36
|
};
|
|
@@ -10,6 +10,11 @@ import { keycloakEventHandler } from "../lib/oauth/keycloak.js";
|
|
|
10
10
|
import { linkedinEventHandler } from "../lib/oauth/linkedin.js";
|
|
11
11
|
import { cognitoEventHandler } from "../lib/oauth/cognito.js";
|
|
12
12
|
import { facebookEventHandler } from "../lib/oauth/facebook.js";
|
|
13
|
+
import { paypalEventHandler } from "../lib/oauth/paypal.js";
|
|
14
|
+
import { steamEventHandler } from "../lib/oauth/steam.js";
|
|
15
|
+
import { xEventHandler } from "../lib/oauth/x.js";
|
|
16
|
+
import { xsuaaEventHandler } from "../lib/oauth/xsuaa.js";
|
|
17
|
+
import { yandexEventHandler } from "../lib/oauth/yandex.js";
|
|
13
18
|
export const oauth = {
|
|
14
19
|
githubEventHandler,
|
|
15
20
|
spotifyEventHandler,
|
|
@@ -22,5 +27,10 @@ export const oauth = {
|
|
|
22
27
|
keycloakEventHandler,
|
|
23
28
|
linkedinEventHandler,
|
|
24
29
|
cognitoEventHandler,
|
|
25
|
-
facebookEventHandler
|
|
30
|
+
facebookEventHandler,
|
|
31
|
+
paypalEventHandler,
|
|
32
|
+
steamEventHandler,
|
|
33
|
+
xEventHandler,
|
|
34
|
+
xsuaaEventHandler,
|
|
35
|
+
yandexEventHandler
|
|
26
36
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-auth-utils",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Minimalist Auth module for Nuxt with SSR",
|
|
5
5
|
"repository": "Atinux/nuxt-auth-utils",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
|
|
26
26
|
"release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
|
|
27
27
|
"lint": "eslint .",
|
|
28
|
+
"lint:fix": "eslint . --fix",
|
|
28
29
|
"test": "vitest run",
|
|
29
30
|
"test:types": "vue-tsc --noEmit",
|
|
30
31
|
"test:types:playground": "cd playground && vue-tsc --noEmit",
|