nuxt-auth-utils 0.6.0 → 0.6.2
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 +95 -3
- package/dist/module.d.mts +36 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +8 -5
- package/dist/runtime/server/api/session.get.d.ts +1 -1
- package/dist/runtime/server/api/session.get.js +5 -1
- package/dist/runtime/server/lib/atproto/bluesky.js +14 -1
- package/dist/runtime/server/lib/oauth/heroku.js +1 -1
- package/dist/runtime/server/lib/oauth/hubspot.js +25 -4
- package/dist/runtime/server/lib/oauth/mastodon.js +2 -2
- package/dist/runtime/server/lib/oauth/salesforce.js +1 -1
- package/dist/runtime/server/lib/oauth/slack.js +1 -1
- package/dist/runtime/server/lib/oauth/steam.js +2 -2
- 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 +1 -0
- package/dist/runtime/server/lib/utils.js +8 -4
- package/dist/runtime/server/lib/webauthn/authenticate.js +5 -4
- package/dist/runtime/server/lib/webauthn/register.js +6 -5
- package/dist/runtime/server/utils/atproto.js +9 -8
- package/dist/runtime/server/utils/session.d.ts +1 -5
- package/dist/runtime/server/utils/session.js +4 -27
- package/dist/runtime/types/atproto.d.ts +6 -4
- package/dist/runtime/types/index.d.ts +1 -1
- package/dist/runtime/types/oauth-config.d.ts +14 -0
- package/dist/runtime/types/oauth-runtime-config.d.ts +119 -0
- package/dist/runtime/types/oauth-runtime-config.js +0 -0
- package/dist/runtime/types/session.d.ts +5 -2
- package/dist/runtime/utils/atproto.js +1 -2
- package/dist/types.d.mts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -410,6 +410,38 @@ export default defineNuxtConfig({
|
|
|
410
410
|
})
|
|
411
411
|
```
|
|
412
412
|
|
|
413
|
+
#### Callback path
|
|
414
|
+
|
|
415
|
+
The authorization server compares the redirect URI of the login request with the list published in the client metadata document. That document is served from `bluesky/client-metadata.json` under your app base URL (`app.baseURL`, `/` by default), so it cannot know where your login route lives.
|
|
416
|
+
|
|
417
|
+
The module fills in `/auth/<provider>`, the path its playground uses, under that base URL. The login route is served under the base URL too, so a deployment that sets `app.baseURL` publishes the path its login route answers on:
|
|
418
|
+
|
|
419
|
+
```text
|
|
420
|
+
app.baseURL: '/my-app/' -> redirect_uris: ["https://example.com/my-app/auth/bluesky"]
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
Set `redirectUris` when your login route lives elsewhere; until you do, the module logs a warning in development. Both requests then use it:
|
|
424
|
+
|
|
425
|
+
```ts
|
|
426
|
+
export default defineNuxtConfig({
|
|
427
|
+
runtimeConfig: {
|
|
428
|
+
oauth: {
|
|
429
|
+
bluesky: {
|
|
430
|
+
redirectUris: ['/login/bluesky']
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
})
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
Declare paths only, without the origin or the app base URL. The module prepends the base URL, like it does for `/auth/<provider>`, and leaves a path that already starts with it as is. It then prepends the origin of the request, because the URL origin must match the client ID:
|
|
438
|
+
|
|
439
|
+
```text
|
|
440
|
+
app.baseURL: '/my-app/', redirectUris: ['/login/bluesky'] -> redirect_uris: ["https://example.com/my-app/login/bluesky"]
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
The `redirectUrl` option of `defineOAuthBlueskyEventHandler()` applies to the login request alone, also under the base URL. The metadata document cannot see it, so use `redirectUris` for a custom callback path.
|
|
444
|
+
|
|
413
445
|
### WebAuthn (passkey)
|
|
414
446
|
|
|
415
447
|
WebAuthn (Web Authentication) is a web standard that enhances security by replacing passwords with passkeys using public key cryptography. Users can authenticate with biometric data (like fingerprints or facial recognition) or physical devices (like USB keys), reducing the risk of phishing and password breaches. This approach offers a more secure and user-friendly authentication method, supported by major browsers and platforms.
|
|
@@ -432,6 +464,37 @@ export default defineNuxtConfig({
|
|
|
432
464
|
})
|
|
433
465
|
```
|
|
434
466
|
|
|
467
|
+
#### Relying Party ID
|
|
468
|
+
|
|
469
|
+
A passkey is bound to the domain that created it. By default, a passkey created on `example.com` cannot be used on `app.example.com`. Set `rpID` to the registrable parent domain to share passkeys between a domain and its subdomains:
|
|
470
|
+
|
|
471
|
+
```ts
|
|
472
|
+
export default defineNuxtConfig({
|
|
473
|
+
auth: {
|
|
474
|
+
webAuthn: {
|
|
475
|
+
rpID: 'example.com', // passkeys created on `example.com` also work on `app.example.com`
|
|
476
|
+
rpName: 'My App' // shown by the authenticator when creating a passkey
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
})
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
Both options default to the hostname of the request. The module uses `rpID` to generate the options and to verify the response, and `rpName` only to label the passkey during registration. Both are read from `runtimeConfig`, which helps when one build serves several domains:
|
|
483
|
+
|
|
484
|
+
```ts
|
|
485
|
+
export default defineNuxtConfig({
|
|
486
|
+
runtimeConfig: {
|
|
487
|
+
webauthn: {
|
|
488
|
+
rpID: 'example.com'
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
})
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
`NUXT_WEBAUTHN_RP_ID` and `NUXT_WEBAUTHN_RP_NAME` override both at runtime.
|
|
495
|
+
|
|
496
|
+
When you define `getOptions`, the module calls it while generating the options and again while verifying the response. Return the same `rpID` in both calls, or verification fails with an `Unexpected RP ID hash` error. The expected origin is always the origin of the request, because that is what the browser sends back.
|
|
497
|
+
|
|
435
498
|
#### Example
|
|
436
499
|
|
|
437
500
|
In this example we will implement the very basic steps to register and authenticate a credential.
|
|
@@ -638,6 +701,7 @@ We leverage hooks to let you extend the session data with your own data or log w
|
|
|
638
701
|
export default () => {
|
|
639
702
|
// Called when the session is fetched during SSR for the Vue composable (/api/_auth/session)
|
|
640
703
|
// Or when we call useUserSession().fetch()
|
|
704
|
+
// Not called for a visitor without a session cookie: they get an empty session and no cookie is set
|
|
641
705
|
sessionHooks.hook('fetch', async (session, event) => {
|
|
642
706
|
// extend User Session by calling your database
|
|
643
707
|
// or
|
|
@@ -899,6 +963,34 @@ export default defineNuxtConfig({
|
|
|
899
963
|
|
|
900
964
|
The browser then sends the session cookie to every subdomain of `example.com`, and each application can decrypt it.
|
|
901
965
|
|
|
966
|
+
## Serve over plain HTTP
|
|
967
|
+
|
|
968
|
+
The OAuth event handlers set short-lived cookies for the state, nonce and PKCE verifier. They are `Secure` except in development. Browsers ignore `Secure` cookies set over plain HTTP, except on `localhost`, so logging in to a built app served over plain HTTP, for example in an end-to-end test stack, fails with `state mismatch`.
|
|
969
|
+
|
|
970
|
+
When `runtimeConfig.session.cookie.secure` is `false`, both the session cookie and these cookies are set without `Secure`. Nuxt only overrides declared options from environment variables, so declare it in your `nuxt.config.ts` to switch it at runtime:
|
|
971
|
+
|
|
972
|
+
```ts
|
|
973
|
+
export default defineNuxtConfig({
|
|
974
|
+
modules: ['nuxt-auth-utils'],
|
|
975
|
+
runtimeConfig: {
|
|
976
|
+
session: {
|
|
977
|
+
cookie: {
|
|
978
|
+
secure: true
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
})
|
|
983
|
+
```
|
|
984
|
+
|
|
985
|
+
Then start the built app with `NUXT_SESSION_COOKIE_SECURE=false`:
|
|
986
|
+
|
|
987
|
+
```bash
|
|
988
|
+
NUXT_SESSION_COOKIE_SECURE=false node .output/server/index.mjs
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
> [!WARNING]
|
|
992
|
+
> Only do this when the app is served over plain HTTP: without `Secure`, the cookies can be read or overwritten on the network. A proxy that terminates TLS is not a reason to disable it, as `Secure` applies to the browser's connection.
|
|
993
|
+
|
|
902
994
|
## More
|
|
903
995
|
|
|
904
996
|
- [nuxt-authorization](https://github.com/barbapapazes/nuxt-authorization): Authorization module for managing permissions inside a Nuxt app, compatible with `nuxt-auth-utils`
|
|
@@ -936,13 +1028,13 @@ Releases are automated with [uppt](https://github.com/danielroe/uppt): every pus
|
|
|
936
1028
|
|
|
937
1029
|
<!-- Badges -->
|
|
938
1030
|
[npm-version-src]: https://img.shields.io/npm/v/nuxt-auth-utils/latest.svg?style=flat&colorA=020420&colorB=00DC82
|
|
939
|
-
[npm-version-href]: https://
|
|
1031
|
+
[npm-version-href]: https://npmx.dev/package/nuxt-auth-utils
|
|
940
1032
|
|
|
941
1033
|
[npm-downloads-src]: https://img.shields.io/npm/dm/nuxt-auth-utils.svg?style=flat&colorA=020420&colorB=00DC82
|
|
942
|
-
[npm-downloads-href]: https://
|
|
1034
|
+
[npm-downloads-href]: https://npmx.dev/package-stats/nuxt-auth-utils/v/latest?granularity=monthly
|
|
943
1035
|
|
|
944
1036
|
[license-src]: https://img.shields.io/npm/l/nuxt-auth-utils.svg?style=flat&colorA=020420&colorB=00DC82
|
|
945
|
-
[license-href]: https://
|
|
1037
|
+
[license-href]: https://github.com/atinux/nuxt-auth-utils/blob/main/LICENSE
|
|
946
1038
|
|
|
947
1039
|
[nuxt-src]: https://img.shields.io/badge/Nuxt-020420?logo=nuxt.js
|
|
948
1040
|
[nuxt-href]: https://nuxt.com
|
package/dist/module.d.mts
CHANGED
|
@@ -1,12 +1,28 @@
|
|
|
1
1
|
import { ScryptConfig } from '@adonisjs/hash/types';
|
|
2
|
+
import { GenerateRegistrationOptionsOpts, GenerateAuthenticationOptionsOpts } from '@simplewebauthn/server';
|
|
2
3
|
import { SessionConfig } from 'nuxt/server';
|
|
4
|
+
import { OAuthRuntimeConfig } from '../dist/runtime/types/oauth-runtime-config.js';
|
|
3
5
|
|
|
6
|
+
interface WebAuthnOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Relying Party ID the passkeys are bound to.
|
|
9
|
+
*
|
|
10
|
+
* Set the registrable parent domain (such as `example.com`) to create and use passkeys across subdomains.
|
|
11
|
+
* @default the request hostname
|
|
12
|
+
*/
|
|
13
|
+
rpID?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Relying Party name shown by the authenticator when creating a passkey
|
|
16
|
+
* @default the request hostname
|
|
17
|
+
*/
|
|
18
|
+
rpName?: string;
|
|
19
|
+
}
|
|
4
20
|
interface ModuleOptions {
|
|
5
21
|
/**
|
|
6
22
|
* Enable WebAuthn (Passkeys)
|
|
7
23
|
* @default false
|
|
8
24
|
*/
|
|
9
|
-
webAuthn?: boolean;
|
|
25
|
+
webAuthn?: boolean | WebAuthnOptions;
|
|
10
26
|
/**
|
|
11
27
|
* Enable atproto OAuth (Bluesky, etc.)
|
|
12
28
|
* @default false
|
|
@@ -36,6 +52,24 @@ declare module 'nuxt/schema' {
|
|
|
36
52
|
* Session configuration, the session is sealed with a secret derived from `appSecret` (`NUXT_APP_SECRET`)
|
|
37
53
|
*/
|
|
38
54
|
session: Omit<SessionConfig, 'password'>;
|
|
55
|
+
/**
|
|
56
|
+
* WebAuthn settings, `rpID` and `rpName` are set from the `webAuthn` module option.
|
|
57
|
+
*
|
|
58
|
+
* Both can be overridden at runtime with `NUXT_WEBAUTHN_RP_ID` and `NUXT_WEBAUTHN_RP_NAME`.
|
|
59
|
+
*
|
|
60
|
+
* `register` and `authenticate` are merged into the options passed to
|
|
61
|
+
* `generateRegistrationOptions()` and `generateAuthenticationOptions()`.
|
|
62
|
+
*/
|
|
63
|
+
webauthn: {
|
|
64
|
+
rpID: string;
|
|
65
|
+
rpName: string;
|
|
66
|
+
register: Partial<GenerateRegistrationOptionsOpts>;
|
|
67
|
+
authenticate: Partial<GenerateAuthenticationOptionsOpts>;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* OAuth providers configuration, one key per provider (`github`, `google`, `keycloak`, ...).
|
|
71
|
+
*/
|
|
72
|
+
oauth: OAuthRuntimeConfig;
|
|
39
73
|
}
|
|
40
74
|
interface PublicRuntimeConfig {
|
|
41
75
|
auth: {
|
|
@@ -46,4 +80,4 @@ declare module 'nuxt/schema' {
|
|
|
46
80
|
declare const _default: any;
|
|
47
81
|
|
|
48
82
|
export { _default as default };
|
|
49
|
-
export type { ModuleOptions };
|
|
83
|
+
export type { ModuleOptions, WebAuthnOptions };
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -91,7 +91,10 @@ const module$1 = defineNuxtModule({
|
|
|
91
91
|
runtimeConfig.public.auth = defu(runtimeConfig.public.auth, {
|
|
92
92
|
loadStrategy: options.loadStrategy ?? "server-first"
|
|
93
93
|
});
|
|
94
|
+
const webAuthnOptions = typeof options.webAuthn === "object" ? options.webAuthn : void 0;
|
|
94
95
|
runtimeConfig.webauthn = defu(runtimeConfig.webauthn, {
|
|
96
|
+
rpID: webAuthnOptions?.rpID ?? "",
|
|
97
|
+
rpName: webAuthnOptions?.rpName ?? "",
|
|
95
98
|
register: {},
|
|
96
99
|
authenticate: {}
|
|
97
100
|
});
|
|
@@ -145,7 +148,7 @@ const module$1 = defineNuxtModule({
|
|
|
145
148
|
clientId: "",
|
|
146
149
|
clientSecret: "",
|
|
147
150
|
connectionId: "",
|
|
148
|
-
screenHint: "",
|
|
151
|
+
screenHint: "sign-in",
|
|
149
152
|
redirectURL: ""
|
|
150
153
|
});
|
|
151
154
|
runtimeConfig.oauth.microsoft = defu(runtimeConfig.oauth.microsoft, {
|
|
@@ -360,25 +363,25 @@ const module$1 = defineNuxtModule({
|
|
|
360
363
|
redirectURL: "",
|
|
361
364
|
baseURL: "",
|
|
362
365
|
userURL: "",
|
|
363
|
-
scope:
|
|
366
|
+
scope: []
|
|
364
367
|
});
|
|
365
368
|
runtimeConfig.oauth.slack = defu(runtimeConfig.oauth.slack, {
|
|
366
369
|
clientId: "",
|
|
367
370
|
clientSecret: "",
|
|
368
371
|
redirectURL: "",
|
|
369
|
-
scope:
|
|
372
|
+
scope: []
|
|
370
373
|
});
|
|
371
374
|
runtimeConfig.oauth.heroku = defu(runtimeConfig.oauth.heroku, {
|
|
372
375
|
clientId: "",
|
|
373
376
|
clientSecret: "",
|
|
374
377
|
redirectURL: "",
|
|
375
|
-
scope:
|
|
378
|
+
scope: []
|
|
376
379
|
});
|
|
377
380
|
runtimeConfig.oauth.roblox = defu(runtimeConfig.oauth.roblox, {
|
|
378
381
|
clientId: "",
|
|
379
382
|
clientSecret: "",
|
|
380
383
|
redirectURL: "",
|
|
381
|
-
scope:
|
|
384
|
+
scope: []
|
|
382
385
|
});
|
|
383
386
|
runtimeConfig.oauth.okta = defu(runtimeConfig.oauth.okta, {
|
|
384
387
|
clientId: "",
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("nuxt/server").EventHandler<Promise<
|
|
1
|
+
declare const _default: import("nuxt/server").EventHandler<Promise<any>>;
|
|
2
2
|
export default _default;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { defineEventHandler } from "nuxt/server";
|
|
1
|
+
import { defineEventHandler, getCookie } from "nuxt/server";
|
|
2
2
|
import { getUserSession, sessionHooks } from "../utils/session.js";
|
|
3
|
+
import { resolveSessionConfig } from "../lib/session.js";
|
|
3
4
|
export default defineEventHandler(async (event) => {
|
|
5
|
+
if (!getCookie(event, resolveSessionConfig().name)) {
|
|
6
|
+
return {};
|
|
7
|
+
}
|
|
4
8
|
const session = await getUserSession(event);
|
|
5
9
|
if (Object.keys(session).length > 0) {
|
|
6
10
|
await sessionHooks.callHookParallel("fetch", session, event);
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { defineEventHandler, createError, deleteCookie, getCookie, getQuery, sendRedirect, setCookie } from "nuxt/server";
|
|
1
|
+
import { defineEventHandler, createError, deleteCookie, getCookie, getQuery, getRequestURL, sendRedirect, setCookie, useRuntimeConfig } from "nuxt/server";
|
|
2
2
|
import { NodeOAuthClient, OAuthCallbackError, OAuthResolverError, OAuthResponseError } from "@atproto/oauth-client-node";
|
|
3
3
|
import { Agent } from "@atproto/api";
|
|
4
|
+
import { withoutBase } from "ufo";
|
|
4
5
|
import { getAtprotoClientMetadata } from "../../utils/atproto.js";
|
|
5
6
|
export function defineOAuthBlueskyEventHandler({ config, onSuccess, onError }) {
|
|
6
7
|
return defineEventHandler(async (event) => {
|
|
7
8
|
const clientMetadata = getAtprotoClientMetadata(event, "bluesky", config);
|
|
9
|
+
if (import.meta.dev && !config?.redirectUrl) warnUnpublishedLoginPath(event, clientMetadata.redirect_uris[0]);
|
|
8
10
|
const scopes = clientMetadata.scope?.split(" ") ?? [];
|
|
9
11
|
const sessionStore = new SessionStore();
|
|
10
12
|
const stateStore = new StateStore(event);
|
|
@@ -65,6 +67,17 @@ export function defineOAuthBlueskyEventHandler({ config, onSuccess, onError }) {
|
|
|
65
67
|
}
|
|
66
68
|
});
|
|
67
69
|
}
|
|
70
|
+
let warnedUnpublishedLoginPath = false;
|
|
71
|
+
function warnUnpublishedLoginPath(event, redirectUri) {
|
|
72
|
+
if (warnedUnpublishedLoginPath) return;
|
|
73
|
+
const runtimeConfig = useRuntimeConfig();
|
|
74
|
+
if (runtimeConfig.oauth.bluesky.redirectUris?.length) return;
|
|
75
|
+
const loginPath = getRequestURL(event).pathname;
|
|
76
|
+
const callbackPath = new URL(redirectUri).pathname;
|
|
77
|
+
if (loginPath === callbackPath) return;
|
|
78
|
+
warnedUnpublishedLoginPath = true;
|
|
79
|
+
console.warn(`[nuxt-auth-utils] The Bluesky login route is served from \`${loginPath}\`, but Bluesky redirects back to \`${callbackPath}\`. Set \`runtimeConfig.oauth.bluesky.redirectUris\` to \`['${withoutBase(loginPath, runtimeConfig.app.baseURL)}']\`.`);
|
|
80
|
+
}
|
|
68
81
|
export class StateStore {
|
|
69
82
|
constructor(event) {
|
|
70
83
|
this.event = event;
|
|
@@ -32,7 +32,7 @@ export function defineOAuthHerokuEventHandler({
|
|
|
32
32
|
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
33
33
|
const state = await handleState(event);
|
|
34
34
|
if (!query.code) {
|
|
35
|
-
config.scope = config.scope
|
|
35
|
+
config.scope = config.scope?.length ? config.scope : ["identity"];
|
|
36
36
|
return sendRedirect(
|
|
37
37
|
event,
|
|
38
38
|
withQuery(config.authorizationURL, {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { defineEventHandler, getQuery, sendRedirect, useRuntimeConfig } from "nuxt/server";
|
|
2
|
-
import { $fetch } from "ofetch";
|
|
1
|
+
import { createError, defineEventHandler, getQuery, sendRedirect, useRuntimeConfig } from "nuxt/server";
|
|
3
2
|
import { withQuery } from "ufo";
|
|
4
3
|
import defu from "defu";
|
|
5
4
|
import {
|
|
@@ -37,7 +36,7 @@ export function defineOAuthHubspotEventHandler({ config, onSuccess, onError }) {
|
|
|
37
36
|
return handleInvalidState(event, "hubspot", onError);
|
|
38
37
|
}
|
|
39
38
|
const tokens = await requestAccessToken(
|
|
40
|
-
"https://api.hubapi.com/oauth/
|
|
39
|
+
"https://api.hubapi.com/oauth/2026-09/token",
|
|
41
40
|
{
|
|
42
41
|
body: {
|
|
43
42
|
client_id: config.clientId,
|
|
@@ -51,7 +50,29 @@ export function defineOAuthHubspotEventHandler({ config, onSuccess, onError }) {
|
|
|
51
50
|
if (tokens.error) {
|
|
52
51
|
return handleAccessTokenErrorResponse(event, "hubspot", tokens, onError);
|
|
53
52
|
}
|
|
54
|
-
const info = await
|
|
53
|
+
const info = await requestAccessToken(
|
|
54
|
+
"https://api.hubapi.com/oauth/2026-09/token/introspect",
|
|
55
|
+
{
|
|
56
|
+
params: {
|
|
57
|
+
client_id: config.clientId,
|
|
58
|
+
client_secret: config.clientSecret,
|
|
59
|
+
token: tokens.access_token,
|
|
60
|
+
token_type_hint: "access_token"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
if (info.error) {
|
|
65
|
+
return handleAccessTokenErrorResponse(event, "hubspot", info, onError);
|
|
66
|
+
}
|
|
67
|
+
if (!info.active) {
|
|
68
|
+
const error = createError({
|
|
69
|
+
status: 401,
|
|
70
|
+
message: "Hubspot login failed: the access token is not active",
|
|
71
|
+
data: info
|
|
72
|
+
});
|
|
73
|
+
if (!onError) throw error;
|
|
74
|
+
return onError(event, error);
|
|
75
|
+
}
|
|
55
76
|
return onSuccess(event, {
|
|
56
77
|
user: {
|
|
57
78
|
id: info.user_id,
|
|
@@ -2,7 +2,7 @@ import { createError, defineEventHandler, deleteCookie, getCookie, getQuery, sen
|
|
|
2
2
|
import { withQuery } from "ufo";
|
|
3
3
|
import { defu } from "defu";
|
|
4
4
|
import { $fetch } from "ofetch";
|
|
5
|
-
import { getOAuthRedirectURL, handleAccessTokenErrorResponse, handleInvalidState, handleState,
|
|
5
|
+
import { getOAuthRedirectURL, handleAccessTokenErrorResponse, handleInvalidState, handleState, isOAuthCookieSecure, requestAccessToken } from "../utils.js";
|
|
6
6
|
const FLOW_COOKIE_NAME = "nuxt-auth-mastodon-flow";
|
|
7
7
|
const appRegistry = /* @__PURE__ */ new Map();
|
|
8
8
|
const pendingRegistrations = /* @__PURE__ */ new Map();
|
|
@@ -114,7 +114,7 @@ export function defineOAuthMastodonEventHandler({ config: userConfig, onSuccess,
|
|
|
114
114
|
const flowState2 = { instance: instance2, clientId: app.client_id, clientSecret: app.client_secret };
|
|
115
115
|
setCookie(event, FLOW_COOKIE_NAME, btoa(JSON.stringify(flowState2)), {
|
|
116
116
|
httpOnly: true,
|
|
117
|
-
secure:
|
|
117
|
+
secure: isOAuthCookieSecure(),
|
|
118
118
|
sameSite: "lax",
|
|
119
119
|
maxAge: 60 * 10,
|
|
120
120
|
path: "/"
|
|
@@ -33,7 +33,7 @@ export function defineOAuthSalesforceEventHandler({
|
|
|
33
33
|
const pkce = await handlePkceVerifier(event);
|
|
34
34
|
const state = await handleState(event);
|
|
35
35
|
if (!query.code) {
|
|
36
|
-
config.scope = config.scope
|
|
36
|
+
config.scope = config.scope?.length ? config.scope : ["id"];
|
|
37
37
|
return sendRedirect(
|
|
38
38
|
event,
|
|
39
39
|
withQuery(config.authorizationURL, {
|
|
@@ -32,7 +32,7 @@ export function defineOAuthSlackEventHandler({
|
|
|
32
32
|
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
33
33
|
const state = await handleState(event);
|
|
34
34
|
if (!query.code) {
|
|
35
|
-
config.scope = config.scope
|
|
35
|
+
config.scope = config.scope?.length ? config.scope : ["openid", "email", "profile"];
|
|
36
36
|
return sendRedirect(
|
|
37
37
|
event,
|
|
38
38
|
withQuery(config.authorizationURL, {
|
|
@@ -12,10 +12,10 @@ export function defineOAuthSteamEventHandler({ config, onSuccess, onError }) {
|
|
|
12
12
|
if (!config.apiKey) {
|
|
13
13
|
return handleMissingConfiguration(event, "steam", ["apiKey"], onError);
|
|
14
14
|
}
|
|
15
|
-
const url = getRequestURL(event);
|
|
15
|
+
const url = getRequestURL(event, { xForwardedProto: true });
|
|
16
16
|
const state = await handleState(event, { isCallback: Boolean(query["openid.claimed_id"]) });
|
|
17
17
|
if (!query["openid.claimed_id"]) {
|
|
18
|
-
const redirectURL = withQuery(config.redirectURL ||
|
|
18
|
+
const redirectURL = withQuery(config.redirectURL || url.href, { state });
|
|
19
19
|
const realm = url.port ? `${url.protocol}//${url.hostname}:${url.port}` : `${url.protocol}//${url.hostname}`;
|
|
20
20
|
const steamOpenIdParams = {
|
|
21
21
|
"openid.ns": "http://specs.openid.net/auth/2.0",
|
|
@@ -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
|
+
}
|
|
@@ -2,6 +2,7 @@ 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 isOAuthCookieSecure(): boolean;
|
|
5
6
|
export declare function getOAuthRedirectURL(event: RequestEvent): string;
|
|
6
7
|
/**
|
|
7
8
|
* Request an access token body.
|
|
@@ -3,10 +3,14 @@ import { $fetch, FetchError } from "ofetch";
|
|
|
3
3
|
import { snakeCase, upperFirst } from "scule";
|
|
4
4
|
import * as jose from "jose";
|
|
5
5
|
import { subtle, getRandomValues } from "uncrypto";
|
|
6
|
+
import { resolveSessionConfig } from "./session.js";
|
|
6
7
|
export const isDevelopment = process.env.NODE_ENV === "development";
|
|
8
|
+
export function isOAuthCookieSecure() {
|
|
9
|
+
return !isDevelopment && resolveSessionConfig().cookie?.secure !== false;
|
|
10
|
+
}
|
|
7
11
|
const OAUTH_COOKIE_MAX_AGE = 60 * 10;
|
|
8
12
|
export function getOAuthRedirectURL(event) {
|
|
9
|
-
const requestURL = getRequestURL(event);
|
|
13
|
+
const requestURL = getRequestURL(event, { xForwardedProto: true });
|
|
10
14
|
return `${requestURL.protocol}//${requestURL.host}${requestURL.pathname}`;
|
|
11
15
|
}
|
|
12
16
|
export async function requestAccessToken(url, options) {
|
|
@@ -84,7 +88,7 @@ export async function handlePkceVerifier(event) {
|
|
|
84
88
|
const verifier2 = encodeBase64Url(getRandomBytes());
|
|
85
89
|
setCookie(event, "nuxt-auth-pkce", verifier2, {
|
|
86
90
|
httpOnly: true,
|
|
87
|
-
secure:
|
|
91
|
+
secure: isOAuthCookieSecure(),
|
|
88
92
|
sameSite: "lax",
|
|
89
93
|
maxAge: OAUTH_COOKIE_MAX_AGE,
|
|
90
94
|
path: "/"
|
|
@@ -114,7 +118,7 @@ export async function handleState(event, options = {}) {
|
|
|
114
118
|
const sameSite = options.sameSite || "lax";
|
|
115
119
|
setCookie(event, "nuxt-auth-state", state, {
|
|
116
120
|
httpOnly: true,
|
|
117
|
-
secure: sameSite === "none" ||
|
|
121
|
+
secure: sameSite === "none" || isOAuthCookieSecure(),
|
|
118
122
|
sameSite,
|
|
119
123
|
maxAge: OAUTH_COOKIE_MAX_AGE,
|
|
120
124
|
path: "/"
|
|
@@ -134,7 +138,7 @@ export function handleNonce(event) {
|
|
|
134
138
|
const nonce = encodeBase64Url(getRandomBytes());
|
|
135
139
|
setCookie(event, "nuxt-auth-nonce", nonce, {
|
|
136
140
|
httpOnly: true,
|
|
137
|
-
secure:
|
|
141
|
+
secure: isOAuthCookieSecure(),
|
|
138
142
|
sameSite: "lax",
|
|
139
143
|
maxAge: OAUTH_COOKIE_MAX_AGE,
|
|
140
144
|
path: "/"
|
|
@@ -13,10 +13,11 @@ export function defineWebAuthnAuthenticateEventHandler({
|
|
|
13
13
|
onError
|
|
14
14
|
}) {
|
|
15
15
|
return defineEventHandler(async (event) => {
|
|
16
|
-
const url = getRequestURL(event);
|
|
16
|
+
const url = getRequestURL(event, { xForwardedProto: true });
|
|
17
17
|
const body = await readBody(event);
|
|
18
|
-
const
|
|
19
|
-
|
|
18
|
+
const runtimeConfig = useRuntimeConfig();
|
|
19
|
+
const _config = defu(await getOptions?.(event, body) ?? {}, runtimeConfig.webauthn.authenticate, {
|
|
20
|
+
rpID: runtimeConfig.webauthn.rpID || url.hostname
|
|
20
21
|
});
|
|
21
22
|
if (!storeChallenge) {
|
|
22
23
|
_config.challenge = "";
|
|
@@ -47,7 +48,7 @@ export function defineWebAuthnAuthenticateEventHandler({
|
|
|
47
48
|
response: body.response,
|
|
48
49
|
expectedChallenge,
|
|
49
50
|
expectedOrigin: url.origin,
|
|
50
|
-
expectedRPID:
|
|
51
|
+
expectedRPID: _config.rpID,
|
|
51
52
|
requireUserVerification: false,
|
|
52
53
|
// TODO: make configurable https://simplewebauthn.dev/docs/advanced/passkeys#verifyauthenticationresponse
|
|
53
54
|
credential: {
|
|
@@ -13,7 +13,7 @@ export function defineWebAuthnRegisterEventHandler({
|
|
|
13
13
|
onError
|
|
14
14
|
}) {
|
|
15
15
|
return defineEventHandler(async (event) => {
|
|
16
|
-
const url = getRequestURL(event);
|
|
16
|
+
const url = getRequestURL(event, { xForwardedProto: true });
|
|
17
17
|
const body = await readBody(event);
|
|
18
18
|
if (body.verify === void 0 || !body.user?.userName)
|
|
19
19
|
throw createError({
|
|
@@ -24,9 +24,10 @@ export function defineWebAuthnRegisterEventHandler({
|
|
|
24
24
|
if (validateUser) {
|
|
25
25
|
user = await validateUserData(body.user, event, validateUser);
|
|
26
26
|
}
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
const runtimeConfig = useRuntimeConfig();
|
|
28
|
+
const _config = defu(await getOptions?.(event, body) ?? {}, runtimeConfig.webauthn.register, {
|
|
29
|
+
rpID: runtimeConfig.webauthn.rpID || url.hostname,
|
|
30
|
+
rpName: runtimeConfig.webauthn.rpName || url.hostname,
|
|
30
31
|
userName: user.userName,
|
|
31
32
|
userDisplayName: user.displayName,
|
|
32
33
|
authenticatorSelection: {
|
|
@@ -65,7 +66,7 @@ export function defineWebAuthnRegisterEventHandler({
|
|
|
65
66
|
response: body.response,
|
|
66
67
|
expectedChallenge,
|
|
67
68
|
expectedOrigin: url.origin,
|
|
68
|
-
expectedRPID:
|
|
69
|
+
expectedRPID: _config.rpID,
|
|
69
70
|
requireUserVerification: false,
|
|
70
71
|
// TODO: make configurable https://simplewebauthn.dev/docs/advanced/passkeys#verifyregistrationresponse
|
|
71
72
|
supportedAlgorithmIDs: _config.supportedAlgorithmIDs
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import { getRequestURL, useRuntimeConfig } from "nuxt/server";
|
|
2
|
-
import {
|
|
2
|
+
import { withBase } from "ufo";
|
|
3
3
|
import { getClientMetadataFilename } from "../../utils/atproto.js";
|
|
4
4
|
export function getAtprotoClientMetadata(event, provider, config) {
|
|
5
|
-
const
|
|
5
|
+
const runtimeConfig = useRuntimeConfig();
|
|
6
|
+
const providerRuntimeConfig = runtimeConfig.oauth[provider];
|
|
6
7
|
const scopes = [.../* @__PURE__ */ new Set(["atproto", ...config?.scope ?? [], ...providerRuntimeConfig.scope ?? []])];
|
|
7
8
|
const scope = scopes.join(" ");
|
|
8
9
|
const grantTypes = [.../* @__PURE__ */ new Set(["authorization_code", ...providerRuntimeConfig.grantTypes ?? []])];
|
|
9
|
-
const requestURL = getRequestURL(event);
|
|
10
|
+
const requestURL = getRequestURL(event, { xForwardedProto: true });
|
|
10
11
|
const baseUrl = `${requestURL.protocol}//${requestURL.host}`;
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
);
|
|
12
|
+
const withAppBase = (path) => withBase(path, runtimeConfig.app.baseURL);
|
|
13
|
+
const callbackPaths = (providerRuntimeConfig.redirectUris?.length ? providerRuntimeConfig.redirectUris : [`/auth/${provider}`]).map(withAppBase);
|
|
14
|
+
const redirectURL = new URL(baseUrl + (config?.redirectUrl ? withAppBase(config.redirectUrl) : callbackPaths[0]));
|
|
14
15
|
const dev = import.meta.dev;
|
|
15
16
|
if (dev && redirectURL.hostname === "localhost") {
|
|
16
17
|
redirectURL.hostname = "127.0.0.1";
|
|
17
18
|
}
|
|
18
|
-
const redirectUris =
|
|
19
|
-
const clientId = dev ? `http://localhost?redirect_uri=${encodeURIComponent(redirectURL.toString())}&scope=${encodeURIComponent(scope)}` : `${baseUrl}
|
|
19
|
+
const redirectUris = callbackPaths.map((uri) => new URL(`${redirectURL.protocol}//${redirectURL.host}${uri}`).toString());
|
|
20
|
+
const clientId = dev ? `http://localhost?redirect_uri=${encodeURIComponent(redirectURL.toString())}&scope=${encodeURIComponent(scope)}` : `${baseUrl}${withBase(`/${getClientMetadataFilename(provider, providerRuntimeConfig)}`, runtimeConfig.app.baseURL)}`;
|
|
20
21
|
const clientMetadata = {
|
|
21
22
|
client_name: providerRuntimeConfig.clientName || void 0,
|
|
22
23
|
client_uri: providerRuntimeConfig.clientUri || void 0,
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import type { RequestEvent, SessionConfig } from 'nuxt/server';
|
|
2
2
|
import type { OmitWithIndexSignature } from '../../types/utils.js';
|
|
3
3
|
import type { LegacyH3Event, SessionEvent, UserSession, UserSessionRequired } from '#auth-utils';
|
|
4
|
-
|
|
5
|
-
* Name of the cookie the user session is sealed into.
|
|
6
|
-
*/
|
|
7
|
-
export declare const USER_SESSION_COOKIE_NAME = "nuxt-auth-session";
|
|
4
|
+
export { USER_SESSION_COOKIE_NAME } from '../lib/session.js';
|
|
8
5
|
type UserSessionData = OmitWithIndexSignature<UserSession, 'id'>;
|
|
9
6
|
/**
|
|
10
7
|
* Session options, the session is always sealed with a secret derived from `appSecret`.
|
|
@@ -66,4 +63,3 @@ export declare function requireUserSession(event: SessionEvent, opts?: {
|
|
|
66
63
|
statusCode?: number;
|
|
67
64
|
message?: string;
|
|
68
65
|
}): Promise<UserSessionRequired>;
|
|
69
|
-
export {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { createError, defineEventHandler,
|
|
1
|
+
import { createError, defineEventHandler, useSession } from "nuxt/server";
|
|
2
2
|
import { defu } from "defu";
|
|
3
3
|
import { createHooks } from "hookable";
|
|
4
|
-
|
|
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);
|
|
@@ -51,25 +52,11 @@ async function _useSession(event, config = {}, write = false) {
|
|
|
51
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."
|
|
52
53
|
});
|
|
53
54
|
}
|
|
54
|
-
|
|
55
|
-
warnLegacySessionPassword(runtimeConfig);
|
|
56
|
-
const { password: _password, ...finalConfig } = defu(config, runtimeConfig.session, { name: USER_SESSION_COOKIE_NAME });
|
|
57
|
-
if (finalConfig.name === APP_SESSION_COOKIE_NAME) {
|
|
58
|
-
warnAppSessionName();
|
|
59
|
-
finalConfig.name = USER_SESSION_COOKIE_NAME;
|
|
60
|
-
}
|
|
61
|
-
return useSession(sessionEvent, finalConfig);
|
|
55
|
+
return useSession(sessionEvent, resolveSessionConfig(config));
|
|
62
56
|
}
|
|
63
57
|
function toJSONData(data) {
|
|
64
58
|
return JSON.parse(JSON.stringify(data));
|
|
65
59
|
}
|
|
66
|
-
const APP_SESSION_COOKIE_NAME = "nuxt-session";
|
|
67
|
-
let warnedAppSessionName = false;
|
|
68
|
-
function warnAppSessionName() {
|
|
69
|
-
if (warnedAppSessionName) return;
|
|
70
|
-
warnedAppSessionName = true;
|
|
71
|
-
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.`);
|
|
72
|
-
}
|
|
73
60
|
const toPortableEvent = defineEventHandler((event) => event);
|
|
74
61
|
function toSessionEvent(event) {
|
|
75
62
|
const input = event;
|
|
@@ -94,13 +81,3 @@ function toSessionEvent(event) {
|
|
|
94
81
|
message: "[nuxt-auth-utils] The session utils need a request event, a WebSocket peer or a WebSocket upgrade request."
|
|
95
82
|
});
|
|
96
83
|
}
|
|
97
|
-
let checkedLegacySessionPassword = false;
|
|
98
|
-
function warnLegacySessionPassword(runtimeConfig) {
|
|
99
|
-
if (checkedLegacySessionPassword) return;
|
|
100
|
-
checkedLegacySessionPassword = true;
|
|
101
|
-
const envPrefix = runtimeConfig.nitro?.envPrefix || "NUXT_";
|
|
102
|
-
const envKey = `${envPrefix}SESSION_PASSWORD`;
|
|
103
|
-
if (globalThis.process?.env?.[envKey] || runtimeConfig.session?.password) {
|
|
104
|
-
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\`.`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { OAuthClientMetadata } from '@atproto/oauth-client-node';
|
|
2
|
-
export
|
|
2
|
+
export type AtprotoProviderClientMetadata = {
|
|
3
3
|
/**
|
|
4
4
|
* The name of the client metadata file. This is used to construct the client ID.
|
|
5
5
|
* @example 'client-metadata.json'
|
|
@@ -46,10 +46,12 @@ export interface AtprotoProviderClientMetadata {
|
|
|
46
46
|
*/
|
|
47
47
|
responseTypes?: OAuthClientMetadata['response_types'];
|
|
48
48
|
/**
|
|
49
|
-
* At least one redirect URI is required. The URL origin must match that of the `clientId`, so declare only the path.
|
|
49
|
+
* At least one redirect URI is required. The URL origin must match that of the `clientId`, so declare only the path. Paths are resolved under the app base URL.
|
|
50
|
+
*
|
|
51
|
+
* Empty by default, in which case `/auth/<provider>` under the app base URL is used.
|
|
50
52
|
* @example ['/auth/callback']
|
|
51
53
|
*/
|
|
52
|
-
redirectUris
|
|
54
|
+
redirectUris?: `/${string}`[];
|
|
53
55
|
/**
|
|
54
56
|
* The token endpoint authentication method. `none` is the default, and the only supported value at this time.
|
|
55
57
|
* @default 'none'
|
|
@@ -59,4 +61,4 @@ export interface AtprotoProviderClientMetadata {
|
|
|
59
61
|
* DPoP is mandatory for all clients, so this must be present and true.
|
|
60
62
|
*/
|
|
61
63
|
dpopBoundAccessTokens?: OAuthClientMetadata['dpop_bound_access_tokens'];
|
|
62
|
-
}
|
|
64
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export type { User, UserSession, UserSessionRequired, UserSessionComposable, SecureSessionData } from './session.js';
|
|
2
2
|
export type { RequestEvent, SessionEvent, LegacyH3Event, WebSocketSessionEvent } from './event.js';
|
|
3
|
-
export type { OAuthConfig, OAuthProvider, ATProtoProvider, OnError } from './oauth-config.js';
|
|
3
|
+
export type { OAuthConfig, OAuthProvider, ATProtoProvider, OAuthRuntimeConfigExtension, OnError } from './oauth-config.js';
|
|
4
4
|
export type { WebAuthnCredential, WebAuthnRegisterEventHandlerOptions, WebAuthnAuthenticateEventHandlerOptions, WebAuthnComposable, WebAuthnUser, } from './webauthn.js';
|
|
@@ -2,6 +2,20 @@ import type { NuxtError, RequestEvent } from 'nuxt/server';
|
|
|
2
2
|
export type ATProtoProvider = 'bluesky';
|
|
3
3
|
export type OAuthProvider = ATProtoProvider | 'atlassian' | 'auth0' | 'authentik' | 'azureb2c' | 'battledotnet' | 'cloudflare' | 'cognito' | 'discord' | 'dropbox' | 'facebook' | 'gitea' | 'github' | 'gitlab' | 'google' | 'hubspot' | 'instagram' | 'kick' | 'keycloak' | 'line' | 'linear' | 'linkedin' | 'mastodon' | 'microsoft' | 'paypal' | 'polar' | 'spotify' | 'seznam' | 'steam' | 'strava' | 'tiktok' | 'twitch' | 'vk' | 'wechat' | 'workos' | 'x' | 'xsuaa' | 'yandex' | 'zitadel' | 'apple' | 'livechat' | 'salesforce' | 'slack' | 'heroku' | 'roblox' | 'okta' | 'ory' | 'shopifyCustomer' | 'oidc' | 'osu' | 'riotgames' | 'box' | 'zendesk' | (string & {});
|
|
4
4
|
export type OnError = (event: RequestEvent, error: NuxtError) => unknown;
|
|
5
|
+
/**
|
|
6
|
+
* Augment this interface to type the runtime configuration of OAuth providers that are not
|
|
7
|
+
* shipped with the module, such as providers added by another module.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* declare module '#auth-utils' {
|
|
11
|
+
* interface OAuthRuntimeConfigExtension {
|
|
12
|
+
* myProvider: { clientId: string, clientSecret: string }
|
|
13
|
+
* }
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export interface OAuthRuntimeConfigExtension {
|
|
18
|
+
}
|
|
5
19
|
export interface OAuthConfig<TConfig, TResult = {
|
|
6
20
|
user: any;
|
|
7
21
|
tokens: any;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { AtprotoProviderClientMetadata } from './atproto.js';
|
|
2
|
+
import type { OAuthRuntimeConfigExtension } from './oauth-config.js';
|
|
3
|
+
import type { LiveChatConfig } from '../server/lib/oauth/livechat.js';
|
|
4
|
+
import type { OAuthAppleConfig } from '../server/lib/oauth/apple.js';
|
|
5
|
+
import type { OAuthAtlassianConfig } from '../server/lib/oauth/atlassian.js';
|
|
6
|
+
import type { OAuthAuth0Config } from '../server/lib/oauth/auth0.js';
|
|
7
|
+
import type { OAuthAuthentikConfig } from '../server/lib/oauth/authentik.js';
|
|
8
|
+
import type { OAuthAzureB2CConfig } from '../server/lib/oauth/azureb2c.js';
|
|
9
|
+
import type { OAuthBattledotnetConfig } from '../server/lib/oauth/battledotnet.js';
|
|
10
|
+
import type { OAuthBoxConfig } from '../server/lib/oauth/box.js';
|
|
11
|
+
import type { OAuthCloudflareConfig } from '../server/lib/oauth/cloudflare.js';
|
|
12
|
+
import type { OAuthCognitoConfig } from '../server/lib/oauth/cognito.js';
|
|
13
|
+
import type { OAuthDiscordConfig } from '../server/lib/oauth/discord.js';
|
|
14
|
+
import type { OAuthDropboxConfig } from '../server/lib/oauth/dropbox.js';
|
|
15
|
+
import type { OAuthFacebookConfig } from '../server/lib/oauth/facebook.js';
|
|
16
|
+
import type { OAuthGitHubConfig } from '../server/lib/oauth/github.js';
|
|
17
|
+
import type { OAuthGitLabConfig } from '../server/lib/oauth/gitlab.js';
|
|
18
|
+
import type { OAuthGiteaConfig } from '../server/lib/oauth/gitea.js';
|
|
19
|
+
import type { OAuthGoogleConfig } from '../server/lib/oauth/google.js';
|
|
20
|
+
import type { OAuthHerokuConfig } from '../server/lib/oauth/heroku.js';
|
|
21
|
+
import type { OAuthHubspotConfig } from '../server/lib/oauth/hubspot.js';
|
|
22
|
+
import type { OAuthInstagramConfig } from '../server/lib/oauth/instagram.js';
|
|
23
|
+
import type { OAuthKeycloakConfig } from '../server/lib/oauth/keycloak.js';
|
|
24
|
+
import type { OAuthKickConfig } from '../server/lib/oauth/kick.js';
|
|
25
|
+
import type { OAuthLineConfig } from '../server/lib/oauth/line.js';
|
|
26
|
+
import type { OAuthLinearConfig } from '../server/lib/oauth/linear.js';
|
|
27
|
+
import type { OAuthLinkedInConfig } from '../server/lib/oauth/linkedin.js';
|
|
28
|
+
import type { OAuthMastodonConfig } from '../server/lib/oauth/mastodon.js';
|
|
29
|
+
import type { OAuthMicrosoftConfig } from '../server/lib/oauth/microsoft.js';
|
|
30
|
+
import type { OAuthOidcConfig } from '../server/lib/oauth/oidc.js';
|
|
31
|
+
import type { OAuthOktaConfig } from '../server/lib/oauth/okta.js';
|
|
32
|
+
import type { OAuthOryConfig } from '../server/lib/oauth/ory.js';
|
|
33
|
+
import type { OAuthOsuConfig } from '../server/lib/oauth/osu.js';
|
|
34
|
+
import type { OAuthPaypalConfig } from '../server/lib/oauth/paypal.js';
|
|
35
|
+
import type { OAuthPolarConfig } from '../server/lib/oauth/polar.js';
|
|
36
|
+
import type { OAuthRiotGamesConfig } from '../server/lib/oauth/riotgames.js';
|
|
37
|
+
import type { OAuthRobloxConfig } from '../server/lib/oauth/roblox.js';
|
|
38
|
+
import type { OAuthSalesforceConfig } from '../server/lib/oauth/salesforce.js';
|
|
39
|
+
import type { OAuthSeznamConfig } from '../server/lib/oauth/seznam.js';
|
|
40
|
+
import type { OAuthShopifyCustomerConfig } from '../server/lib/oauth/shopifyCustomer.js';
|
|
41
|
+
import type { OAuthSlackConfig } from '../server/lib/oauth/slack.js';
|
|
42
|
+
import type { OAuthSpotifyConfig } from '../server/lib/oauth/spotify.js';
|
|
43
|
+
import type { OAuthSteamConfig } from '../server/lib/oauth/steam.js';
|
|
44
|
+
import type { OAuthStravaConfig } from '../server/lib/oauth/strava.js';
|
|
45
|
+
import type { OAuthTikTokConfig } from '../server/lib/oauth/tiktok.js';
|
|
46
|
+
import type { OAuthTwitchConfig } from '../server/lib/oauth/twitch.js';
|
|
47
|
+
import type { OAuthVKConfig } from '../server/lib/oauth/vk.js';
|
|
48
|
+
import type { OAuthWechatConfig } from '../server/lib/oauth/wechat.js';
|
|
49
|
+
import type { OAuthWorkOSConfig } from '../server/lib/oauth/workos.js';
|
|
50
|
+
import type { OAuthXConfig } from '../server/lib/oauth/x.js';
|
|
51
|
+
import type { OAuthXSUAAConfig } from '../server/lib/oauth/xsuaa.js';
|
|
52
|
+
import type { OAuthYandexConfig } from '../server/lib/oauth/yandex.js';
|
|
53
|
+
import type { OAuthZendeskConfig } from '../server/lib/oauth/zendesk.js';
|
|
54
|
+
import type { OAuthZitadelConfig } from '../server/lib/oauth/zitadel.js';
|
|
55
|
+
/**
|
|
56
|
+
* Runtime configuration of every OAuth provider supported by the module, exposed as `runtimeConfig.oauth`.
|
|
57
|
+
*
|
|
58
|
+
* Keys are the provider names accepted by `defineOAuth<Provider>EventHandler()`, and they are always
|
|
59
|
+
* set by the module, so server code can read them without checking for `undefined`. In `nuxt.config`,
|
|
60
|
+
* `runtimeConfig` is deep-partial, so you only write the providers you use.
|
|
61
|
+
* Providers contributed by other modules can add themselves through the `OAuthRuntimeConfigExtension`
|
|
62
|
+
* interface, and any other key stays `unknown`.
|
|
63
|
+
*/
|
|
64
|
+
export interface OAuthRuntimeConfig extends OAuthRuntimeConfigExtension {
|
|
65
|
+
[provider: string]: unknown;
|
|
66
|
+
apple: OAuthAppleConfig;
|
|
67
|
+
atlassian: OAuthAtlassianConfig;
|
|
68
|
+
auth0: OAuthAuth0Config;
|
|
69
|
+
authentik: OAuthAuthentikConfig;
|
|
70
|
+
azureb2c: OAuthAzureB2CConfig;
|
|
71
|
+
battledotnet: OAuthBattledotnetConfig;
|
|
72
|
+
bluesky: AtprotoProviderClientMetadata;
|
|
73
|
+
box: OAuthBoxConfig;
|
|
74
|
+
cloudflare: OAuthCloudflareConfig;
|
|
75
|
+
cognito: OAuthCognitoConfig;
|
|
76
|
+
discord: OAuthDiscordConfig;
|
|
77
|
+
dropbox: OAuthDropboxConfig;
|
|
78
|
+
facebook: OAuthFacebookConfig;
|
|
79
|
+
gitea: OAuthGiteaConfig;
|
|
80
|
+
github: OAuthGitHubConfig;
|
|
81
|
+
gitlab: OAuthGitLabConfig;
|
|
82
|
+
google: OAuthGoogleConfig;
|
|
83
|
+
heroku: OAuthHerokuConfig;
|
|
84
|
+
hubspot: OAuthHubspotConfig;
|
|
85
|
+
instagram: OAuthInstagramConfig;
|
|
86
|
+
keycloak: OAuthKeycloakConfig;
|
|
87
|
+
kick: OAuthKickConfig;
|
|
88
|
+
line: OAuthLineConfig;
|
|
89
|
+
linear: OAuthLinearConfig;
|
|
90
|
+
linkedin: OAuthLinkedInConfig;
|
|
91
|
+
livechat: LiveChatConfig;
|
|
92
|
+
mastodon: OAuthMastodonConfig;
|
|
93
|
+
microsoft: OAuthMicrosoftConfig;
|
|
94
|
+
oidc: OAuthOidcConfig;
|
|
95
|
+
okta: OAuthOktaConfig;
|
|
96
|
+
ory: OAuthOryConfig;
|
|
97
|
+
osu: OAuthOsuConfig;
|
|
98
|
+
paypal: OAuthPaypalConfig;
|
|
99
|
+
polar: OAuthPolarConfig;
|
|
100
|
+
riotgames: OAuthRiotGamesConfig;
|
|
101
|
+
roblox: OAuthRobloxConfig;
|
|
102
|
+
salesforce: OAuthSalesforceConfig;
|
|
103
|
+
seznam: OAuthSeznamConfig;
|
|
104
|
+
shopifyCustomer: OAuthShopifyCustomerConfig;
|
|
105
|
+
slack: OAuthSlackConfig;
|
|
106
|
+
spotify: OAuthSpotifyConfig;
|
|
107
|
+
steam: OAuthSteamConfig;
|
|
108
|
+
strava: OAuthStravaConfig;
|
|
109
|
+
tiktok: OAuthTikTokConfig;
|
|
110
|
+
twitch: OAuthTwitchConfig;
|
|
111
|
+
vk: OAuthVKConfig;
|
|
112
|
+
wechat: OAuthWechatConfig;
|
|
113
|
+
workos: OAuthWorkOSConfig;
|
|
114
|
+
x: OAuthXConfig;
|
|
115
|
+
xsuaa: OAuthXSUAAConfig;
|
|
116
|
+
yandex: OAuthYandexConfig;
|
|
117
|
+
zendesk: OAuthZendeskConfig;
|
|
118
|
+
zitadel: OAuthZitadelConfig;
|
|
119
|
+
}
|
|
File without changes
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ComputedRef, Ref } from 'vue';
|
|
2
|
+
import type { OmitWithIndexSignature } from './utils.js';
|
|
2
3
|
export interface User {
|
|
3
4
|
}
|
|
4
5
|
export interface SecureSessionData {
|
|
@@ -38,9 +39,11 @@ export interface UserSessionComposable {
|
|
|
38
39
|
*/
|
|
39
40
|
user: ComputedRef<User | null>;
|
|
40
41
|
/**
|
|
41
|
-
* The session object.
|
|
42
|
+
* The session object, without `id` for a visitor who has no session yet.
|
|
42
43
|
*/
|
|
43
|
-
session: Ref<UserSession
|
|
44
|
+
session: Ref<(OmitWithIndexSignature<UserSession, 'id'> & {
|
|
45
|
+
id?: string;
|
|
46
|
+
}) | null>;
|
|
44
47
|
/**
|
|
45
48
|
* Fetch the user session from the server.
|
|
46
49
|
*/
|
|
@@ -10,8 +10,7 @@ export const atprotoProviderDefaultClientMetadata = {
|
|
|
10
10
|
grantTypes: ["authorization_code"],
|
|
11
11
|
responseTypes: ["code"],
|
|
12
12
|
applicationType: "web",
|
|
13
|
-
|
|
14
|
-
redirectUris: void 0,
|
|
13
|
+
redirectUris: [],
|
|
15
14
|
dpopBoundAccessTokens: true,
|
|
16
15
|
tokenEndpointAuthMethod: "none"
|
|
17
16
|
};
|
package/dist/types.d.mts
CHANGED