nuxt-auth-utils 0.5.11 → 0.5.13

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 CHANGED
@@ -217,6 +217,7 @@ It can also be set using environment variables:
217
217
  - Authentik
218
218
  - AWS Cognito
219
219
  - Battle.net
220
+ - Bluesky (AT Protocol)
220
221
  - Discord
221
222
  - Dropbox
222
223
  - Facebook
@@ -304,6 +305,28 @@ export default defineNuxtConfig({
304
305
  })
305
306
  ```
306
307
 
308
+ ### AT Protocol
309
+
310
+ Social networks that rely on AT Protocol (e.g., Bluesky) slightly differ from a regular OAuth flow.
311
+
312
+ To enable OAuth with AT Protocol, you need to:
313
+
314
+ 1. Install the peer dependencies:
315
+
316
+ ```bash
317
+ npx nypm i @atproto/oauth-client-node @atproto/api
318
+ ```
319
+
320
+ 2. Enable it in your `nuxt.config.ts`
321
+
322
+ ```ts
323
+ export default defineNuxtConfig({
324
+ auth: {
325
+ atproto: true
326
+ }
327
+ })
328
+ ```
329
+
307
330
  ### WebAuthn (passkey)
308
331
 
309
332
  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.
@@ -711,26 +734,26 @@ Checkout the [`SessionConfig`](https://github.com/unjs/h3/blob/c04c458810e34eb15
711
734
 
712
735
  ```bash
713
736
  # Install dependencies
714
- npm install
737
+ pnpm install
715
738
 
716
739
  # Generate type stubs
717
- npm run dev:prepare
740
+ pnpm run dev:prepare
718
741
 
719
742
  # Develop with the playground
720
- npm run dev
743
+ pnpm run dev
721
744
 
722
745
  # Build the playground
723
- npm run dev:build
746
+ pnpm run dev:build
724
747
 
725
748
  # Run ESLint
726
- npm run lint
749
+ pnpm run lint
727
750
 
728
751
  # Run Vitest
729
- npm run test
730
- npm run test:watch
752
+ pnpm run test
753
+ pnpm run test:watch
731
754
 
732
755
  # Release new version
733
- npm run release
756
+ pnpm run release
734
757
  ```
735
758
 
736
759
  <!-- Badges -->
package/dist/module.d.mts CHANGED
@@ -8,6 +8,11 @@ interface ModuleOptions {
8
8
  * @default false
9
9
  */
10
10
  webAuthn?: boolean;
11
+ /**
12
+ * Enable atproto OAuth (Bluesky, etc.)
13
+ * @default false
14
+ */
15
+ atproto?: boolean;
11
16
  /**
12
17
  * Hash options used for password hashing
13
18
  */
package/dist/module.d.ts CHANGED
@@ -8,6 +8,11 @@ interface ModuleOptions {
8
8
  * @default false
9
9
  */
10
10
  webAuthn?: boolean;
11
+ /**
12
+ * Enable atproto OAuth (Bluesky, etc.)
13
+ * @default false
14
+ */
15
+ atproto?: boolean;
11
16
  /**
12
17
  * Hash options used for password hashing
13
18
  */
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "auth-utils",
3
3
  "configKey": "auth",
4
- "version": "0.5.11",
4
+ "version": "0.5.13",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "0.8.4",
7
7
  "unbuild": "2.0.0"
package/dist/module.mjs CHANGED
@@ -4,6 +4,26 @@ import { join } from 'pathe';
4
4
  import { defu } from 'defu';
5
5
  import { randomUUID } from 'uncrypto';
6
6
 
7
+ const atprotoProviders = ["bluesky"];
8
+ const atprotoProviderDefaultClientMetadata = {
9
+ clientMetadataFilename: "",
10
+ clientName: "",
11
+ clientUri: "",
12
+ logoUri: "",
13
+ policyUri: "",
14
+ tosUri: "",
15
+ scope: ["atproto"],
16
+ grantTypes: ["authorization_code"],
17
+ responseTypes: ["code"],
18
+ applicationType: "web",
19
+ redirectUris: [],
20
+ dpopBoundAccessTokens: true,
21
+ tokenEndpointAuthMethod: "none"
22
+ };
23
+ function getClientMetadataFilename(provider, config) {
24
+ return config?.clientMetadataFilename || provider + "/client-metadata.json";
25
+ }
26
+
7
27
  const module = defineNuxtModule({
8
28
  meta: {
9
29
  name: "auth-utils",
@@ -12,6 +32,7 @@ const module = defineNuxtModule({
12
32
  // Default configuration options of the Nuxt module
13
33
  defaults: {
14
34
  webAuthn: false,
35
+ atproto: false,
15
36
  hash: {
16
37
  scrypt: {}
17
38
  }
@@ -163,6 +184,32 @@ const module = defineNuxtModule({
163
184
  clientSecret: "",
164
185
  redirectURL: ""
165
186
  });
187
+ for (const provider of atprotoProviders) {
188
+ runtimeConfig.oauth[provider] = defu(runtimeConfig.oauth[provider], atprotoProviderDefaultClientMetadata);
189
+ }
190
+ if (options.atproto) {
191
+ const missingDeps = [];
192
+ const peerDeps = ["@atproto/oauth-client-node", "@atproto/api"];
193
+ for (const pkg of peerDeps) {
194
+ await import(pkg).catch(() => {
195
+ missingDeps.push(pkg);
196
+ });
197
+ }
198
+ if (missingDeps.length > 0) {
199
+ logger.withTag("nuxt-auth-utils").error(`Missing dependencies for \`atproto\`, please install with:
200
+
201
+ \`npx nypm i ${missingDeps.join(" ")}\``);
202
+ process.exit(1);
203
+ }
204
+ for (const provider of atprotoProviders) {
205
+ addServerHandler({
206
+ handler: resolver.resolve("./runtime/server/routes/atproto/client-metadata.json.get.ts"),
207
+ route: "/" + getClientMetadataFilename(provider, runtimeConfig.oauth[provider]),
208
+ method: "get"
209
+ });
210
+ }
211
+ addServerImportsDir(resolver.resolve("./runtime/server/lib/atproto"));
212
+ }
166
213
  runtimeConfig.oauth.keycloak = defu(runtimeConfig.oauth.keycloak, {
167
214
  clientId: "",
168
215
  clientSecret: "",
@@ -1,5 +1,5 @@
1
1
  declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
2
- [key: string]: unknown;
2
+ id: any;
3
3
  user?: import("../../types").User;
4
4
  }>>;
5
5
  export default _default;
@@ -0,0 +1,37 @@
1
+ import type { H3Event } from 'h3';
2
+ import type { NodeSavedSession, NodeSavedSessionStore, NodeSavedState, NodeSavedStateStore } from '@atproto/oauth-client-node';
3
+ import type { AppBskyActorDefs } from '@atproto/api';
4
+ import type { OAuthConfig } from '#auth-utils';
5
+ export interface OAuthBlueskyConfig {
6
+ /**
7
+ * Redirect URL to use for this authorization flow. It should only consist of the path, as the hostname must always match the client id's hostname.
8
+ * @default process.env.NUXT_OAUTH_BLUESKY_REDIRECT_URL
9
+ * @example '/auth/bluesky'
10
+ */
11
+ redirectUrl?: string;
12
+ /**
13
+ * Bluesky OAuth Scope. The `atproto` scope is required and will be added if not present.
14
+ * @default ['atproto']
15
+ * @see https://atproto.com/specs/oauth#authorization-scopes
16
+ * @example ['atproto', 'transition:generic']
17
+ */
18
+ scope?: string[];
19
+ }
20
+ type BlueSkyUser = AppBskyActorDefs.ProfileViewDetailed | Pick<AppBskyActorDefs.ProfileView, 'did'>;
21
+ type BlueSkyTokens = NodeSavedSession['tokenSet'];
22
+ export declare function defineOAuthBlueskyEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthBlueskyConfig, BlueSkyUser, BlueSkyTokens>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
23
+ export declare class StateStore implements NodeSavedStateStore {
24
+ private event;
25
+ private readonly stateKey;
26
+ constructor(event: H3Event);
27
+ get(): Promise<NodeSavedState | undefined>;
28
+ set(key: string, val: NodeSavedState): Promise<void>;
29
+ del(): Promise<void>;
30
+ }
31
+ export declare class SessionStore implements NodeSavedSessionStore {
32
+ private store;
33
+ get(key: string): Promise<NodeSavedSession | undefined>;
34
+ set(key: string, val: NodeSavedSession): Promise<void>;
35
+ del(key: string): Promise<void>;
36
+ }
37
+ export {};
@@ -0,0 +1,101 @@
1
+ import { createError, eventHandler, getQuery, sendRedirect, getCookie, setCookie, deleteCookie } from "h3";
2
+ import { NodeOAuthClient, OAuthCallbackError, OAuthResolverError, OAuthResponseError } from "@atproto/oauth-client-node";
3
+ import { Agent } from "@atproto/api";
4
+ import { getAtprotoClientMetadata } from "../../utils/atproto.js";
5
+ export function defineOAuthBlueskyEventHandler({ config, onSuccess, onError }) {
6
+ return eventHandler(async (event) => {
7
+ const clientMetadata = getAtprotoClientMetadata(event, "bluesky", config);
8
+ const scopes = clientMetadata.scope?.split(" ") ?? [];
9
+ const sessionStore = new SessionStore();
10
+ const stateStore = new StateStore(event);
11
+ const client = new NodeOAuthClient({
12
+ stateStore,
13
+ sessionStore,
14
+ clientMetadata
15
+ });
16
+ const query = getQuery(event);
17
+ if (!query.code) {
18
+ try {
19
+ const handle = query.handle?.toString();
20
+ if (!handle) throw createError({
21
+ statusCode: 400,
22
+ message: "Query parameter `handle` empty or missing. Please provide a valid Bluesky handle."
23
+ });
24
+ const url = await client.authorize(handle, { scope: clientMetadata.scope });
25
+ return sendRedirect(event, url.toString());
26
+ } catch (err) {
27
+ const error = (() => {
28
+ switch (true) {
29
+ case err instanceof OAuthResponseError:
30
+ return createError({
31
+ statusCode: 500,
32
+ message: `Bluesky login failed: ${err.errorDescription || "Unknown error"}`,
33
+ data: err.payload
34
+ });
35
+ case err instanceof OAuthResolverError:
36
+ return createError({
37
+ statusCode: 400,
38
+ message: `Bluesky login failed: ${err.message || "Unknown error"}`
39
+ });
40
+ default:
41
+ throw err;
42
+ }
43
+ })();
44
+ if (!onError) throw error;
45
+ return onError(event, error);
46
+ }
47
+ }
48
+ try {
49
+ const { session } = await client.callback(new URLSearchParams(query));
50
+ const sessionInfo = await sessionStore.get(session.did);
51
+ const profile = scopes.includes("transition:generic") ? (await new Agent(session).getProfile({ actor: session.did })).data : null;
52
+ sessionStore.del(session.did);
53
+ return onSuccess(event, {
54
+ user: profile ?? { did: session.did },
55
+ tokens: sessionInfo.tokenSet
56
+ });
57
+ } catch (err) {
58
+ if (!(err instanceof OAuthCallbackError)) throw err;
59
+ const error = createError({
60
+ statusCode: 500,
61
+ message: `Bluesky login failed: ${err.message || "Unknown error"}`
62
+ });
63
+ if (!onError) throw error;
64
+ return onError(event, error);
65
+ }
66
+ });
67
+ }
68
+ export class StateStore {
69
+ constructor(event) {
70
+ this.event = event;
71
+ }
72
+ stateKey = "oauth-bluesky-state";
73
+ async get() {
74
+ const result = getCookie(this.event, this.stateKey);
75
+ if (!result) return;
76
+ return JSON.parse(atob(result));
77
+ }
78
+ async set(key, val) {
79
+ setCookie(this.event, this.stateKey, btoa(JSON.stringify(val)), {
80
+ path: "/",
81
+ httpOnly: true,
82
+ secure: true,
83
+ sameSite: "lax"
84
+ });
85
+ }
86
+ async del() {
87
+ deleteCookie(this.event, this.stateKey);
88
+ }
89
+ }
90
+ export class SessionStore {
91
+ store = {};
92
+ async get(key) {
93
+ return this.store[key];
94
+ }
95
+ async set(key, val) {
96
+ this.store[key] = val;
97
+ }
98
+ async del(key) {
99
+ delete this.store[key];
100
+ }
101
+ }
@@ -0,0 +1,129 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, {
2
+ redirect_uris: [string, ...string[]];
3
+ scope?: string | undefined;
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;
5
+ grant_types?: ["authorization_code" | "implicit" | "refresh_token" | "password" | "client_credentials" | "urn:ietf:params:oauth:grant-type:jwt-bearer" | "urn:ietf:params:oauth:grant-type:saml2-bearer", ...("authorization_code" | "implicit" | "refresh_token" | "password" | "client_credentials" | "urn:ietf:params:oauth:grant-type:jwt-bearer" | "urn:ietf:params:oauth:grant-type:saml2-bearer")[]] | undefined;
6
+ token_endpoint_auth_method?: "client_secret_basic" | "client_secret_jwt" | "client_secret_post" | "none" | "private_key_jwt" | "self_signed_tls_client_auth" | "tls_client_auth" | undefined;
7
+ token_endpoint_auth_signing_alg?: string | undefined;
8
+ userinfo_signed_response_alg?: string | undefined;
9
+ userinfo_encrypted_response_alg?: string | undefined;
10
+ jwks_uri?: string | undefined;
11
+ jwks?: {
12
+ keys: ({
13
+ kty: "RSA";
14
+ n: string;
15
+ e: string;
16
+ alg?: "RS256" | "RS384" | "RS512" | "PS256" | "PS384" | "PS512" | undefined;
17
+ kid?: string | undefined;
18
+ ext?: boolean | undefined;
19
+ use?: "sig" | "enc" | undefined;
20
+ key_ops?: ("sign" | "verify" | "encrypt" | "decrypt" | "wrapKey" | "unwrapKey" | "deriveKey" | "deriveBits")[] | undefined;
21
+ x5c?: string[] | undefined;
22
+ x5t?: string | undefined;
23
+ "x5t#S256"?: string | undefined;
24
+ x5u?: string | undefined;
25
+ d?: string | undefined;
26
+ p?: string | undefined;
27
+ q?: string | undefined;
28
+ dp?: string | undefined;
29
+ dq?: string | undefined;
30
+ qi?: string | undefined;
31
+ oth?: [{
32
+ d?: string | undefined;
33
+ r?: string | undefined;
34
+ t?: string | undefined;
35
+ }, ...{
36
+ d?: string | undefined;
37
+ r?: string | undefined;
38
+ t?: string | undefined;
39
+ }[]] | undefined;
40
+ } | {
41
+ kty: "EC";
42
+ crv: "P-256" | "P-384" | "P-521";
43
+ x: string;
44
+ y: string;
45
+ alg?: "ES256" | "ES384" | "ES512" | undefined;
46
+ kid?: string | undefined;
47
+ ext?: boolean | undefined;
48
+ use?: "sig" | "enc" | undefined;
49
+ key_ops?: ("sign" | "verify" | "encrypt" | "decrypt" | "wrapKey" | "unwrapKey" | "deriveKey" | "deriveBits")[] | undefined;
50
+ x5c?: string[] | undefined;
51
+ x5t?: string | undefined;
52
+ "x5t#S256"?: string | undefined;
53
+ x5u?: string | undefined;
54
+ d?: string | undefined;
55
+ } | {
56
+ kty: "EC";
57
+ crv: "secp256k1";
58
+ x: string;
59
+ y: string;
60
+ alg?: "ES256K" | undefined;
61
+ kid?: string | undefined;
62
+ ext?: boolean | undefined;
63
+ use?: "sig" | "enc" | undefined;
64
+ key_ops?: ("sign" | "verify" | "encrypt" | "decrypt" | "wrapKey" | "unwrapKey" | "deriveKey" | "deriveBits")[] | undefined;
65
+ x5c?: string[] | undefined;
66
+ x5t?: string | undefined;
67
+ "x5t#S256"?: string | undefined;
68
+ x5u?: string | undefined;
69
+ d?: string | undefined;
70
+ } | {
71
+ kty: "OKP";
72
+ crv: "Ed25519" | "Ed448";
73
+ x: string;
74
+ alg?: "EdDSA" | undefined;
75
+ kid?: string | undefined;
76
+ ext?: boolean | undefined;
77
+ use?: "sig" | "enc" | undefined;
78
+ key_ops?: ("sign" | "verify" | "encrypt" | "decrypt" | "wrapKey" | "unwrapKey" | "deriveKey" | "deriveBits")[] | undefined;
79
+ x5c?: string[] | undefined;
80
+ x5t?: string | undefined;
81
+ "x5t#S256"?: string | undefined;
82
+ x5u?: string | undefined;
83
+ d?: string | undefined;
84
+ } | {
85
+ kty: "oct";
86
+ k: string;
87
+ alg?: "HS256" | "HS384" | "HS512" | undefined;
88
+ kid?: string | undefined;
89
+ ext?: boolean | undefined;
90
+ use?: "sig" | "enc" | undefined;
91
+ key_ops?: ("sign" | "verify" | "encrypt" | "decrypt" | "wrapKey" | "unwrapKey" | "deriveKey" | "deriveBits")[] | undefined;
92
+ x5c?: string[] | undefined;
93
+ x5t?: string | undefined;
94
+ "x5t#S256"?: string | undefined;
95
+ x5u?: string | undefined;
96
+ } | {
97
+ kty: string;
98
+ alg?: string | undefined;
99
+ kid?: string | undefined;
100
+ ext?: boolean | undefined;
101
+ use?: "sig" | "enc" | undefined;
102
+ key_ops?: ("sign" | "verify" | "encrypt" | "decrypt" | "wrapKey" | "unwrapKey" | "deriveKey" | "deriveBits")[] | undefined;
103
+ x5c?: string[] | undefined;
104
+ x5t?: string | undefined;
105
+ "x5t#S256"?: string | undefined;
106
+ x5u?: string | undefined;
107
+ })[];
108
+ } | undefined;
109
+ application_type?: "web" | "native" | undefined;
110
+ subject_type?: "public" | "pairwise" | undefined;
111
+ request_object_signing_alg?: string | undefined;
112
+ id_token_signed_response_alg?: string | undefined;
113
+ authorization_signed_response_alg?: string | undefined;
114
+ authorization_encrypted_response_enc?: "A128CBC-HS256" | undefined;
115
+ authorization_encrypted_response_alg?: string | undefined;
116
+ client_id?: string | undefined;
117
+ client_name?: string | undefined;
118
+ client_uri?: string | undefined;
119
+ policy_uri?: string | undefined;
120
+ tos_uri?: string | undefined;
121
+ logo_uri?: string | undefined;
122
+ default_max_age?: number | undefined;
123
+ require_auth_time?: boolean | undefined;
124
+ contacts?: string[] | undefined;
125
+ tls_client_certificate_bound_access_tokens?: boolean | undefined;
126
+ dpop_bound_access_tokens?: boolean | undefined;
127
+ authorization_details_types?: string[] | undefined;
128
+ }>;
129
+ export default _default;
@@ -0,0 +1,18 @@
1
+ import { defineEventHandler, createError } from "h3";
2
+ import { getAtprotoClientMetadata } from "../../utils/atproto.js";
3
+ import { atprotoProviders, getClientMetadataFilename } from "../../../../utils/atproto";
4
+ import { useRuntimeConfig } from "#imports";
5
+ export default defineEventHandler((event) => {
6
+ const path = event.path.slice(1);
7
+ const runtimeConfig = useRuntimeConfig(event);
8
+ for (const provider of atprotoProviders) {
9
+ const config = runtimeConfig.oauth[provider];
10
+ if (getClientMetadataFilename(provider, config) === path) {
11
+ return getAtprotoClientMetadata(event, provider);
12
+ }
13
+ }
14
+ throw createError({
15
+ statusCode: 404,
16
+ message: "Provider not found"
17
+ });
18
+ });
@@ -0,0 +1,5 @@
1
+ import type { H3Event } from 'h3';
2
+ import type { OAuthClientMetadataInput } from '@atproto/oauth-client-node';
3
+ import type { OAuthBlueskyConfig } from '../lib/oauth/bluesky.js';
4
+ import type { ATProtoProvider, OAuthConfig } from '#auth-utils';
5
+ export declare function getAtprotoClientMetadata(event: H3Event, provider: ATProtoProvider, config?: OAuthConfig<OAuthBlueskyConfig>['config']): OAuthClientMetadataInput;
@@ -0,0 +1,35 @@
1
+ import { getOAuthRedirectURL } from "../lib/utils.js";
2
+ import { getClientMetadataFilename } from "../../../utils/atproto";
3
+ import { getRequestURL, useRuntimeConfig } from "#imports";
4
+ export function getAtprotoClientMetadata(event, provider, config) {
5
+ const providerRuntimeConfig = useRuntimeConfig(event).oauth[provider];
6
+ const scopes = [.../* @__PURE__ */ new Set(["atproto", ...config?.scope ?? [], ...providerRuntimeConfig.scope ?? []])];
7
+ const scope = scopes.join(" ");
8
+ const grantTypes = [.../* @__PURE__ */ new Set(["authorization_code", ...providerRuntimeConfig.grantTypes ?? []])];
9
+ const requestURL = getRequestURL(event);
10
+ const baseUrl = `${requestURL.protocol}//${requestURL.host}`;
11
+ const redirectURL = new URL(
12
+ config?.redirectUrl && baseUrl + config.redirectUrl || providerRuntimeConfig.redirectUris[0] && baseUrl + providerRuntimeConfig.redirectUris[0] || getOAuthRedirectURL(event)
13
+ );
14
+ const dev = import.meta.dev;
15
+ if (dev && redirectURL.hostname === "localhost") {
16
+ redirectURL.hostname = "127.0.0.1";
17
+ }
18
+ const redirectUris = (providerRuntimeConfig.redirectUris.length ? providerRuntimeConfig.redirectUris : [requestURL.pathname]).map((uri) => new URL(`${redirectURL.protocol}//${redirectURL.host}${uri}`).toString());
19
+ const clientId = dev ? `http://localhost?redirect_uri=${encodeURIComponent(redirectURL.toString())}&scope=${encodeURIComponent(scope)}` : `${baseUrl}/${getClientMetadataFilename("bluesky", providerRuntimeConfig)}`;
20
+ const clientMetadata = {
21
+ client_name: providerRuntimeConfig.clientName || void 0,
22
+ client_uri: providerRuntimeConfig.clientUri || void 0,
23
+ logo_uri: providerRuntimeConfig.logoUri || void 0,
24
+ policy_uri: providerRuntimeConfig.policyUri || void 0,
25
+ tos_uri: providerRuntimeConfig.tosUri || void 0,
26
+ client_id: clientId,
27
+ redirect_uris: redirectUris,
28
+ scope,
29
+ grant_types: grantTypes,
30
+ application_type: providerRuntimeConfig.applicationType,
31
+ token_endpoint_auth_method: providerRuntimeConfig.tokenEndpointAuthMethod,
32
+ dpop_bound_access_tokens: true
33
+ };
34
+ return clientMetadata;
35
+ }
@@ -20,7 +20,11 @@ export declare const sessionHooks: import("hookable").Hookable<SessionHooks, imp
20
20
  * @param event The Request (h3) event
21
21
  * @returns The user session
22
22
  */
23
- export declare function getUserSession(event: UseSessionEvent): Promise<UserSession>;
23
+ export declare function getUserSession(event: UseSessionEvent): Promise<{
24
+ id: any;
25
+ user?: import("#auth-utils").User;
26
+ secure?: import("#auth-utils").SecureSessionData;
27
+ }>;
24
28
  /**
25
29
  * Set a user session
26
30
  * @param event The Request (h3) event
@@ -4,7 +4,11 @@ import { createHooks } from "hookable";
4
4
  import { useRuntimeConfig } from "#imports";
5
5
  export const sessionHooks = createHooks();
6
6
  export async function getUserSession(event) {
7
- return (await _useSession(event)).data;
7
+ const session = await _useSession(event);
8
+ return {
9
+ id: session.id,
10
+ ...session.data
11
+ };
8
12
  }
9
13
  export async function setUserSession(event, data, config) {
10
14
  const session = await _useSession(event, config);
@@ -0,0 +1,62 @@
1
+ import type { OAuthClientMetadata } from '@atproto/oauth-client-node';
2
+ export interface AtprotoProviderClientMetadata {
3
+ /**
4
+ * The name of the client metadata file. This is used to construct the client ID.
5
+ * @example 'client-metadata.json'
6
+ * @example 'bluesky/client-metadata.json'
7
+ */
8
+ clientMetadataFilename: string;
9
+ /**
10
+ * The human-readable name of the client.
11
+ */
12
+ clientName?: OAuthClientMetadata['client_name'];
13
+ /**
14
+ * Not to be confused with client_id, this is a homepage URL for the client. If provided, the client_uri must have the same hostname as client_id.
15
+ */
16
+ clientUri?: OAuthClientMetadata['client_uri'];
17
+ /**
18
+ * The client's logo URL.
19
+ */
20
+ logoUri?: OAuthClientMetadata['logo_uri'];
21
+ /**
22
+ * URL to human-readable terms of service (ToS) for the client. Only https: URIs are allowed.
23
+ */
24
+ tosUri?: OAuthClientMetadata['tos_uri'];
25
+ /**
26
+ * URL to human-readable privacy policy for the client. Only https: URIs are allowed.
27
+ */
28
+ policyUri?: OAuthClientMetadata['policy_uri'];
29
+ /**
30
+ * Must be one of web or native, with web as the default if not specified. Note that this is field specified by OpenID/OIDC, which we are borrowing. Used by the Authorization Server to enforce the relevant "best current practices".
31
+ * @default 'web'
32
+ */
33
+ applicationType?: OAuthClientMetadata['application_type'];
34
+ /**
35
+ * `authorization_code` must always be included and will be added if missing. `refresh_token` is optional, but must be included if the client will make token refresh requests.
36
+ * @default ['authorization_code']
37
+ */
38
+ grantTypes?: OAuthClientMetadata['grant_types'];
39
+ /**
40
+ * All scope values which might be requested by this client are declared here. The atproto scope is required, and will be added if missing.
41
+ * @default ['atproto']
42
+ */
43
+ scope?: NonNullable<OAuthClientMetadata['scope']>[];
44
+ /**
45
+ * `code` must always be included and will be added if missing.
46
+ */
47
+ responseTypes?: OAuthClientMetadata['response_types'];
48
+ /**
49
+ * At least one redirect URI is required. The URL origin must match that of the `clientId`, so declare only the path.
50
+ * @example ['/auth/callback']
51
+ */
52
+ redirectUris: OAuthClientMetadata['redirect_uris'];
53
+ /**
54
+ * The token endpoint authentication method. `none` is the default, and the only supported value at this time.
55
+ * @default 'none'
56
+ */
57
+ tokenEndpointAuthMethod?: OAuthClientMetadata['token_endpoint_auth_method'];
58
+ /**
59
+ * DPoP is mandatory for all clients, so this must be present and true.
60
+ */
61
+ dpopBoundAccessTokens?: OAuthClientMetadata['dpop_bound_access_tokens'];
62
+ }
File without changes
@@ -1,3 +1,3 @@
1
1
  export type { User, UserSession, UserSessionRequired, UserSessionComposable, SecureSessionData } from './session.js';
2
- export type { OAuthConfig, OAuthProvider, OnError } from './oauth-config.js';
2
+ export type { OAuthConfig, OAuthProvider, ATProtoProvider, OnError } from './oauth-config.js';
3
3
  export type { WebAuthnCredential, WebAuthnRegisterEventHandlerOptions, WebAuthnAuthenticateEventHandlerOptions, WebAuthnComposable, WebAuthnUser, } from './webauthn.js';
@@ -1,5 +1,6 @@
1
1
  import type { H3Event, H3Error } from 'h3';
2
- export type OAuthProvider = 'atlassian' | 'auth0' | 'authentik' | 'battledotnet' | 'cognito' | 'discord' | 'dropbox' | 'facebook' | 'gitea' | 'github' | 'gitlab' | 'google' | 'hubspot' | 'instagram' | 'keycloak' | 'line' | 'linear' | 'linkedin' | 'microsoft' | 'paypal' | 'polar' | 'spotify' | 'seznam' | 'steam' | 'strava' | 'tiktok' | 'twitch' | 'vk' | 'workos' | 'x' | 'xsuaa' | 'yandex' | 'zitadel' | 'apple' | (string & {});
2
+ export type ATProtoProvider = 'bluesky';
3
+ export type OAuthProvider = ATProtoProvider | 'atlassian' | 'auth0' | 'authentik' | 'battledotnet' | 'cognito' | 'discord' | 'dropbox' | 'facebook' | 'gitea' | 'github' | 'gitlab' | 'google' | 'hubspot' | 'instagram' | 'keycloak' | 'line' | 'linear' | 'linkedin' | 'microsoft' | 'paypal' | 'polar' | 'spotify' | 'seznam' | 'steam' | 'strava' | 'tiktok' | 'twitch' | 'vk' | 'workos' | 'x' | 'xsuaa' | 'yandex' | 'zitadel' | 'apple' | (string & {});
3
4
  export type OnError = (event: H3Event, error: H3Error) => Promise<void> | void;
4
5
  export interface OAuthConfig<TConfig, TUser = any, TTokens = any> {
5
6
  config?: TConfig;
@@ -4,6 +4,10 @@ export interface User {
4
4
  export interface SecureSessionData {
5
5
  }
6
6
  export interface UserSession {
7
+ /**
8
+ * Session ID
9
+ */
10
+ id?: string;
7
11
  /**
8
12
  * User session data, available on client and server
9
13
  */
@@ -18,6 +22,7 @@ export interface UserSession {
18
22
  [key: string]: unknown;
19
23
  }
20
24
  export interface UserSessionRequired extends UserSession {
25
+ id: string;
21
26
  user: User;
22
27
  }
23
28
  export interface UserSessionComposable {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-auth-utils",
3
- "version": "0.5.11",
3
+ "version": "0.5.13",
4
4
  "description": "Add Authentication to Nuxt applications with secured & sealed cookies sessions.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@adonisjs/hash": "^9.0.5",
39
- "@nuxt/kit": "^3.15.2",
39
+ "@nuxt/kit": "^3.15.4",
40
40
  "defu": "^6.1.4",
41
41
  "h3": "^1.14.0",
42
42
  "hookable": "^5.5.3",
@@ -49,6 +49,8 @@
49
49
  "uncrypto": "^0.1.3"
50
50
  },
51
51
  "peerDependencies": {
52
+ "@atproto/api": "^0.13.15",
53
+ "@atproto/oauth-client-node": "^0.2.0",
52
54
  "@simplewebauthn/browser": "^11.0.0",
53
55
  "@simplewebauthn/server": "^11.0.0"
54
56
  },
@@ -58,23 +60,29 @@
58
60
  },
59
61
  "@simplewebauthn/server": {
60
62
  "optional": true
63
+ },
64
+ "@atproto/oauth-client-node": {
65
+ "optional": true
66
+ },
67
+ "@atproto/api": {
68
+ "optional": true
61
69
  }
62
70
  },
63
71
  "devDependencies": {
64
- "@iconify-json/simple-icons": "^1.2.21",
65
- "@nuxt/devtools": "latest",
66
- "@nuxt/eslint-config": "^0.7.5",
72
+ "@iconify-json/simple-icons": "^1.2.23",
73
+ "@nuxt/devtools": "2.0.0-beta.7",
74
+ "@nuxt/eslint-config": "^0.7.6",
67
75
  "@nuxt/module-builder": "^0.8.4",
68
- "@nuxt/schema": "^3.15.2",
76
+ "@nuxt/schema": "^3.15.4",
69
77
  "@nuxt/test-utils": "^3.15.4",
70
78
  "@nuxt/ui": "^2.21.0",
71
79
  "@nuxt/ui-pro": "^1.7.0",
72
80
  "@simplewebauthn/types": "^12.0.0",
73
81
  "changelogen": "^0.5.7",
74
- "eslint": "^9.18.0",
75
- "nuxt": "^3.15.2",
82
+ "eslint": "^9.19.0",
83
+ "nuxt": "^3.15.4",
76
84
  "typescript": "5.6.3",
77
- "vitest": "^3.0.3",
85
+ "vitest": "^3.0.5",
78
86
  "vue-tsc": "^2.2.0"
79
87
  }
80
88
  }