nuxt-auth-utils 0.5.34 → 0.6.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 +118 -15
- package/dist/module.d.mts +3 -3
- package/dist/module.json +4 -1
- package/dist/module.mjs +18 -31
- package/dist/runtime/app/composables/session.js +3 -2
- package/dist/runtime/server/api/session.delete.d.ts +1 -1
- package/dist/runtime/server/api/session.delete.js +2 -2
- package/dist/runtime/server/api/session.get.d.ts +1 -1
- package/dist/runtime/server/api/session.get.js +6 -2
- package/dist/runtime/server/lib/atproto/bluesky.d.ts +3 -3
- package/dist/runtime/server/lib/atproto/bluesky.js +6 -6
- package/dist/runtime/server/lib/oauth/apple.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/apple.js +3 -4
- package/dist/runtime/server/lib/oauth/atlassian.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/atlassian.js +8 -8
- package/dist/runtime/server/lib/oauth/auth0.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/auth0.js +4 -4
- package/dist/runtime/server/lib/oauth/authentik.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/authentik.js +6 -6
- package/dist/runtime/server/lib/oauth/azureb2c.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/azureb2c.js +5 -5
- package/dist/runtime/server/lib/oauth/battledotnet.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/battledotnet.js +6 -6
- package/dist/runtime/server/lib/oauth/box.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/box.js +5 -5
- package/dist/runtime/server/lib/oauth/cloudflare.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/cloudflare.js +5 -5
- package/dist/runtime/server/lib/oauth/cognito.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/cognito.js +4 -4
- package/dist/runtime/server/lib/oauth/discord.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/discord.js +4 -4
- package/dist/runtime/server/lib/oauth/dropbox.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/dropbox.js +5 -5
- package/dist/runtime/server/lib/oauth/facebook.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/facebook.js +7 -7
- package/dist/runtime/server/lib/oauth/gitea.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/gitea.js +5 -5
- package/dist/runtime/server/lib/oauth/github.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/github.js +6 -6
- package/dist/runtime/server/lib/oauth/gitlab.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/gitlab.js +5 -5
- package/dist/runtime/server/lib/oauth/google.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/google.js +4 -4
- package/dist/runtime/server/lib/oauth/heroku.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/heroku.js +5 -5
- package/dist/runtime/server/lib/oauth/hubspot.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/hubspot.js +4 -4
- package/dist/runtime/server/lib/oauth/instagram.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/instagram.js +6 -6
- package/dist/runtime/server/lib/oauth/keycloak.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/keycloak.js +6 -6
- package/dist/runtime/server/lib/oauth/kick.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/kick.js +5 -5
- package/dist/runtime/server/lib/oauth/line.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/line.js +6 -6
- package/dist/runtime/server/lib/oauth/linear.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/linear.js +6 -6
- package/dist/runtime/server/lib/oauth/linkedin.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/linkedin.js +4 -4
- package/dist/runtime/server/lib/oauth/livechat.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/livechat.js +4 -4
- package/dist/runtime/server/lib/oauth/mastodon.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/mastodon.js +10 -11
- package/dist/runtime/server/lib/oauth/microsoft.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/microsoft.js +5 -5
- package/dist/runtime/server/lib/oauth/oidc.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/oidc.js +6 -6
- package/dist/runtime/server/lib/oauth/okta.d.ts +4 -4
- package/dist/runtime/server/lib/oauth/okta.js +8 -8
- package/dist/runtime/server/lib/oauth/ory.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/ory.js +6 -6
- package/dist/runtime/server/lib/oauth/osu.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/osu.js +4 -4
- package/dist/runtime/server/lib/oauth/paypal.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/paypal.js +5 -5
- package/dist/runtime/server/lib/oauth/polar.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/polar.js +5 -5
- package/dist/runtime/server/lib/oauth/riotgames.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/riotgames.js +5 -5
- package/dist/runtime/server/lib/oauth/roblox.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/roblox.js +4 -4
- package/dist/runtime/server/lib/oauth/salesforce.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/salesforce.js +5 -5
- 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/shopifyCustomer.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/shopifyCustomer.js +7 -7
- package/dist/runtime/server/lib/oauth/slack.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/slack.js +5 -5
- package/dist/runtime/server/lib/oauth/spotify.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/spotify.js +4 -4
- package/dist/runtime/server/lib/oauth/steam.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/steam.js +9 -9
- package/dist/runtime/server/lib/oauth/strava.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/strava.js +5 -5
- package/dist/runtime/server/lib/oauth/tiktok.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/tiktok.js +5 -5
- package/dist/runtime/server/lib/oauth/twitch.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/twitch.js +5 -5
- package/dist/runtime/server/lib/oauth/vk.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/vk.js +5 -5
- package/dist/runtime/server/lib/oauth/wechat.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/wechat.js +7 -7
- package/dist/runtime/server/lib/oauth/workos.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/workos.js +3 -4
- package/dist/runtime/server/lib/oauth/x.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/x.js +4 -4
- package/dist/runtime/server/lib/oauth/xsuaa.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/xsuaa.js +4 -4
- package/dist/runtime/server/lib/oauth/yandex.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/yandex.js +4 -4
- package/dist/runtime/server/lib/oauth/zendesk.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/zendesk.js +4 -4
- package/dist/runtime/server/lib/oauth/zitadel.d.ts +1 -1
- package/dist/runtime/server/lib/oauth/zitadel.js +6 -6
- package/dist/runtime/server/lib/session.d.ts +11 -0
- package/dist/runtime/server/lib/session.js +33 -0
- package/dist/runtime/server/lib/utils.d.ts +9 -8
- package/dist/runtime/server/lib/utils.js +13 -11
- package/dist/runtime/server/lib/webauthn/authenticate.d.ts +1 -1
- package/dist/runtime/server/lib/webauthn/authenticate.js +8 -9
- package/dist/runtime/server/lib/webauthn/register.d.ts +2 -2
- package/dist/runtime/server/lib/webauthn/register.js +10 -11
- package/dist/runtime/server/plugins/oauth-hash.d.ts +2 -0
- package/dist/runtime/server/plugins/oauth-hash.js +8 -0
- package/dist/runtime/server/routes/atproto/client-metadata.json.get.d.ts +1 -1
- package/dist/runtime/server/routes/atproto/client-metadata.json.get.js +5 -5
- package/dist/runtime/server/utils/atproto.d.ts +2 -2
- package/dist/runtime/server/utils/atproto.js +3 -4
- package/dist/runtime/server/utils/password.js +1 -1
- package/dist/runtime/server/utils/session.d.ts +26 -17
- package/dist/runtime/server/utils/session.js +49 -29
- package/dist/runtime/types/event.d.ts +27 -3
- package/dist/runtime/types/index.d.ts +1 -1
- package/dist/runtime/types/oauth-config.d.ts +3 -3
- package/dist/runtime/types/session.d.ts +5 -2
- package/dist/runtime/types/webauthn.d.ts +14 -17
- package/package.json +10 -6
- package/dist/runtime/server/plugins/oauth.d.ts +0 -2
- package/dist/runtime/server/plugins/oauth.js +0 -16
- package/dist/runtime/server/plugins/ws.d.ts +0 -2
- package/dist/runtime/server/plugins/ws.js +0 -7
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SessionConfig } from 'nuxt/server';
|
|
2
|
+
/**
|
|
3
|
+
* Name of the cookie the user session is sealed into.
|
|
4
|
+
*/
|
|
5
|
+
export declare const USER_SESSION_COOKIE_NAME = "nuxt-auth-session";
|
|
6
|
+
/**
|
|
7
|
+
* The session config of the user session: the runtime config merged under `config`, without `password`.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveSessionConfig(config?: Omit<SessionConfig, 'password'>): SessionConfig & {
|
|
10
|
+
name: string;
|
|
11
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useRuntimeConfig } from "nuxt/server";
|
|
2
|
+
import { defu } from "defu";
|
|
3
|
+
export const USER_SESSION_COOKIE_NAME = "nuxt-auth-session";
|
|
4
|
+
const APP_SESSION_COOKIE_NAME = "nuxt-session";
|
|
5
|
+
export function resolveSessionConfig(config = {}) {
|
|
6
|
+
const runtimeConfig = useRuntimeConfig();
|
|
7
|
+
warnLegacySessionPassword(runtimeConfig);
|
|
8
|
+
const { password: _password, ...finalConfig } = defu(config, runtimeConfig.session, { name: USER_SESSION_COOKIE_NAME });
|
|
9
|
+
if (finalConfig.name === APP_SESSION_COOKIE_NAME) {
|
|
10
|
+
warnAppSessionName();
|
|
11
|
+
finalConfig.name = USER_SESSION_COOKIE_NAME;
|
|
12
|
+
}
|
|
13
|
+
if (finalConfig.cookie?.secure === "false") {
|
|
14
|
+
finalConfig.cookie = { ...finalConfig.cookie, secure: false };
|
|
15
|
+
}
|
|
16
|
+
return finalConfig;
|
|
17
|
+
}
|
|
18
|
+
let warnedAppSessionName = false;
|
|
19
|
+
function warnAppSessionName() {
|
|
20
|
+
if (warnedAppSessionName) return;
|
|
21
|
+
warnedAppSessionName = true;
|
|
22
|
+
console.warn(`[nuxt-auth-utils] The session name \`${APP_SESSION_COOKIE_NAME}\` is the cookie of \`useSession()\` from \`nuxt/server\`, using \`${USER_SESSION_COOKIE_NAME}\` instead. Set another \`runtimeConfig.session.name\` (\`NUXT_SESSION_NAME\`) to rename the user session cookie.`);
|
|
23
|
+
}
|
|
24
|
+
let checkedLegacySessionPassword = false;
|
|
25
|
+
function warnLegacySessionPassword(runtimeConfig) {
|
|
26
|
+
if (checkedLegacySessionPassword) return;
|
|
27
|
+
checkedLegacySessionPassword = true;
|
|
28
|
+
const envPrefix = runtimeConfig.nitro?.envPrefix || "NUXT_";
|
|
29
|
+
const envKey = `${envPrefix}SESSION_PASSWORD`;
|
|
30
|
+
if (globalThis.process?.env?.[envKey] || runtimeConfig.session?.password) {
|
|
31
|
+
console.warn(`[nuxt-auth-utils] \`${envKey}\` (\`runtimeConfig.session.password\`) is not used anymore. Set \`NUXT_APP_SECRET\` instead, for example with \`openssl rand -base64 32\`. Sessions are now sealed with a secret derived from \`appSecret\`.`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { RequestEvent } from 'nuxt/server';
|
|
2
2
|
import * as jose from 'jose';
|
|
3
3
|
import type { OAuthProvider, OnError } from '#auth-utils';
|
|
4
4
|
export declare const isDevelopment: boolean;
|
|
5
|
-
export declare function
|
|
5
|
+
export declare function isOAuthCookieSecure(): boolean;
|
|
6
|
+
export declare function getOAuthRedirectURL(event: RequestEvent): string;
|
|
6
7
|
/**
|
|
7
8
|
* Request an access token body.
|
|
8
9
|
*
|
|
@@ -34,9 +35,9 @@ export declare function requestAccessToken<T = any>(url: string, options: Reques
|
|
|
34
35
|
*
|
|
35
36
|
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
|
|
36
37
|
*/
|
|
37
|
-
export declare function handleAccessTokenErrorResponse(event:
|
|
38
|
-
export declare function handleMissingConfiguration(event:
|
|
39
|
-
export declare function handleInvalidState(event:
|
|
38
|
+
export declare function handleAccessTokenErrorResponse(event: RequestEvent, oauthProvider: OAuthProvider, oauthError: any, onError?: OnError): any;
|
|
39
|
+
export declare function handleMissingConfiguration(event: RequestEvent, provider: OAuthProvider, missingKeys: string[], onError?: OnError): any;
|
|
40
|
+
export declare function handleInvalidState(event: RequestEvent, provider: OAuthProvider, onError?: OnError): any;
|
|
40
41
|
/**
|
|
41
42
|
* JWT signing using jose
|
|
42
43
|
*
|
|
@@ -62,7 +63,7 @@ interface JWTVerifyOptions {
|
|
|
62
63
|
issuer: string;
|
|
63
64
|
}
|
|
64
65
|
export declare function verifyJwt<T>(token: string, options: JWTVerifyOptions): Promise<T>;
|
|
65
|
-
export declare function handlePkceVerifier(event:
|
|
66
|
+
export declare function handlePkceVerifier(event: RequestEvent): Promise<{
|
|
66
67
|
code_verifier: string;
|
|
67
68
|
code_challenge: string;
|
|
68
69
|
code_challenge_method: string;
|
|
@@ -75,7 +76,7 @@ interface HandleStateOptions {
|
|
|
75
76
|
isCallback?: boolean;
|
|
76
77
|
sameSite?: 'lax' | 'none' | 'strict';
|
|
77
78
|
}
|
|
78
|
-
export declare function handleState(event:
|
|
79
|
+
export declare function handleState(event: RequestEvent, options?: HandleStateOptions): Promise<string | undefined>;
|
|
79
80
|
export declare function parseJwt(token: string): jose.JWTPayload;
|
|
80
|
-
export declare function handleNonce(event:
|
|
81
|
+
export declare function handleNonce(event: RequestEvent): string | undefined;
|
|
81
82
|
export {};
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { deleteCookie, getCookie, getQuery, setCookie } from "
|
|
2
|
-
import {
|
|
3
|
-
import { FetchError } from "ofetch";
|
|
1
|
+
import { createError, deleteCookie, getCookie, getQuery, getRequestURL, setCookie } from "nuxt/server";
|
|
2
|
+
import { $fetch, FetchError } from "ofetch";
|
|
4
3
|
import { snakeCase, upperFirst } from "scule";
|
|
5
4
|
import * as jose from "jose";
|
|
6
5
|
import { subtle, getRandomValues } from "uncrypto";
|
|
7
|
-
import {
|
|
6
|
+
import { resolveSessionConfig } from "./session.js";
|
|
8
7
|
export const isDevelopment = process.env.NODE_ENV === "development";
|
|
8
|
+
export function isOAuthCookieSecure() {
|
|
9
|
+
return !isDevelopment && resolveSessionConfig().cookie?.secure !== false;
|
|
10
|
+
}
|
|
9
11
|
const OAUTH_COOKIE_MAX_AGE = 60 * 10;
|
|
10
12
|
export function getOAuthRedirectURL(event) {
|
|
11
|
-
const requestURL = getRequestURL(event);
|
|
13
|
+
const requestURL = getRequestURL(event, { xForwardedProto: true });
|
|
12
14
|
return `${requestURL.protocol}//${requestURL.host}${requestURL.pathname}`;
|
|
13
15
|
}
|
|
14
16
|
export async function requestAccessToken(url, options) {
|
|
@@ -33,7 +35,7 @@ export async function requestAccessToken(url, options) {
|
|
|
33
35
|
export function handleAccessTokenErrorResponse(event, oauthProvider, oauthError, onError) {
|
|
34
36
|
const message = `${upperFirst(oauthProvider)} login failed: ${oauthError.error_description || oauthError.error?.message || oauthError.error || "Unknown error"}`;
|
|
35
37
|
const error = createError({
|
|
36
|
-
|
|
38
|
+
status: 401,
|
|
37
39
|
message,
|
|
38
40
|
data: oauthError
|
|
39
41
|
});
|
|
@@ -43,7 +45,7 @@ export function handleAccessTokenErrorResponse(event, oauthProvider, oauthError,
|
|
|
43
45
|
export function handleMissingConfiguration(event, provider, missingKeys, onError) {
|
|
44
46
|
const environmentVariables = missingKeys.map((key) => `NUXT_OAUTH_${provider.toUpperCase()}_${snakeCase(key).toUpperCase()}`);
|
|
45
47
|
const error = createError({
|
|
46
|
-
|
|
48
|
+
status: 500,
|
|
47
49
|
message: `Missing ${environmentVariables.join(" or ")} env ${missingKeys.length > 1 ? "variables" : "variable"}.`
|
|
48
50
|
});
|
|
49
51
|
if (!onError) throw error;
|
|
@@ -52,7 +54,7 @@ export function handleMissingConfiguration(event, provider, missingKeys, onError
|
|
|
52
54
|
export function handleInvalidState(event, provider, onError) {
|
|
53
55
|
const message = `${upperFirst(provider)} login failed: state mismatch`;
|
|
54
56
|
const error = createError({
|
|
55
|
-
|
|
57
|
+
status: 500,
|
|
56
58
|
message
|
|
57
59
|
});
|
|
58
60
|
if (!onError) throw error;
|
|
@@ -86,7 +88,7 @@ export async function handlePkceVerifier(event) {
|
|
|
86
88
|
const verifier2 = encodeBase64Url(getRandomBytes());
|
|
87
89
|
setCookie(event, "nuxt-auth-pkce", verifier2, {
|
|
88
90
|
httpOnly: true,
|
|
89
|
-
secure:
|
|
91
|
+
secure: isOAuthCookieSecure(),
|
|
90
92
|
sameSite: "lax",
|
|
91
93
|
maxAge: OAUTH_COOKIE_MAX_AGE,
|
|
92
94
|
path: "/"
|
|
@@ -116,7 +118,7 @@ export async function handleState(event, options = {}) {
|
|
|
116
118
|
const sameSite = options.sameSite || "lax";
|
|
117
119
|
setCookie(event, "nuxt-auth-state", state, {
|
|
118
120
|
httpOnly: true,
|
|
119
|
-
secure: sameSite === "none" ||
|
|
121
|
+
secure: sameSite === "none" || isOAuthCookieSecure(),
|
|
120
122
|
sameSite,
|
|
121
123
|
maxAge: OAUTH_COOKIE_MAX_AGE,
|
|
122
124
|
path: "/"
|
|
@@ -136,7 +138,7 @@ export function handleNonce(event) {
|
|
|
136
138
|
const nonce = encodeBase64Url(getRandomBytes());
|
|
137
139
|
setCookie(event, "nuxt-auth-nonce", nonce, {
|
|
138
140
|
httpOnly: true,
|
|
139
|
-
secure:
|
|
141
|
+
secure: isOAuthCookieSecure(),
|
|
140
142
|
sameSite: "lax",
|
|
141
143
|
maxAge: OAUTH_COOKIE_MAX_AGE,
|
|
142
144
|
path: "/"
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { EventHandler } from '
|
|
1
|
+
import type { EventHandler } from 'nuxt/server';
|
|
2
2
|
import type { WebAuthnAuthenticateEventHandlerOptions, WebAuthnCredential } from '#auth-utils';
|
|
3
3
|
export declare function defineWebAuthnAuthenticateEventHandler<T extends WebAuthnCredential>({ storeChallenge, getChallenge, getCredential, allowCredentials, getOptions, onSuccess, onError, }: WebAuthnAuthenticateEventHandlerOptions<T>): EventHandler;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineEventHandler, createError, getRequestURL, isNuxtError, readBody, useRuntimeConfig } from "nuxt/server";
|
|
2
2
|
import { generateAuthenticationOptions, verifyAuthenticationResponse } from "@simplewebauthn/server";
|
|
3
3
|
import defu from "defu";
|
|
4
4
|
import { getRandomValues } from "uncrypto";
|
|
5
5
|
import { base64URLStringToBuffer, bufferToBase64URLString } from "@simplewebauthn/browser";
|
|
6
|
-
import { useRuntimeConfig } from "#imports";
|
|
7
6
|
export function defineWebAuthnAuthenticateEventHandler({
|
|
8
7
|
storeChallenge,
|
|
9
8
|
getChallenge,
|
|
@@ -13,10 +12,10 @@ export function defineWebAuthnAuthenticateEventHandler({
|
|
|
13
12
|
onSuccess,
|
|
14
13
|
onError
|
|
15
14
|
}) {
|
|
16
|
-
return
|
|
17
|
-
const url = getRequestURL(event);
|
|
15
|
+
return defineEventHandler(async (event) => {
|
|
16
|
+
const url = getRequestURL(event, { xForwardedProto: true });
|
|
18
17
|
const body = await readBody(event);
|
|
19
|
-
const _config = defu(await getOptions?.(event, body) ?? {}, useRuntimeConfig(
|
|
18
|
+
const _config = defu(await getOptions?.(event, body) ?? {}, useRuntimeConfig().webauthn.authenticate, {
|
|
20
19
|
rpID: url.hostname
|
|
21
20
|
});
|
|
22
21
|
if (!storeChallenge) {
|
|
@@ -38,7 +37,7 @@ export function defineWebAuthnAuthenticateEventHandler({
|
|
|
38
37
|
};
|
|
39
38
|
}
|
|
40
39
|
if (!body.attemptId)
|
|
41
|
-
throw createError({
|
|
40
|
+
throw createError({ status: 400 });
|
|
42
41
|
let expectedChallenge = "";
|
|
43
42
|
if (getChallenge) {
|
|
44
43
|
expectedChallenge = await getChallenge(event, body.attemptId);
|
|
@@ -59,7 +58,7 @@ export function defineWebAuthnAuthenticateEventHandler({
|
|
|
59
58
|
}
|
|
60
59
|
});
|
|
61
60
|
if (!verification.verified)
|
|
62
|
-
throw createError({
|
|
61
|
+
throw createError({ status: 400, message: "Failed to verify registration response" });
|
|
63
62
|
await onSuccess(event, {
|
|
64
63
|
credential,
|
|
65
64
|
authenticationInfo: verification.authenticationInfo
|
|
@@ -67,9 +66,9 @@ export function defineWebAuthnAuthenticateEventHandler({
|
|
|
67
66
|
return verification;
|
|
68
67
|
} catch (error) {
|
|
69
68
|
if (!onError) throw error;
|
|
70
|
-
if (error
|
|
69
|
+
if (isNuxtError(error))
|
|
71
70
|
return onError(event, error);
|
|
72
|
-
return onError(event, createError({
|
|
71
|
+
return onError(event, createError({ status: 500, message: "Failed to authenticate credential" }));
|
|
73
72
|
}
|
|
74
73
|
});
|
|
75
74
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { EventHandler, RequestEvent } from 'nuxt/server';
|
|
2
2
|
import type { ValidateUserFunction } from '../../../types/webauthn.js';
|
|
3
3
|
import type { WebAuthnUser, WebAuthnRegisterEventHandlerOptions } from '#auth-utils';
|
|
4
4
|
export declare function defineWebAuthnRegisterEventHandler<T extends WebAuthnUser>({ storeChallenge, getChallenge, getOptions, validateUser, excludeCredentials, onSuccess, onError, }: WebAuthnRegisterEventHandlerOptions<T>): EventHandler;
|
|
5
|
-
export declare function validateUserData<T>(userBody: WebAuthnUser, event:
|
|
5
|
+
export declare function validateUserData<T>(userBody: WebAuthnUser, event: RequestEvent, fn: ValidateUserFunction<T>): Promise<T>;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineEventHandler, createError, getRequestURL, isNuxtError, readBody, useRuntimeConfig } from "nuxt/server";
|
|
2
2
|
import { generateRegistrationOptions, verifyRegistrationResponse } from "@simplewebauthn/server";
|
|
3
3
|
import defu from "defu";
|
|
4
4
|
import { bufferToBase64URLString } from "@simplewebauthn/browser";
|
|
5
5
|
import { getRandomValues } from "uncrypto";
|
|
6
|
-
import { useRuntimeConfig } from "#imports";
|
|
7
6
|
export function defineWebAuthnRegisterEventHandler({
|
|
8
7
|
storeChallenge,
|
|
9
8
|
getChallenge,
|
|
@@ -13,19 +12,19 @@ export function defineWebAuthnRegisterEventHandler({
|
|
|
13
12
|
onSuccess,
|
|
14
13
|
onError
|
|
15
14
|
}) {
|
|
16
|
-
return
|
|
17
|
-
const url = getRequestURL(event);
|
|
15
|
+
return defineEventHandler(async (event) => {
|
|
16
|
+
const url = getRequestURL(event, { xForwardedProto: true });
|
|
18
17
|
const body = await readBody(event);
|
|
19
18
|
if (body.verify === void 0 || !body.user?.userName)
|
|
20
19
|
throw createError({
|
|
21
20
|
message: "Invalid request, missing userName or verify property",
|
|
22
|
-
|
|
21
|
+
status: 400
|
|
23
22
|
});
|
|
24
23
|
let user = body.user;
|
|
25
24
|
if (validateUser) {
|
|
26
25
|
user = await validateUserData(body.user, event, validateUser);
|
|
27
26
|
}
|
|
28
|
-
const _config = defu(await getOptions?.(event, body) ?? {}, useRuntimeConfig(
|
|
27
|
+
const _config = defu(await getOptions?.(event, body) ?? {}, useRuntimeConfig().webauthn.register, {
|
|
29
28
|
rpID: url.hostname,
|
|
30
29
|
rpName: url.hostname,
|
|
31
30
|
userName: user.userName,
|
|
@@ -55,7 +54,7 @@ export function defineWebAuthnRegisterEventHandler({
|
|
|
55
54
|
if (!body.attemptId) {
|
|
56
55
|
throw createError({
|
|
57
56
|
message: "Invalid request, missing attemptId",
|
|
58
|
-
|
|
57
|
+
status: 400
|
|
59
58
|
});
|
|
60
59
|
}
|
|
61
60
|
let expectedChallenge = "";
|
|
@@ -74,7 +73,7 @@ export function defineWebAuthnRegisterEventHandler({
|
|
|
74
73
|
if (!verification.verified) {
|
|
75
74
|
throw createError({
|
|
76
75
|
message: "Failed to verify registration response",
|
|
77
|
-
|
|
76
|
+
status: 400
|
|
78
77
|
});
|
|
79
78
|
}
|
|
80
79
|
await onSuccess(event, {
|
|
@@ -92,9 +91,9 @@ export function defineWebAuthnRegisterEventHandler({
|
|
|
92
91
|
return verification;
|
|
93
92
|
} catch (error) {
|
|
94
93
|
if (!onError) throw error;
|
|
95
|
-
if (error
|
|
94
|
+
if (isNuxtError(error))
|
|
96
95
|
return onError(event, error);
|
|
97
|
-
return onError(event, createError({
|
|
96
|
+
return onError(event, createError({ status: 500, message: "Failed to register credential" }));
|
|
98
97
|
}
|
|
99
98
|
});
|
|
100
99
|
}
|
|
@@ -114,7 +113,7 @@ export async function validateUserData(userBody, event, fn) {
|
|
|
114
113
|
}
|
|
115
114
|
function createUserValidationError(validateError) {
|
|
116
115
|
throw createError({
|
|
117
|
-
status: validateError?.statusCode || 400,
|
|
116
|
+
status: validateError?.status || validateError?.statusCode || 400,
|
|
118
117
|
message: validateError?.message || "User Validation Error",
|
|
119
118
|
data: validateError
|
|
120
119
|
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { useRuntimeConfig, useServerHooks } from "nuxt/server";
|
|
2
|
+
export default () => {
|
|
3
|
+
useServerHooks().hook("render:html", (html) => {
|
|
4
|
+
const oauth = useRuntimeConfig().oauth;
|
|
5
|
+
if (!oauth?.facebook?.clientId && !oauth?.instagram?.clientId) return;
|
|
6
|
+
html.head.unshift('<script>if(window.location.hash==="#_=_"){history.replaceState?history.replaceState(null,null,window.location.href.split("#")[0]):window.location.hash=""}<\/script>');
|
|
7
|
+
});
|
|
8
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const _default: import("
|
|
1
|
+
declare const _default: import("nuxt/server").EventHandler<{
|
|
2
2
|
redirect_uris: [string, ...string[]];
|
|
3
3
|
scope?: string | undefined;
|
|
4
4
|
response_types?: ["code" | "none" | "token" | "code id_token token" | "code id_token" | "code token" | "id_token token" | "id_token", ...("code" | "none" | "token" | "code id_token token" | "code id_token" | "code token" | "id_token token" | "id_token")[]] | undefined;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { defineEventHandler,
|
|
1
|
+
import { createError, defineEventHandler, getRequestURL, useRuntimeConfig } from "nuxt/server";
|
|
2
|
+
import { withoutBase } from "ufo";
|
|
2
3
|
import { getAtprotoClientMetadata } from "../../utils/atproto.js";
|
|
3
4
|
import { atprotoProviders, getClientMetadataFilename } from "../../../utils/atproto.js";
|
|
4
|
-
import { useRuntimeConfig } from "#imports";
|
|
5
5
|
export default defineEventHandler((event) => {
|
|
6
|
-
const
|
|
7
|
-
const
|
|
6
|
+
const runtimeConfig = useRuntimeConfig();
|
|
7
|
+
const path = withoutBase(getRequestURL(event).pathname, runtimeConfig.app.baseURL).slice(1);
|
|
8
8
|
for (const provider of atprotoProviders) {
|
|
9
9
|
const config = runtimeConfig.oauth[provider];
|
|
10
10
|
if (getClientMetadataFilename(provider, config) === path) {
|
|
@@ -12,7 +12,7 @@ export default defineEventHandler((event) => {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
throw createError({
|
|
15
|
-
|
|
15
|
+
status: 404,
|
|
16
16
|
message: "Provider not found"
|
|
17
17
|
});
|
|
18
18
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RequestEvent } from 'nuxt/server';
|
|
2
2
|
import type { OAuthClientMetadataInput } from '@atproto/oauth-client-node';
|
|
3
3
|
import type { OAuthBlueskyConfig } from '../lib/atproto/bluesky.js';
|
|
4
4
|
import type { ATProtoProvider, OAuthConfig } from '#auth-utils';
|
|
5
|
-
export declare function getAtprotoClientMetadata(event:
|
|
5
|
+
export declare function getAtprotoClientMetadata(event: RequestEvent, provider: ATProtoProvider, config?: OAuthConfig<OAuthBlueskyConfig>['config']): OAuthClientMetadataInput;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { getRequestURL } from "
|
|
1
|
+
import { getRequestURL, useRuntimeConfig } from "nuxt/server";
|
|
2
2
|
import { getOAuthRedirectURL } from "../lib/utils.js";
|
|
3
3
|
import { getClientMetadataFilename } from "../../utils/atproto.js";
|
|
4
|
-
import { useRuntimeConfig } from "#imports";
|
|
5
4
|
export function getAtprotoClientMetadata(event, provider, config) {
|
|
6
|
-
const providerRuntimeConfig = useRuntimeConfig(
|
|
5
|
+
const providerRuntimeConfig = useRuntimeConfig().oauth[provider];
|
|
7
6
|
const scopes = [.../* @__PURE__ */ new Set(["atproto", ...config?.scope ?? [], ...providerRuntimeConfig.scope ?? []])];
|
|
8
7
|
const scope = scopes.join(" ");
|
|
9
8
|
const grantTypes = [.../* @__PURE__ */ new Set(["authorization_code", ...providerRuntimeConfig.grantTypes ?? []])];
|
|
10
|
-
const requestURL = getRequestURL(event);
|
|
9
|
+
const requestURL = getRequestURL(event, { xForwardedProto: true });
|
|
11
10
|
const baseUrl = `${requestURL.protocol}//${requestURL.host}`;
|
|
12
11
|
const redirectURL = new URL(
|
|
13
12
|
config?.redirectUrl && baseUrl + config.redirectUrl || providerRuntimeConfig.redirectUris[0] && baseUrl + providerRuntimeConfig.redirectUris[0] || getOAuthRedirectURL(event)
|
|
@@ -1,56 +1,65 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { useSession } from 'h3';
|
|
1
|
+
import type { RequestEvent, SessionConfig } from 'nuxt/server';
|
|
3
2
|
import type { OmitWithIndexSignature } from '../../types/utils.js';
|
|
4
|
-
import type { SessionEvent, UserSession, UserSessionRequired } from '#auth-utils';
|
|
5
|
-
|
|
3
|
+
import type { LegacyH3Event, SessionEvent, UserSession, UserSessionRequired } from '#auth-utils';
|
|
4
|
+
export { USER_SESSION_COOKIE_NAME } from '../lib/session.js';
|
|
5
|
+
type UserSessionData = OmitWithIndexSignature<UserSession, 'id'>;
|
|
6
|
+
/**
|
|
7
|
+
* Session options, the session is always sealed with a secret derived from `appSecret`.
|
|
8
|
+
*/
|
|
9
|
+
type UserSessionConfig = Omit<SessionConfig, 'password'>;
|
|
10
|
+
/**
|
|
11
|
+
* An event the session can be written with, WebSocket peers and upgrade requests are read-only.
|
|
12
|
+
*/
|
|
13
|
+
type WritableSessionEvent = RequestEvent | LegacyH3Event;
|
|
6
14
|
export interface SessionHooks {
|
|
7
15
|
/**
|
|
8
16
|
* Called when fetching the session from the API
|
|
9
17
|
* - Add extra properties to the session
|
|
10
18
|
* - Throw an error if the session could not be verified (with a database for example)
|
|
11
19
|
*/
|
|
12
|
-
fetch: (session: UserSession, event:
|
|
20
|
+
fetch: (session: UserSession, event: RequestEvent) => void | Promise<void>;
|
|
13
21
|
/**
|
|
14
22
|
* Called before clearing the session
|
|
15
23
|
*/
|
|
16
|
-
clear: (session: UserSession, event:
|
|
24
|
+
clear: (session: UserSession, event: RequestEvent) => void | Promise<void>;
|
|
17
25
|
}
|
|
18
26
|
export declare const sessionHooks: import("hookable").Hookable<SessionHooks, import("hookable").HookKeys<SessionHooks>>;
|
|
19
27
|
/**
|
|
20
28
|
* Get the user session from the current request
|
|
21
|
-
* @param event The
|
|
29
|
+
* @param event The request event
|
|
22
30
|
* @returns The user session
|
|
23
31
|
*/
|
|
24
|
-
export declare function getUserSession(event:
|
|
32
|
+
export declare function getUserSession(event: SessionEvent): Promise<UserSession>;
|
|
25
33
|
/**
|
|
26
34
|
* Set a user session
|
|
27
|
-
* @param event The
|
|
35
|
+
* @param event The request event
|
|
28
36
|
* @param data User session data, please only store public information since it can be decoded with API calls
|
|
29
37
|
* @see https://github.com/atinux/nuxt-auth-utils
|
|
30
38
|
*/
|
|
31
|
-
export declare function setUserSession(event:
|
|
39
|
+
export declare function setUserSession(event: WritableSessionEvent, data: UserSessionData, config?: UserSessionConfig): Promise<UserSession>;
|
|
32
40
|
/**
|
|
33
41
|
* Replace a user session
|
|
34
|
-
* @param event The
|
|
42
|
+
* @param event The request event
|
|
35
43
|
* @param data User session data, please only store public information since it can be decoded with API calls
|
|
36
44
|
*/
|
|
37
|
-
export declare function replaceUserSession(event:
|
|
45
|
+
export declare function replaceUserSession(event: WritableSessionEvent, data: UserSessionData, config?: UserSessionConfig): Promise<UserSession>;
|
|
38
46
|
/**
|
|
39
47
|
* Clear the user session and removing the session cookie
|
|
40
|
-
* @param event The
|
|
48
|
+
* @param event The request event
|
|
41
49
|
* @returns true if the session was cleared
|
|
42
50
|
*/
|
|
43
|
-
export declare function clearUserSession(event:
|
|
51
|
+
export declare function clearUserSession(event: WritableSessionEvent, config?: UserSessionConfig): Promise<boolean>;
|
|
44
52
|
/**
|
|
45
53
|
* Require a user session, throw a 401 error if the user is not logged in
|
|
46
54
|
* @param event
|
|
47
55
|
* @param opts Options to customize the error message and status code
|
|
48
|
-
* @param opts.
|
|
56
|
+
* @param opts.status The status code to use for the error (defaults to 401)
|
|
57
|
+
* @param opts.statusCode Deprecated, use `status`
|
|
49
58
|
* @param opts.message The message to use for the error (defaults to Unauthorized)
|
|
50
59
|
* @see https://github.com/atinux/nuxt-auth-utils
|
|
51
60
|
*/
|
|
52
|
-
export declare function requireUserSession(event:
|
|
61
|
+
export declare function requireUserSession(event: SessionEvent, opts?: {
|
|
62
|
+
status?: number;
|
|
53
63
|
statusCode?: number;
|
|
54
64
|
message?: string;
|
|
55
65
|
}): Promise<UserSessionRequired>;
|
|
56
|
-
export {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createError, defineEventHandler, useSession } from "nuxt/server";
|
|
2
2
|
import { defu } from "defu";
|
|
3
3
|
import { createHooks } from "hookable";
|
|
4
|
-
import {
|
|
4
|
+
import { resolveSessionConfig } from "../lib/session.js";
|
|
5
|
+
export { USER_SESSION_COOKIE_NAME } from "../lib/session.js";
|
|
5
6
|
export const sessionHooks = createHooks();
|
|
6
7
|
export async function getUserSession(event) {
|
|
7
8
|
const session = await _useSession(event);
|
|
@@ -11,53 +12,72 @@ export async function getUserSession(event) {
|
|
|
11
12
|
};
|
|
12
13
|
}
|
|
13
14
|
export async function setUserSession(event, data, config) {
|
|
14
|
-
const session = await _useSession(event, config);
|
|
15
|
-
await session.update(defu(data, session.data));
|
|
15
|
+
const session = await _useSession(event, config, true);
|
|
16
|
+
await session.update(defu(toJSONData(data), session.data));
|
|
16
17
|
return session.data;
|
|
17
18
|
}
|
|
18
19
|
export async function replaceUserSession(event, data, config) {
|
|
19
|
-
const session = await _useSession(event, config);
|
|
20
|
-
await session.clear();
|
|
20
|
+
const session = await _useSession(event, config, true);
|
|
21
21
|
await session.update((oldData) => {
|
|
22
22
|
for (const key of Object.keys(oldData)) {
|
|
23
23
|
Reflect.deleteProperty(oldData, key);
|
|
24
24
|
}
|
|
25
|
-
return data;
|
|
25
|
+
return toJSONData(data);
|
|
26
26
|
});
|
|
27
27
|
return session.data;
|
|
28
28
|
}
|
|
29
29
|
export async function clearUserSession(event, config) {
|
|
30
|
-
const session = await _useSession(event, config);
|
|
31
|
-
await sessionHooks.callHookParallel("clear", session.data, event);
|
|
30
|
+
const session = await _useSession(event, config, true);
|
|
31
|
+
await sessionHooks.callHookParallel("clear", session.data, toSessionEvent(event).event);
|
|
32
32
|
await session.clear();
|
|
33
33
|
return true;
|
|
34
34
|
}
|
|
35
35
|
export async function requireUserSession(event, opts = {}) {
|
|
36
36
|
const userSession = await getUserSession(event);
|
|
37
37
|
if (!userSession.user) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
});
|
|
43
|
-
} else {
|
|
44
|
-
throw new Response(opts.message || "Unauthorized", {
|
|
45
|
-
status: opts.statusCode || 401
|
|
46
|
-
});
|
|
38
|
+
const status = opts.status || opts.statusCode || 401;
|
|
39
|
+
const message = opts.message || "Unauthorized";
|
|
40
|
+
if (toSessionEvent(event).webSocket) {
|
|
41
|
+
throw new Response(message, { status });
|
|
47
42
|
}
|
|
43
|
+
throw createError({ status, message });
|
|
48
44
|
}
|
|
49
45
|
return userSession;
|
|
50
46
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
47
|
+
async function _useSession(event, config = {}, write = false) {
|
|
48
|
+
const { event: sessionEvent, webSocket } = toSessionEvent(event);
|
|
49
|
+
if (write && webSocket) {
|
|
50
|
+
throw createError({
|
|
51
|
+
status: 500,
|
|
52
|
+
message: "[nuxt-auth-utils] The user session is read-only in WebSocket handlers: `setUserSession`, `replaceUserSession` and `clearUserSession` need the event of an HTTP request."
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return useSession(sessionEvent, resolveSessionConfig(config));
|
|
56
|
+
}
|
|
57
|
+
function toJSONData(data) {
|
|
58
|
+
return JSON.parse(JSON.stringify(data));
|
|
59
|
+
}
|
|
60
|
+
const toPortableEvent = defineEventHandler((event) => event);
|
|
61
|
+
function toSessionEvent(event) {
|
|
62
|
+
const input = event;
|
|
63
|
+
if (input["~portable"] === true || typeof input.res?.headers?.append === "function") {
|
|
64
|
+
return { event: input, webSocket: false };
|
|
65
|
+
}
|
|
66
|
+
if (input.node?.req) {
|
|
67
|
+
return { event: toPortableEvent(input), webSocket: false };
|
|
60
68
|
}
|
|
61
|
-
const
|
|
62
|
-
|
|
69
|
+
const headers = input.request?.headers || input.headers;
|
|
70
|
+
if (headers && typeof headers.get === "function") {
|
|
71
|
+
return {
|
|
72
|
+
event: {
|
|
73
|
+
req: { headers },
|
|
74
|
+
res: { status: 200, statusText: "", headers: new Headers() }
|
|
75
|
+
},
|
|
76
|
+
webSocket: true
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
throw createError({
|
|
80
|
+
status: 500,
|
|
81
|
+
message: "[nuxt-auth-utils] The session utils need a request event, a WebSocket peer or a WebSocket upgrade request."
|
|
82
|
+
});
|
|
63
83
|
}
|
|
@@ -1,4 +1,28 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { RequestEvent } from 'nuxt/server';
|
|
2
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
3
3
|
export type { RequestEvent };
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* The h3 v1 event a handler defined with `defineEventHandler` from `h3` (or its auto-import) receives on Nuxt 4.
|
|
6
|
+
* @deprecated Use `defineEventHandler` from `nuxt/server`. Removed with Nuxt 5 support.
|
|
7
|
+
*/
|
|
8
|
+
export interface LegacyH3Event {
|
|
9
|
+
node: {
|
|
10
|
+
req: IncomingMessage;
|
|
11
|
+
res: ServerResponse;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A WebSocket peer or upgrade request, from `defineWebSocketHandler`.
|
|
16
|
+
* The session is read-only: changes are not sent back to the client.
|
|
17
|
+
*/
|
|
18
|
+
export type WebSocketSessionEvent = {
|
|
19
|
+
request: {
|
|
20
|
+
headers: Headers;
|
|
21
|
+
};
|
|
22
|
+
} | {
|
|
23
|
+
headers: Headers;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Any event the session utils accept.
|
|
27
|
+
*/
|
|
28
|
+
export type SessionEvent = RequestEvent | LegacyH3Event | WebSocketSessionEvent;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export type { User, UserSession, UserSessionRequired, UserSessionComposable, SecureSessionData } from './session.js';
|
|
2
|
-
export type { RequestEvent, SessionEvent } from './event.js';
|
|
2
|
+
export type { RequestEvent, SessionEvent, LegacyH3Event, WebSocketSessionEvent } from './event.js';
|
|
3
3
|
export type { OAuthConfig, OAuthProvider, ATProtoProvider, OnError } from './oauth-config.js';
|
|
4
4
|
export type { WebAuthnCredential, WebAuthnRegisterEventHandlerOptions, WebAuthnAuthenticateEventHandlerOptions, WebAuthnComposable, WebAuthnUser, } from './webauthn.js';
|