nuxt-auth-utils 0.5.5 → 0.5.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 +3 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +11 -0
- package/dist/runtime/app/plugins/session.client.d.ts +1 -1
- package/dist/runtime/app/plugins/session.server.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/cognito.d.ts +0 -5
- package/dist/runtime/server/lib/oauth/cognito.js +14 -6
- package/dist/runtime/server/lib/oauth/dropbox.js +2 -2
- package/dist/runtime/server/lib/oauth/facebook.js +2 -2
- package/dist/runtime/server/lib/oauth/google.js +2 -2
- package/dist/runtime/server/lib/oauth/hubspot.d.ts +26 -0
- package/dist/runtime/server/lib/oauth/hubspot.js +57 -0
- package/dist/runtime/server/lib/oauth/instagram.d.ts +6 -6
- package/dist/runtime/server/lib/oauth/instagram.js +12 -8
- package/dist/runtime/server/lib/oauth/keycloak.d.ts +9 -2
- package/dist/runtime/server/lib/oauth/keycloak.js +2 -1
- package/dist/runtime/server/lib/oauth/paypal.js +2 -2
- package/dist/runtime/server/lib/oauth/seznam.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/seznam.js +4 -4
- package/dist/runtime/server/lib/oauth/strava.d.ts +116 -0
- package/dist/runtime/server/lib/oauth/strava.js +73 -0
- package/dist/runtime/server/lib/oauth/twitch.js +2 -2
- package/dist/runtime/server/lib/oauth/workos.d.ts +9 -4
- package/dist/runtime/server/lib/oauth/workos.js +6 -7
- package/dist/runtime/server/lib/oauth/x.js +2 -2
- package/dist/runtime/server/lib/oauth/zitadel.js +0 -1
- package/dist/runtime/types/oauth-config.d.ts +1 -1
- package/package.json +13 -12
package/README.md
CHANGED
|
@@ -214,6 +214,7 @@ It can also be set using environment variables:
|
|
|
214
214
|
- GitHub
|
|
215
215
|
- GitLab
|
|
216
216
|
- Google
|
|
217
|
+
- Hubspot
|
|
217
218
|
- Instagram
|
|
218
219
|
- Keycloak
|
|
219
220
|
- Linear
|
|
@@ -224,6 +225,7 @@ It can also be set using environment variables:
|
|
|
224
225
|
- Seznam
|
|
225
226
|
- Spotify
|
|
226
227
|
- Steam
|
|
228
|
+
- Strava
|
|
227
229
|
- TikTok
|
|
228
230
|
- Twitch
|
|
229
231
|
- VK
|
|
@@ -231,6 +233,7 @@ It can also be set using environment variables:
|
|
|
231
233
|
- X (Twitter)
|
|
232
234
|
- XSUAA
|
|
233
235
|
- Yandex
|
|
236
|
+
- Zitadel
|
|
234
237
|
|
|
235
238
|
You can add your favorite provider by creating a new file in [src/runtime/server/lib/oauth/](./src/runtime/server/lib/oauth/).
|
|
236
239
|
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -157,6 +157,7 @@ const module = defineNuxtModule({
|
|
|
157
157
|
clientId: "",
|
|
158
158
|
clientSecret: "",
|
|
159
159
|
serverUrl: "",
|
|
160
|
+
serverUrlInternal: "",
|
|
160
161
|
realm: "",
|
|
161
162
|
redirectURL: ""
|
|
162
163
|
});
|
|
@@ -249,6 +250,16 @@ const module = defineNuxtModule({
|
|
|
249
250
|
clientSecret: "",
|
|
250
251
|
redirectURL: ""
|
|
251
252
|
});
|
|
253
|
+
runtimeConfig.oauth.strava = defu(runtimeConfig.oauth.strava, {
|
|
254
|
+
clientId: "",
|
|
255
|
+
clientSecret: "",
|
|
256
|
+
redirectURL: ""
|
|
257
|
+
});
|
|
258
|
+
runtimeConfig.oauth.hubspot = defu(runtimeConfig.oauth.hubspot, {
|
|
259
|
+
clientId: "",
|
|
260
|
+
clientSecret: "",
|
|
261
|
+
redirectURL: ""
|
|
262
|
+
});
|
|
252
263
|
}
|
|
253
264
|
});
|
|
254
265
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default:
|
|
1
|
+
declare const _default: any;
|
|
2
2
|
export default _default;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default:
|
|
1
|
+
declare const _default: any;
|
|
2
2
|
export default _default;
|
|
@@ -35,10 +35,5 @@ export interface OAuthCognitoConfig {
|
|
|
35
35
|
* @default process.env.NUXT_OAUTH_COGNITO_REDIRECT_URL or current URL
|
|
36
36
|
*/
|
|
37
37
|
redirectURL?: string;
|
|
38
|
-
/**
|
|
39
|
-
* AWS Cognito App Custom Domain – some pool configurations require this
|
|
40
|
-
* @default ''
|
|
41
|
-
*/
|
|
42
|
-
domain?: string;
|
|
43
38
|
}
|
|
44
39
|
export declare function defineOAuthCognitoEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthCognitoConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { defu } from "defu";
|
|
1
2
|
import { eventHandler, getQuery, sendRedirect } from "h3";
|
|
3
|
+
import { discovery } from "openid-client";
|
|
2
4
|
import { withQuery } from "ufo";
|
|
3
|
-
import {
|
|
4
|
-
import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
|
|
5
|
+
import { getOAuthRedirectURL, handleAccessTokenErrorResponse, handleMissingConfiguration, requestAccessToken } from "../utils.js";
|
|
5
6
|
import { useRuntimeConfig } from "#imports";
|
|
6
7
|
export function defineOAuthCognitoEventHandler({ config, onSuccess, onError }) {
|
|
7
8
|
return eventHandler(async (event) => {
|
|
@@ -11,9 +12,16 @@ export function defineOAuthCognitoEventHandler({ config, onSuccess, onError }) {
|
|
|
11
12
|
if (!config.clientId || !config.clientSecret || !config.userPoolId || !config.region) {
|
|
12
13
|
return handleMissingConfiguration(event, "cognito", ["clientId", "clientSecret", "userPoolId", "region"], onError);
|
|
13
14
|
}
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
15
|
+
const congitoDiscoveryUrl = new URL(`https://cognito-idp.${config.region}.amazonaws.com/${config.userPoolId}/.well-known/openid-configuration`);
|
|
16
|
+
const issuer = await discovery(congitoDiscoveryUrl, config.clientId, config.clientSecret);
|
|
17
|
+
const {
|
|
18
|
+
authorization_endpoint: authorizationURL,
|
|
19
|
+
token_endpoint: tokenURL,
|
|
20
|
+
userinfo_endpoint: userinfoURL,
|
|
21
|
+
// TODO: implement logout
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
23
|
+
end_session_endpoint: logoutURL
|
|
24
|
+
} = issuer.serverMetadata();
|
|
17
25
|
const query = getQuery(event);
|
|
18
26
|
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
19
27
|
if (!query.code) {
|
|
@@ -46,7 +54,7 @@ export function defineOAuthCognitoEventHandler({ config, onSuccess, onError }) {
|
|
|
46
54
|
}
|
|
47
55
|
const tokenType = tokens.token_type;
|
|
48
56
|
const accessToken = tokens.access_token;
|
|
49
|
-
const user = await $fetch(
|
|
57
|
+
const user = await $fetch(userinfoURL, {
|
|
50
58
|
headers: {
|
|
51
59
|
Authorization: `${tokenType} ${accessToken}`
|
|
52
60
|
}
|
|
@@ -11,8 +11,8 @@ export function defineOAuthDropboxEventHandler({ config, onSuccess, onError }) {
|
|
|
11
11
|
authorizationParams: {}
|
|
12
12
|
});
|
|
13
13
|
const query = getQuery(event);
|
|
14
|
-
if (!config.clientId) {
|
|
15
|
-
return handleMissingConfiguration(event, "dropbox", ["clientId"], onError);
|
|
14
|
+
if (!config.clientId || !config.clientSecret) {
|
|
15
|
+
return handleMissingConfiguration(event, "dropbox", ["clientId", "clientSecret"], onError);
|
|
16
16
|
}
|
|
17
17
|
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
18
18
|
if (!query.code) {
|
|
@@ -24,8 +24,8 @@ export function defineOAuthFacebookEventHandler({
|
|
|
24
24
|
if (!onError) throw error;
|
|
25
25
|
return onError(event, error);
|
|
26
26
|
}
|
|
27
|
-
if (!config.clientId) {
|
|
28
|
-
return handleMissingConfiguration(event, "facebook", ["clientId"], onError);
|
|
27
|
+
if (!config.clientId || !config.clientSecret) {
|
|
28
|
+
return handleMissingConfiguration(event, "facebook", ["clientId", "clientSecret"], onError);
|
|
29
29
|
}
|
|
30
30
|
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
31
31
|
if (!query.code) {
|
|
@@ -16,8 +16,8 @@ export function defineOAuthGoogleEventHandler({
|
|
|
16
16
|
authorizationParams: {}
|
|
17
17
|
});
|
|
18
18
|
const query = getQuery(event);
|
|
19
|
-
if (!config.clientId) {
|
|
20
|
-
return handleMissingConfiguration(event, "google", ["clientId"], onError);
|
|
19
|
+
if (!config.clientId || !config.clientSecret) {
|
|
20
|
+
return handleMissingConfiguration(event, "google", ["clientId", "clientSecret"], onError);
|
|
21
21
|
}
|
|
22
22
|
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
23
23
|
if (!query.code) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { OAuthConfig } from '#auth-utils';
|
|
2
|
+
export interface OAuthHubspotConfig {
|
|
3
|
+
/**
|
|
4
|
+
* Hubspot OAuth Client ID
|
|
5
|
+
* @default process.env.NUXT_OAUTH_HUBSPOT_CLIENT_ID
|
|
6
|
+
*/
|
|
7
|
+
clientId?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Hubspot OAuth Client Secret
|
|
10
|
+
* @default process.env.NUXT_OAUTH_HUBSPOT_CLIENT_SECRET
|
|
11
|
+
*/
|
|
12
|
+
clientSecret?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Hubspot OAuth Redirect URL
|
|
15
|
+
* @default process.env.NUXT_OAUTH_HUBSPOT_REDIRECT_URL
|
|
16
|
+
*/
|
|
17
|
+
redirectURL?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Hubspot OAuth Scope
|
|
20
|
+
* @default ['oauth']
|
|
21
|
+
* @see https://developers.hubspot.com/beta-docs/guides/apps/authentication/scopes
|
|
22
|
+
* @example ['accounting', 'automation', 'actions']
|
|
23
|
+
*/
|
|
24
|
+
scope?: string[];
|
|
25
|
+
}
|
|
26
|
+
export declare function defineOAuthHubspotEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthHubspotConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { eventHandler, getQuery, sendRedirect } from "h3";
|
|
2
|
+
import { withQuery } from "ufo";
|
|
3
|
+
import defu from "defu";
|
|
4
|
+
import {
|
|
5
|
+
getOAuthRedirectURL,
|
|
6
|
+
handleAccessTokenErrorResponse,
|
|
7
|
+
handleMissingConfiguration,
|
|
8
|
+
requestAccessToken
|
|
9
|
+
} from "../utils.js";
|
|
10
|
+
import { useRuntimeConfig } from "#imports";
|
|
11
|
+
export function defineOAuthHubspotEventHandler({ config, onSuccess, onError }) {
|
|
12
|
+
return eventHandler(async (event) => {
|
|
13
|
+
config = defu(config, useRuntimeConfig(event).oauth?.hubspot);
|
|
14
|
+
if (!config.clientId || !config.clientSecret || !config.redirectURL) {
|
|
15
|
+
return handleMissingConfiguration(event, "hubspot", ["clientId", "clientSecret", "redirectURL"], onError);
|
|
16
|
+
}
|
|
17
|
+
const query = getQuery(event);
|
|
18
|
+
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
19
|
+
if (query.error) {
|
|
20
|
+
return handleAccessTokenErrorResponse(event, "hubspot", query, onError);
|
|
21
|
+
}
|
|
22
|
+
if (!query.code) {
|
|
23
|
+
return sendRedirect(
|
|
24
|
+
event,
|
|
25
|
+
withQuery("https://app.hubspot.com/oauth/authorize", {
|
|
26
|
+
client_id: config.clientId,
|
|
27
|
+
redirect_uri: redirectURL,
|
|
28
|
+
scope: config.scope?.join(" ") || "oauth"
|
|
29
|
+
})
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
const tokens = await requestAccessToken(
|
|
33
|
+
"https://api.hubapi.com/oauth/v1/token",
|
|
34
|
+
{
|
|
35
|
+
body: {
|
|
36
|
+
client_id: config.clientId,
|
|
37
|
+
client_secret: config.clientSecret,
|
|
38
|
+
code: query.code,
|
|
39
|
+
redirect_uri: redirectURL,
|
|
40
|
+
grant_type: "authorization_code"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
if (tokens.error) {
|
|
45
|
+
return handleAccessTokenErrorResponse(event, "hubspot", tokens, onError);
|
|
46
|
+
}
|
|
47
|
+
const info = await $fetch("https://api.hubapi.com/oauth/v1/access-tokens/" + tokens.access_token);
|
|
48
|
+
return onSuccess(event, {
|
|
49
|
+
user: {
|
|
50
|
+
id: info.user_id,
|
|
51
|
+
email: info.user,
|
|
52
|
+
domain: info.hub_domain
|
|
53
|
+
},
|
|
54
|
+
tokens
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -12,21 +12,21 @@ export interface OAuthInstagramConfig {
|
|
|
12
12
|
clientSecret?: string;
|
|
13
13
|
/**
|
|
14
14
|
* Instagram OAuth Scope
|
|
15
|
-
* @
|
|
15
|
+
* @required [ 'business_basic' ]
|
|
16
16
|
* @see https://developers.facebook.com/docs/instagram-basic-display-api/overview#permissions
|
|
17
|
-
* @example [ '
|
|
17
|
+
* @example [ 'business_basic', 'business_manage_messages' ],
|
|
18
18
|
*/
|
|
19
|
-
scope?:
|
|
19
|
+
scope?: ('business_basic' | 'business_content_publish' | 'business_manage_comments' | 'business_manage_messages')[];
|
|
20
20
|
/**
|
|
21
21
|
* Instagram OAuth User Fields
|
|
22
22
|
* @default [ 'id', 'username'],
|
|
23
23
|
* @see https://developers.facebook.com/docs/instagram-basic-display-api/reference/user#fields
|
|
24
|
-
* @example [ 'id', 'username', 'account_type', '
|
|
24
|
+
* @example [ 'id', 'username', 'user_id', 'account_type', 'profile_picture_url' ],
|
|
25
25
|
*/
|
|
26
|
-
fields?:
|
|
26
|
+
fields?: ('id' | 'user_id' | 'username' | 'name' | 'account_type' | 'profile_picture_url' | 'followers_count' | 'follows_count' | 'media_count')[];
|
|
27
27
|
/**
|
|
28
28
|
* Instagram OAuth Authorization URL
|
|
29
|
-
* @default 'https://
|
|
29
|
+
* @default 'https://www.instagram.com/oauth/authorize'
|
|
30
30
|
*/
|
|
31
31
|
authorizationURL?: string;
|
|
32
32
|
/**
|
|
@@ -10,8 +10,8 @@ export function defineOAuthInstagramEventHandler({
|
|
|
10
10
|
}) {
|
|
11
11
|
return eventHandler(async (event) => {
|
|
12
12
|
config = defu(config, useRuntimeConfig(event).oauth?.instagram, {
|
|
13
|
-
scope: ["
|
|
14
|
-
authorizationURL: "https://
|
|
13
|
+
scope: ["business_basic"],
|
|
14
|
+
authorizationURL: "https://www.instagram.com/oauth/authorize",
|
|
15
15
|
tokenURL: "https://api.instagram.com/oauth/access_token",
|
|
16
16
|
authorizationParams: {}
|
|
17
17
|
});
|
|
@@ -20,13 +20,17 @@ export function defineOAuthInstagramEventHandler({
|
|
|
20
20
|
const error = createError({
|
|
21
21
|
statusCode: 401,
|
|
22
22
|
message: `Instagram login failed: ${query.error || "Unknown error"}`,
|
|
23
|
-
data:
|
|
23
|
+
data: {
|
|
24
|
+
error: query.error,
|
|
25
|
+
error_reason: query.error_reason,
|
|
26
|
+
error_description: query.error_description
|
|
27
|
+
}
|
|
24
28
|
});
|
|
25
29
|
if (!onError) throw error;
|
|
26
30
|
return onError(event, error);
|
|
27
31
|
}
|
|
28
|
-
if (!config.clientId) {
|
|
29
|
-
return handleMissingConfiguration(event, "instagram", ["clientId"], onError);
|
|
32
|
+
if (!config.clientId || !config.clientSecret) {
|
|
33
|
+
return handleMissingConfiguration(event, "instagram", ["clientId", "clientSecret"], onError);
|
|
30
34
|
}
|
|
31
35
|
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
32
36
|
if (!query.code) {
|
|
@@ -36,7 +40,7 @@ export function defineOAuthInstagramEventHandler({
|
|
|
36
40
|
withQuery(config.authorizationURL, {
|
|
37
41
|
client_id: config.clientId,
|
|
38
42
|
redirect_uri: redirectURL,
|
|
39
|
-
scope: config.scope.join(
|
|
43
|
+
scope: config.scope.join(),
|
|
40
44
|
response_type: "code"
|
|
41
45
|
})
|
|
42
46
|
);
|
|
@@ -55,9 +59,9 @@ export function defineOAuthInstagramEventHandler({
|
|
|
55
59
|
}
|
|
56
60
|
const accessToken = tokens.access_token;
|
|
57
61
|
config.fields = config.fields || ["id", "username"];
|
|
58
|
-
const fields = config.fields.join(
|
|
62
|
+
const fields = config.fields.join();
|
|
59
63
|
const user = await $fetch(
|
|
60
|
-
`https://graph.instagram.com/
|
|
64
|
+
`https://graph.instagram.com/v21.0/me?fields=${fields}&access_token=${accessToken}`
|
|
61
65
|
);
|
|
62
66
|
if (!user) {
|
|
63
67
|
throw new Error("Instagram login failed: no user found");
|
|
@@ -12,10 +12,17 @@ export interface OAuthKeycloakConfig {
|
|
|
12
12
|
clientSecret?: string;
|
|
13
13
|
/**
|
|
14
14
|
* Keycloak OAuth Server URL
|
|
15
|
-
* @example http://192.168.1.10:8080
|
|
15
|
+
* @example http://192.168.1.10:8080
|
|
16
16
|
* @default process.env.NUXT_OAUTH_KEYCLOAK_SERVER_URL
|
|
17
17
|
*/
|
|
18
18
|
serverUrl?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Optional Keycloak OAuth Server URL to use internally, e.g. if Nuxt connects to a Docker hostname while the browser
|
|
21
|
+
* redirect goes to localhost
|
|
22
|
+
* @example http://keycloak:8080
|
|
23
|
+
* @default process.env.NUXT_OAUTH_KEYCLOAK_SERVER_URL_INTERNAL
|
|
24
|
+
*/
|
|
25
|
+
serverUrlInternal?: string;
|
|
19
26
|
/**
|
|
20
27
|
* Keycloak OAuth Realm
|
|
21
28
|
* @default process.env.NUXT_OAUTH_KEYCLOAK_REALM
|
|
@@ -33,7 +40,7 @@ export interface OAuthKeycloakConfig {
|
|
|
33
40
|
*/
|
|
34
41
|
authorizationParams?: Record<string, string>;
|
|
35
42
|
/**
|
|
36
|
-
* Redirect URL to
|
|
43
|
+
* Redirect URL to allow overriding for situations like prod failing to determine public hostname
|
|
37
44
|
* @default process.env.NUXT_OAUTH_KEYCLOAK_REDIRECT_URL or current URL
|
|
38
45
|
*/
|
|
39
46
|
redirectURL?: string;
|
|
@@ -26,8 +26,9 @@ export function defineOAuthKeycloakEventHandler({
|
|
|
26
26
|
return handleMissingConfiguration(event, "keycloak", ["clientId", "clientSecret", "serverUrl", "realm"], onError);
|
|
27
27
|
}
|
|
28
28
|
const realmURL = `${config.serverUrl}/realms/${config.realm}`;
|
|
29
|
+
const realmURLInternal = `${config.serverUrlInternal || config.serverUrl}/realms/${config.realm}`;
|
|
29
30
|
const authorizationURL = `${realmURL}/protocol/openid-connect/auth`;
|
|
30
|
-
const tokenURL = `${
|
|
31
|
+
const tokenURL = `${realmURLInternal}/protocol/openid-connect/token`;
|
|
31
32
|
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
32
33
|
if (!query.code) {
|
|
33
34
|
config.scope = config.scope || ["openid"];
|
|
@@ -12,8 +12,8 @@ export function defineOAuthPaypalEventHandler({ config, onSuccess, onError }) {
|
|
|
12
12
|
authorizationParams: {}
|
|
13
13
|
});
|
|
14
14
|
const query = getQuery(event);
|
|
15
|
-
if (!config.clientId) {
|
|
16
|
-
return handleMissingConfiguration(event, "paypal", ["clientId"], onError);
|
|
15
|
+
if (!config.clientId || !config.clientSecret) {
|
|
16
|
+
return handleMissingConfiguration(event, "paypal", ["clientId", "clientSecret"], onError);
|
|
17
17
|
}
|
|
18
18
|
let paypalAPI = "api-m.paypal.com";
|
|
19
19
|
if (config.sandbox) {
|
|
@@ -92,4 +92,4 @@ export interface OAuthSeznamUser {
|
|
|
92
92
|
*/
|
|
93
93
|
gender?: string | null;
|
|
94
94
|
}
|
|
95
|
-
export declare function
|
|
95
|
+
export declare function defineOAuthSeznamEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthSeznamConfig, OAuthSeznamUser>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
@@ -3,16 +3,16 @@ import { eventHandler, getQuery, sendRedirect } from "h3";
|
|
|
3
3
|
import { withQuery } from "ufo";
|
|
4
4
|
import { getOAuthRedirectURL, handleAccessTokenErrorResponse, handleMissingConfiguration, requestAccessToken } from "../utils.js";
|
|
5
5
|
import { useRuntimeConfig } from "#imports";
|
|
6
|
-
export function
|
|
6
|
+
export function defineOAuthSeznamEventHandler({ config, onSuccess, onError }) {
|
|
7
7
|
return eventHandler(async (event) => {
|
|
8
|
-
config = defu(config, useRuntimeConfig(event).oauth?.
|
|
8
|
+
config = defu(config, useRuntimeConfig(event).oauth?.seznam, {
|
|
9
9
|
authorizationURL: "https://login.szn.cz/api/v1/oauth/auth",
|
|
10
10
|
tokenURL: "https://login.szn.cz/api/v1/oauth/token",
|
|
11
11
|
userURL: "https://login.szn.cz/api/v1/user"
|
|
12
12
|
});
|
|
13
13
|
const query = getQuery(event);
|
|
14
|
-
if (!config.clientId) {
|
|
15
|
-
return handleMissingConfiguration(event, "seznam", ["clientId"], onError);
|
|
14
|
+
if (!config.clientId || !config.clientSecret) {
|
|
15
|
+
return handleMissingConfiguration(event, "seznam", ["clientId", "clientSecret"], onError);
|
|
16
16
|
}
|
|
17
17
|
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
18
18
|
if (!query.code) {
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { OAuthConfig } from '#auth-utils';
|
|
2
|
+
export interface OAuthStravaConfig {
|
|
3
|
+
/**
|
|
4
|
+
* Strava OAuth Client ID
|
|
5
|
+
* @default process.env.NUXT_OAUTH_STRAVA_CLIENT_ID
|
|
6
|
+
*/
|
|
7
|
+
clientId?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Strava OAuth Client Secret
|
|
10
|
+
* @default process.env.NUXT_OAUTH_STRAVA_CLIENT_SECRET
|
|
11
|
+
*/
|
|
12
|
+
clientSecret?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Strava OAuth Scope
|
|
15
|
+
* @default []
|
|
16
|
+
* @see https://developers.strava.com/docs/authentication/ # Details About Requesting Access
|
|
17
|
+
* @example ['read', 'read_all', 'profile:read_all', 'profile:write', 'activity:read', 'activity:read_all', 'activity:write']
|
|
18
|
+
*/
|
|
19
|
+
scope?: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Redirect URL to allow overriding for situations like prod failing to determine public hostname
|
|
22
|
+
* @default process.env.NUXT_OAUTH_STRAVA_REDIRECT_URL or current URL
|
|
23
|
+
*/
|
|
24
|
+
redirectURL?: string;
|
|
25
|
+
/**
|
|
26
|
+
* To show the authorization prompt to the user, 'force' will always show the prompt
|
|
27
|
+
* @default 'auto'
|
|
28
|
+
* @see https://developers.strava.com/docs/authentication/ # Details About Requesting Access
|
|
29
|
+
*/
|
|
30
|
+
approvalPrompt?: 'auto' | 'force';
|
|
31
|
+
}
|
|
32
|
+
export interface OAuthStravaUser {
|
|
33
|
+
/**
|
|
34
|
+
* The unique identifier of the athlete
|
|
35
|
+
*/
|
|
36
|
+
id: number;
|
|
37
|
+
/**
|
|
38
|
+
* The username of the athlete
|
|
39
|
+
*/
|
|
40
|
+
username: string;
|
|
41
|
+
/**
|
|
42
|
+
* Resource state, indicates level of detail.
|
|
43
|
+
* - Meta (1): Basic information
|
|
44
|
+
* - Summary (2): Summary information
|
|
45
|
+
* - Detail (3): Detailed information
|
|
46
|
+
* @see https://developers.strava.com/docs/reference/#api-models-DetailedAthlete
|
|
47
|
+
*/
|
|
48
|
+
resource_state: 1 | 2 | 3;
|
|
49
|
+
/**
|
|
50
|
+
* The athlete's first name
|
|
51
|
+
*/
|
|
52
|
+
firstname: string;
|
|
53
|
+
/**
|
|
54
|
+
* The athlete's last name
|
|
55
|
+
*/
|
|
56
|
+
lastname: string;
|
|
57
|
+
/**
|
|
58
|
+
* The athlete's bio
|
|
59
|
+
*/
|
|
60
|
+
bio: string;
|
|
61
|
+
/**
|
|
62
|
+
* The athlete's city
|
|
63
|
+
*/
|
|
64
|
+
city: string;
|
|
65
|
+
/**
|
|
66
|
+
* The athlete's state or geographical region
|
|
67
|
+
*/
|
|
68
|
+
state: string;
|
|
69
|
+
/**
|
|
70
|
+
* The athlete's country
|
|
71
|
+
*/
|
|
72
|
+
country: string;
|
|
73
|
+
/**
|
|
74
|
+
* The athlete's sex
|
|
75
|
+
*/
|
|
76
|
+
sex: string;
|
|
77
|
+
/**
|
|
78
|
+
* Whether the athlete has any Summit subscription
|
|
79
|
+
* @see https://developers.strava.com/docs/reference/#api-models-DetailedAthlete
|
|
80
|
+
*/
|
|
81
|
+
summit: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* The time at which the athlete was created
|
|
84
|
+
*/
|
|
85
|
+
created_at: Date;
|
|
86
|
+
/**
|
|
87
|
+
* The time at which the athlete was last updated
|
|
88
|
+
*/
|
|
89
|
+
updated_at: Date;
|
|
90
|
+
/**
|
|
91
|
+
* The athlete's weight
|
|
92
|
+
*/
|
|
93
|
+
weight: number;
|
|
94
|
+
/**
|
|
95
|
+
* URL to a 124x124 pixel profile picture
|
|
96
|
+
*/
|
|
97
|
+
profile_medium: string;
|
|
98
|
+
/**
|
|
99
|
+
* URL to a 62x62 pixel profile picture
|
|
100
|
+
*/
|
|
101
|
+
profile: string;
|
|
102
|
+
/**
|
|
103
|
+
* The athlete's timezone
|
|
104
|
+
*/
|
|
105
|
+
timezone: string;
|
|
106
|
+
}
|
|
107
|
+
export interface OAuthStravaTokens {
|
|
108
|
+
token_type: 'Bearer';
|
|
109
|
+
expires_at: number;
|
|
110
|
+
expires_in: number;
|
|
111
|
+
access_token: string;
|
|
112
|
+
refresh_token: string;
|
|
113
|
+
athlete: OAuthStravaUser;
|
|
114
|
+
error?: string;
|
|
115
|
+
}
|
|
116
|
+
export declare function defineOAuthStravaEventHandler({ config, onSuccess, onError, }: OAuthConfig<OAuthStravaConfig, OAuthStravaUser>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { eventHandler, getQuery, sendRedirect } from "h3";
|
|
2
|
+
import { withQuery } from "ufo";
|
|
3
|
+
import { defu } from "defu";
|
|
4
|
+
import {
|
|
5
|
+
getOAuthRedirectURL,
|
|
6
|
+
handleAccessTokenErrorResponse,
|
|
7
|
+
handleMissingConfiguration,
|
|
8
|
+
requestAccessToken
|
|
9
|
+
} from "../utils.js";
|
|
10
|
+
import { useRuntimeConfig, createError } from "#imports";
|
|
11
|
+
export function defineOAuthStravaEventHandler({
|
|
12
|
+
config,
|
|
13
|
+
onSuccess,
|
|
14
|
+
onError
|
|
15
|
+
}) {
|
|
16
|
+
return eventHandler(async (event) => {
|
|
17
|
+
config = defu(config, useRuntimeConfig(event).oauth?.strava);
|
|
18
|
+
const query = getQuery(event);
|
|
19
|
+
if (query.error) {
|
|
20
|
+
const error = createError({
|
|
21
|
+
statusCode: 401,
|
|
22
|
+
message: `Strava login failed: ${query.error || "Unknown error"}`,
|
|
23
|
+
data: query
|
|
24
|
+
});
|
|
25
|
+
if (!onError) throw error;
|
|
26
|
+
return onError(event, error);
|
|
27
|
+
}
|
|
28
|
+
if (!config.clientId || !config.clientSecret) {
|
|
29
|
+
return handleMissingConfiguration(
|
|
30
|
+
event,
|
|
31
|
+
"strava",
|
|
32
|
+
["clientId", "clientSecret"],
|
|
33
|
+
onError
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
const authorizationURL = "https://www.strava.com/oauth/authorize";
|
|
37
|
+
const tokenURL = "https://www.strava.com/oauth/token";
|
|
38
|
+
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
39
|
+
if (!query.code) {
|
|
40
|
+
return sendRedirect(
|
|
41
|
+
event,
|
|
42
|
+
withQuery(authorizationURL, {
|
|
43
|
+
client_id: config.clientId,
|
|
44
|
+
redirect_uri: redirectURL,
|
|
45
|
+
response_type: "code",
|
|
46
|
+
approval_prompt: config.approvalPrompt || "auto",
|
|
47
|
+
scope: config.scope
|
|
48
|
+
})
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
const tokens = await requestAccessToken(tokenURL, {
|
|
52
|
+
body: {
|
|
53
|
+
client_id: config.clientId,
|
|
54
|
+
client_secret: config.clientSecret,
|
|
55
|
+
code: query.code,
|
|
56
|
+
grant_type: "authorization_code",
|
|
57
|
+
redirect_uri: redirectURL
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
if (tokens.error) {
|
|
61
|
+
return handleAccessTokenErrorResponse(event, "strava", tokens, onError);
|
|
62
|
+
}
|
|
63
|
+
const user = await $fetch("https://www.strava.com/api/v3/athlete", {
|
|
64
|
+
headers: {
|
|
65
|
+
Authorization: `Bearer ${tokens.access_token}`
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
return onSuccess(event, {
|
|
69
|
+
user,
|
|
70
|
+
tokens
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
@@ -11,8 +11,8 @@ export function defineOAuthTwitchEventHandler({ config, onSuccess, onError }) {
|
|
|
11
11
|
authorizationParams: {}
|
|
12
12
|
});
|
|
13
13
|
const query = getQuery(event);
|
|
14
|
-
if (!config.clientId) {
|
|
15
|
-
return handleMissingConfiguration(event, "twitch", ["clientId"], onError);
|
|
14
|
+
if (!config.clientId || !config.clientSecret) {
|
|
15
|
+
return handleMissingConfiguration(event, "twitch", ["clientId", "clientSecret"], onError);
|
|
16
16
|
}
|
|
17
17
|
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
18
18
|
if (!query.code) {
|
|
@@ -5,22 +5,27 @@ import type { OAuthConfig } from '#auth-utils';
|
|
|
5
5
|
*/
|
|
6
6
|
export interface OAuthWorkOSConfig {
|
|
7
7
|
/**
|
|
8
|
-
* WorkOS OAuth Client ID
|
|
8
|
+
* WorkOS OAuth Client ID *required
|
|
9
9
|
* @default process.env.NUXT_OAUTH_WORKOS_CLIENT_ID
|
|
10
10
|
*/
|
|
11
11
|
clientId?: string;
|
|
12
12
|
/**
|
|
13
|
-
* WorkOS OAuth Client Secret (API Key)
|
|
13
|
+
* WorkOS OAuth Client Secret (API Key) *required
|
|
14
14
|
* @default process.env.NUXT_OAUTH_WORKOS_CLIENT_SECRET
|
|
15
15
|
*/
|
|
16
16
|
clientSecret?: string;
|
|
17
17
|
/**
|
|
18
|
-
* WorkOS OAuth
|
|
18
|
+
* WorkOS OAuth Organization ID *not required
|
|
19
|
+
* @default process.env.NUXT_OAUTH_WORKOS_ORGANIZATION_ID
|
|
20
|
+
*/
|
|
21
|
+
organizationId?: string;
|
|
22
|
+
/**
|
|
23
|
+
* WorkOS OAuth Connection ID *not required
|
|
19
24
|
* @default process.env.NUXT_OAUTH_WORKOS_CONNECTION_ID
|
|
20
25
|
*/
|
|
21
26
|
connectionId?: string;
|
|
22
27
|
/**
|
|
23
|
-
* WorkOS OAuth screen hint
|
|
28
|
+
* WorkOS OAuth screen hint *not required
|
|
24
29
|
* @default 'sign-in'
|
|
25
30
|
*/
|
|
26
31
|
screenHint?: 'sign-in' | 'sign-up';
|
|
@@ -21,14 +21,13 @@ export function defineOAuthWorkOSEventHandler({ config, onSuccess, onError }) {
|
|
|
21
21
|
response_type: "code",
|
|
22
22
|
provider: "authkit",
|
|
23
23
|
client_id: config.clientId,
|
|
24
|
-
redirect_uri: redirectURL,
|
|
25
|
-
connection_id: config.connectionId,
|
|
26
|
-
screen_hint: config.screenHint
|
|
24
|
+
redirect_uri: redirectURL || void 0,
|
|
25
|
+
connection_id: config.connectionId || void 0,
|
|
26
|
+
screen_hint: config.screenHint || "sign-in",
|
|
27
|
+
organization_id: config.organizationId || void 0
|
|
27
28
|
})
|
|
28
29
|
);
|
|
29
30
|
}
|
|
30
|
-
const ip_address = getRequestIP(event);
|
|
31
|
-
const user_agent = getRequestHeader(event, "user-agent");
|
|
32
31
|
const authenticateResponse = await requestAccessToken("https://api.workos.com/user_management/authenticate", {
|
|
33
32
|
headers: {
|
|
34
33
|
"Content-Type": "application/json"
|
|
@@ -38,8 +37,8 @@ export function defineOAuthWorkOSEventHandler({ config, onSuccess, onError }) {
|
|
|
38
37
|
client_id: config.clientId,
|
|
39
38
|
client_secret: config.clientSecret,
|
|
40
39
|
redirect_uri: redirectURL,
|
|
41
|
-
ip_address,
|
|
42
|
-
user_agent,
|
|
40
|
+
ip_address: getRequestIP(event),
|
|
41
|
+
user_agent: getRequestHeader(event, "user-agent"),
|
|
43
42
|
code: query.code
|
|
44
43
|
}
|
|
45
44
|
});
|
|
@@ -25,8 +25,8 @@ export function defineOAuthXEventHandler({
|
|
|
25
25
|
}
|
|
26
26
|
});
|
|
27
27
|
const query = getQuery(event);
|
|
28
|
-
if (!config.clientId) {
|
|
29
|
-
return handleMissingConfiguration(event, "x", ["clientId"], onError);
|
|
28
|
+
if (!config.clientId || !config.clientSecret) {
|
|
29
|
+
return handleMissingConfiguration(event, "x", ["clientId", "clientSecret"], onError);
|
|
30
30
|
}
|
|
31
31
|
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
32
32
|
if (!query.code) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { H3Event, H3Error } from 'h3';
|
|
2
|
-
export type OAuthProvider = 'auth0' | 'authentik' | 'battledotnet' | 'cognito' | 'discord' | 'dropbox' | 'facebook' | 'github' | 'gitlab' | 'google' | 'instagram' | 'keycloak' | 'linear' | 'linkedin' | 'microsoft' | 'paypal' | 'polar' | 'spotify' | 'seznam' | 'steam' | 'tiktok' | 'twitch' | 'vk' | 'workos' | 'x' | 'xsuaa' | 'yandex' | 'zitadel' | (string & {});
|
|
2
|
+
export type OAuthProvider = 'auth0' | 'authentik' | 'battledotnet' | 'cognito' | 'discord' | 'dropbox' | 'facebook' | 'github' | 'gitlab' | 'google' | 'hubspot' | 'instagram' | 'keycloak' | 'linear' | 'linkedin' | 'microsoft' | 'paypal' | 'polar' | 'spotify' | 'seznam' | 'steam' | 'strava' | 'tiktok' | 'twitch' | 'vk' | 'workos' | 'x' | 'xsuaa' | 'yandex' | 'zitadel' | (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;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-auth-utils",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.7",
|
|
4
4
|
"description": "Add Authentication to Nuxt applications with secured & sealed cookies sessions.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"type": "module",
|
|
11
|
-
"packageManager": "pnpm@9.
|
|
11
|
+
"packageManager": "pnpm@9.15.0",
|
|
12
12
|
"exports": {
|
|
13
13
|
".": {
|
|
14
14
|
"types": "./dist/types.d.ts",
|
|
@@ -36,12 +36,13 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@adonisjs/hash": "^9.0.5",
|
|
39
|
-
"@nuxt/kit": "^3.14.
|
|
39
|
+
"@nuxt/kit": "^3.14.1592",
|
|
40
40
|
"defu": "^6.1.4",
|
|
41
41
|
"h3": "^1.13.0",
|
|
42
42
|
"hookable": "^5.5.3",
|
|
43
43
|
"ofetch": "^1.4.1",
|
|
44
44
|
"ohash": "^1.1.4",
|
|
45
|
+
"openid-client": "^6.1.7",
|
|
45
46
|
"pathe": "^1.1.2",
|
|
46
47
|
"scule": "^1.3.0",
|
|
47
48
|
"uncrypto": "^0.1.3"
|
|
@@ -59,20 +60,20 @@
|
|
|
59
60
|
}
|
|
60
61
|
},
|
|
61
62
|
"devDependencies": {
|
|
62
|
-
"@iconify-json/simple-icons": "^1.2.
|
|
63
|
+
"@iconify-json/simple-icons": "^1.2.15",
|
|
63
64
|
"@nuxt/devtools": "latest",
|
|
64
|
-
"@nuxt/eslint-config": "^0.
|
|
65
|
+
"@nuxt/eslint-config": "^0.7.3",
|
|
65
66
|
"@nuxt/module-builder": "^0.8.4",
|
|
66
|
-
"@nuxt/schema": "^3.14.
|
|
67
|
-
"@nuxt/test-utils": "^3.
|
|
68
|
-
"@nuxt/ui": "^2.
|
|
69
|
-
"@nuxt/ui-pro": "^1.
|
|
67
|
+
"@nuxt/schema": "^3.14.1592",
|
|
68
|
+
"@nuxt/test-utils": "^3.15.1",
|
|
69
|
+
"@nuxt/ui": "^2.20.0",
|
|
70
|
+
"@nuxt/ui-pro": "^1.6.0",
|
|
70
71
|
"@simplewebauthn/types": "^11.0.0",
|
|
71
72
|
"changelogen": "^0.5.7",
|
|
72
|
-
"eslint": "^9.
|
|
73
|
-
"nuxt": "^3.14.
|
|
73
|
+
"eslint": "^9.16.0",
|
|
74
|
+
"nuxt": "^3.14.1592",
|
|
74
75
|
"typescript": "^5.6.3",
|
|
75
|
-
"vitest": "^2.1.
|
|
76
|
+
"vitest": "^2.1.8",
|
|
76
77
|
"vue-tsc": "^2.1.10"
|
|
77
78
|
}
|
|
78
79
|
}
|