nuxt-auth-utils 0.5.10 → 0.5.12
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 +39 -9
- package/dist/module.d.mts +5 -0
- package/dist/module.d.ts +5 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +53 -0
- package/dist/runtime/app/composables/session.js +20 -0
- package/dist/runtime/app/plugins/session.client.js +5 -0
- package/dist/runtime/server/api/session.get.d.ts +1 -1
- package/dist/runtime/server/lib/atproto/bluesky.d.ts +39 -0
- package/dist/runtime/server/lib/atproto/bluesky.js +101 -0
- package/dist/runtime/server/lib/oauth/gitea.d.ts +51 -0
- package/dist/runtime/server/lib/oauth/gitea.js +85 -0
- package/dist/runtime/server/lib/oauth/github.js +1 -0
- package/dist/runtime/server/lib/oauth/microsoft.js +3 -2
- package/dist/runtime/server/routes/atproto/client-metadata.json.get.d.ts +129 -0
- package/dist/runtime/server/routes/atproto/client-metadata.json.get.js +18 -0
- package/dist/runtime/server/utils/atproto.d.ts +5 -0
- package/dist/runtime/server/utils/atproto.js +35 -0
- package/dist/runtime/server/utils/session.d.ts +5 -1
- package/dist/runtime/server/utils/session.js +5 -1
- package/dist/runtime/types/atproto.d.ts +62 -0
- package/dist/runtime/types/atproto.js +0 -0
- package/dist/runtime/types/index.d.ts +1 -1
- package/dist/runtime/types/oauth-config.d.ts +2 -1
- package/dist/runtime/types/session.d.ts +11 -0
- package/package.json +17 -9
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ Nuxt Auth Utils automatically adds some plugins to fetch the current user sessio
|
|
|
62
62
|
|
|
63
63
|
```vue
|
|
64
64
|
<script setup>
|
|
65
|
-
const { loggedIn, user, session, fetch, clear } = useUserSession()
|
|
65
|
+
const { loggedIn, user, session, fetch, clear, openInPopup } = useUserSession()
|
|
66
66
|
</script>
|
|
67
67
|
|
|
68
68
|
<template>
|
|
@@ -74,6 +74,8 @@ const { loggedIn, user, session, fetch, clear } = useUserSession()
|
|
|
74
74
|
<div v-else>
|
|
75
75
|
<h1>Not logged in</h1>
|
|
76
76
|
<a href="/auth/github">Login with GitHub</a>
|
|
77
|
+
<!-- or open the OAuth route in a popup -->
|
|
78
|
+
<button @click="openInPopup('/auth/github')">Login with GitHub</button>
|
|
77
79
|
</div>
|
|
78
80
|
</template>
|
|
79
81
|
```
|
|
@@ -106,6 +108,10 @@ interface UserSessionComposable {
|
|
|
106
108
|
* Clear the user session and remove the session cookie.
|
|
107
109
|
*/
|
|
108
110
|
clear: () => Promise<void>
|
|
111
|
+
/**
|
|
112
|
+
* Open the OAuth route in a popup that auto-closes when successful.
|
|
113
|
+
*/
|
|
114
|
+
openInPopup: (route: string, size?: { width?: number, height?: number }) => void
|
|
109
115
|
}
|
|
110
116
|
```
|
|
111
117
|
|
|
@@ -211,11 +217,13 @@ It can also be set using environment variables:
|
|
|
211
217
|
- Authentik
|
|
212
218
|
- AWS Cognito
|
|
213
219
|
- Battle.net
|
|
220
|
+
- Bluesky (AT Protocol)
|
|
214
221
|
- Discord
|
|
215
222
|
- Dropbox
|
|
216
223
|
- Facebook
|
|
217
224
|
- GitHub
|
|
218
225
|
- GitLab
|
|
226
|
+
- Gitea
|
|
219
227
|
- Google
|
|
220
228
|
- Hubspot
|
|
221
229
|
- Instagram
|
|
@@ -297,6 +305,28 @@ export default defineNuxtConfig({
|
|
|
297
305
|
})
|
|
298
306
|
```
|
|
299
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
|
+
|
|
300
330
|
### WebAuthn (passkey)
|
|
301
331
|
|
|
302
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.
|
|
@@ -704,26 +734,26 @@ Checkout the [`SessionConfig`](https://github.com/unjs/h3/blob/c04c458810e34eb15
|
|
|
704
734
|
|
|
705
735
|
```bash
|
|
706
736
|
# Install dependencies
|
|
707
|
-
|
|
737
|
+
pnpm install
|
|
708
738
|
|
|
709
739
|
# Generate type stubs
|
|
710
|
-
|
|
740
|
+
pnpm run dev:prepare
|
|
711
741
|
|
|
712
742
|
# Develop with the playground
|
|
713
|
-
|
|
743
|
+
pnpm run dev
|
|
714
744
|
|
|
715
745
|
# Build the playground
|
|
716
|
-
|
|
746
|
+
pnpm run dev:build
|
|
717
747
|
|
|
718
748
|
# Run ESLint
|
|
719
|
-
|
|
749
|
+
pnpm run lint
|
|
720
750
|
|
|
721
751
|
# Run Vitest
|
|
722
|
-
|
|
723
|
-
|
|
752
|
+
pnpm run test
|
|
753
|
+
pnpm run test:watch
|
|
724
754
|
|
|
725
755
|
# Release new version
|
|
726
|
-
|
|
756
|
+
pnpm run release
|
|
727
757
|
```
|
|
728
758
|
|
|
729
759
|
<!-- Badges -->
|
package/dist/module.d.mts
CHANGED
package/dist/module.d.ts
CHANGED
package/dist/module.json
CHANGED
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
|
}
|
|
@@ -97,6 +118,12 @@ const module = defineNuxtModule({
|
|
|
97
118
|
authenticate: {}
|
|
98
119
|
});
|
|
99
120
|
runtimeConfig.oauth = defu(runtimeConfig.oauth, {});
|
|
121
|
+
runtimeConfig.oauth.gitea = defu(runtimeConfig.oauth.gitea, {
|
|
122
|
+
clientId: "",
|
|
123
|
+
clientSecret: "",
|
|
124
|
+
redirectURL: "",
|
|
125
|
+
baseURL: ""
|
|
126
|
+
});
|
|
100
127
|
runtimeConfig.oauth.github = defu(runtimeConfig.oauth.github, {
|
|
101
128
|
clientId: "",
|
|
102
129
|
clientSecret: "",
|
|
@@ -157,6 +184,32 @@ const module = defineNuxtModule({
|
|
|
157
184
|
clientSecret: "",
|
|
158
185
|
redirectURL: ""
|
|
159
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
|
+
}
|
|
160
213
|
runtimeConfig.oauth.keycloak = defu(runtimeConfig.oauth.keycloak, {
|
|
161
214
|
clientId: "",
|
|
162
215
|
clientSecret: "",
|
|
@@ -28,12 +28,32 @@ export function useUserSession() {
|
|
|
28
28
|
authReadyState.value = true;
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
|
+
const popupListener = (e) => {
|
|
32
|
+
if (e.key === "temp-nuxt-auth-utils-popup") {
|
|
33
|
+
fetch();
|
|
34
|
+
window.removeEventListener("storage", popupListener);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const openInPopup = (route, size = {}) => {
|
|
38
|
+
localStorage.setItem("temp-nuxt-auth-utils-popup", "true");
|
|
39
|
+
const width = size.width ?? 960;
|
|
40
|
+
const height = size.height ?? 600;
|
|
41
|
+
const top = (window.top?.outerHeight ?? 0) / 2 + (window.top?.screenY ?? 0) - height / 2;
|
|
42
|
+
const left = (window.top?.outerWidth ?? 0) / 2 + (window.top?.screenX ?? 0) - width / 2;
|
|
43
|
+
window.open(
|
|
44
|
+
route,
|
|
45
|
+
"nuxt-auth-utils-popup",
|
|
46
|
+
`width=${width}, height=${height}, top=${top}, left=${left}, toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no`
|
|
47
|
+
);
|
|
48
|
+
window.addEventListener("storage", popupListener);
|
|
49
|
+
};
|
|
31
50
|
return {
|
|
32
51
|
ready: computed(() => authReadyState.value),
|
|
33
52
|
loggedIn: computed(() => Boolean(sessionState.value.user)),
|
|
34
53
|
user: computed(() => sessionState.value.user || null),
|
|
35
54
|
session: sessionState,
|
|
36
55
|
fetch,
|
|
56
|
+
openInPopup,
|
|
37
57
|
clear
|
|
38
58
|
};
|
|
39
59
|
}
|
|
@@ -7,4 +7,9 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|
|
7
7
|
await useUserSession().fetch();
|
|
8
8
|
});
|
|
9
9
|
}
|
|
10
|
+
if (localStorage.getItem("temp-nuxt-auth-utils-popup")) {
|
|
11
|
+
localStorage.removeItem("temp-nuxt-auth-utils-popup");
|
|
12
|
+
const error = useError();
|
|
13
|
+
if (!error.value) window.close();
|
|
14
|
+
}
|
|
10
15
|
});
|
|
@@ -0,0 +1,39 @@
|
|
|
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 event;
|
|
33
|
+
private readonly sessionKey;
|
|
34
|
+
constructor(event: H3Event);
|
|
35
|
+
get(): Promise<NodeSavedSession | undefined>;
|
|
36
|
+
set(key: string, val: NodeSavedSession): Promise<void>;
|
|
37
|
+
del(): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
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(event);
|
|
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();
|
|
51
|
+
const profile = scopes.includes("transition:generic") ? (await new Agent(session).getProfile({ actor: session.did })).data : null;
|
|
52
|
+
sessionStore.del();
|
|
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:stat";
|
|
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
|
+
}
|
|
81
|
+
async del() {
|
|
82
|
+
deleteCookie(this.event, this.stateKey);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export class SessionStore {
|
|
86
|
+
constructor(event) {
|
|
87
|
+
this.event = event;
|
|
88
|
+
}
|
|
89
|
+
sessionKey = "oauth:bluesky:session";
|
|
90
|
+
async get() {
|
|
91
|
+
const result = getCookie(this.event, this.sessionKey);
|
|
92
|
+
if (!result) return;
|
|
93
|
+
return JSON.parse(atob(result));
|
|
94
|
+
}
|
|
95
|
+
async set(key, val) {
|
|
96
|
+
setCookie(this.event, this.sessionKey, btoa(JSON.stringify(val)));
|
|
97
|
+
}
|
|
98
|
+
async del() {
|
|
99
|
+
deleteCookie(this.event, this.sessionKey);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { OAuthConfig } from '#auth-utils';
|
|
2
|
+
export interface OAuthGiteaConfig {
|
|
3
|
+
/**
|
|
4
|
+
* Gitea OAuth Client ID
|
|
5
|
+
* @default process.env.NUXT_OAUTH_GITEA_CLIENT_ID
|
|
6
|
+
*/
|
|
7
|
+
clientId?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Gitea OAuth Client Secret
|
|
10
|
+
* @default process.env.NUXT_OAUTH_GITEA_CLIENT_SECRET
|
|
11
|
+
*/
|
|
12
|
+
clientSecret?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Gitea OAuth Scope
|
|
15
|
+
* @default ['read:user']
|
|
16
|
+
* @see https://docs.gitea.io/en-us/oauth2-provider/
|
|
17
|
+
* @example ['read:user']
|
|
18
|
+
*/
|
|
19
|
+
scope?: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Require email from user, adds the ['email'] scope if not present
|
|
22
|
+
* @default false
|
|
23
|
+
*/
|
|
24
|
+
emailRequired?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Gitea OAuth Authorization URL
|
|
27
|
+
* @default '/login/oauth/authorize'
|
|
28
|
+
*/
|
|
29
|
+
authorizationURL?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Gitea OAuth Token URL
|
|
32
|
+
* @default '/login/oauth/access_token'
|
|
33
|
+
*/
|
|
34
|
+
tokenURL?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Extra authorization parameters to provide to the authorization URL
|
|
37
|
+
* @see https://docs.gitea.io/en-us/oauth2-provider/
|
|
38
|
+
*/
|
|
39
|
+
authorizationParams?: Record<string, string>;
|
|
40
|
+
/**
|
|
41
|
+
* Redirect URL to to allow overriding for situations like prod failing to determine public hostname
|
|
42
|
+
* @default process.env.NUXT_OAUTH_GITEA_REDIRECT_URL
|
|
43
|
+
*/
|
|
44
|
+
redirectURL?: string;
|
|
45
|
+
/**
|
|
46
|
+
* URL of your Gitea instance
|
|
47
|
+
* @default 'https://gitea.com'
|
|
48
|
+
*/
|
|
49
|
+
baseURL?: string;
|
|
50
|
+
}
|
|
51
|
+
export declare function defineOAuthGiteaEventHandler({ config, onSuccess, onError, }: OAuthConfig<OAuthGiteaConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { eventHandler, getQuery, sendRedirect } from "h3";
|
|
2
|
+
import { withQuery } from "ufo";
|
|
3
|
+
import { defu } from "defu";
|
|
4
|
+
import {
|
|
5
|
+
handleMissingConfiguration,
|
|
6
|
+
handleAccessTokenErrorResponse,
|
|
7
|
+
getOAuthRedirectURL,
|
|
8
|
+
requestAccessToken
|
|
9
|
+
} from "../utils.js";
|
|
10
|
+
import { useRuntimeConfig, createError } from "#imports";
|
|
11
|
+
export function defineOAuthGiteaEventHandler({
|
|
12
|
+
config,
|
|
13
|
+
onSuccess,
|
|
14
|
+
onError
|
|
15
|
+
}) {
|
|
16
|
+
return eventHandler(async (event) => {
|
|
17
|
+
const runtimeConfig = useRuntimeConfig(event).oauth?.gitea;
|
|
18
|
+
const baseURL = config?.baseURL ?? runtimeConfig.baseURL ?? "https://gitea.com";
|
|
19
|
+
config = defu(config, runtimeConfig, {
|
|
20
|
+
authorizationURL: `${baseURL}/login/oauth/authorize`,
|
|
21
|
+
tokenURL: `${baseURL}/login/oauth/access_token`,
|
|
22
|
+
authorizationParams: {}
|
|
23
|
+
});
|
|
24
|
+
const query = getQuery(event);
|
|
25
|
+
if (query.error) {
|
|
26
|
+
const error = createError({
|
|
27
|
+
statusCode: 401,
|
|
28
|
+
message: `Gitea login failed: ${query.error || "Unknown error"}`,
|
|
29
|
+
data: query
|
|
30
|
+
});
|
|
31
|
+
if (!onError) throw error;
|
|
32
|
+
return onError(event, error);
|
|
33
|
+
}
|
|
34
|
+
if (!config.clientId || !config.clientSecret) {
|
|
35
|
+
return handleMissingConfiguration(
|
|
36
|
+
event,
|
|
37
|
+
"gitea",
|
|
38
|
+
["clientId", "clientSecret"],
|
|
39
|
+
onError
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
43
|
+
if (!query.code) {
|
|
44
|
+
config.scope = config.scope || [];
|
|
45
|
+
if (!config.scope.length) {
|
|
46
|
+
config.scope.push("read:user");
|
|
47
|
+
}
|
|
48
|
+
if (config.emailRequired && !config.scope.includes("email")) {
|
|
49
|
+
config.scope.push("email");
|
|
50
|
+
}
|
|
51
|
+
return sendRedirect(
|
|
52
|
+
event,
|
|
53
|
+
withQuery(config.authorizationURL, {
|
|
54
|
+
response_type: "code",
|
|
55
|
+
client_id: config.clientId,
|
|
56
|
+
redirect_uri: redirectURL,
|
|
57
|
+
scope: config.scope.join(" "),
|
|
58
|
+
...config.authorizationParams
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
const tokens = await requestAccessToken(config.tokenURL, {
|
|
63
|
+
body: {
|
|
64
|
+
grant_type: "authorization_code",
|
|
65
|
+
client_id: config.clientId,
|
|
66
|
+
client_secret: config.clientSecret,
|
|
67
|
+
redirect_uri: redirectURL,
|
|
68
|
+
code: query.code
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
if (tokens.error) {
|
|
72
|
+
return handleAccessTokenErrorResponse(event, "gitea", tokens, onError);
|
|
73
|
+
}
|
|
74
|
+
const accessToken = tokens.access_token;
|
|
75
|
+
const user = await $fetch(`${baseURL}/api/v1/user`, {
|
|
76
|
+
headers: {
|
|
77
|
+
Authorization: `token ${accessToken}`
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
return onSuccess(event, {
|
|
81
|
+
user,
|
|
82
|
+
tokens
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
@@ -16,14 +16,15 @@ export function defineOAuthMicrosoftEventHandler({ config, onSuccess, onError })
|
|
|
16
16
|
const tokenURL = config.tokenURL || `https://login.microsoftonline.com/${config.tenant}/oauth2/v2.0/token`;
|
|
17
17
|
const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
|
|
18
18
|
if (!query.code) {
|
|
19
|
-
|
|
19
|
+
config.scope = config.scope && config.scope.length > 0 ? config.scope : ["User.Read"];
|
|
20
|
+
config.scope = [...new Set(config.scope)];
|
|
20
21
|
return sendRedirect(
|
|
21
22
|
event,
|
|
22
23
|
withQuery(authorizationURL, {
|
|
23
24
|
client_id: config.clientId,
|
|
24
25
|
response_type: "code",
|
|
25
26
|
redirect_uri: redirectURL,
|
|
26
|
-
scope: scope.join(" "),
|
|
27
|
+
scope: config.scope.join(" "),
|
|
27
28
|
...config.authorizationParams
|
|
28
29
|
})
|
|
29
30
|
);
|
|
@@ -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<
|
|
23
|
+
export declare function getUserSession(event: UseSessionEvent): Promise<{
|
|
24
|
+
id: string;
|
|
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
|
-
|
|
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
|
|
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
|
*/
|
|
@@ -45,4 +49,11 @@ export interface UserSessionComposable {
|
|
|
45
49
|
* Clear the user session and remove the session cookie.
|
|
46
50
|
*/
|
|
47
51
|
clear: () => Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Open the OAuth route in a popup that auto-closes when successful.
|
|
54
|
+
*/
|
|
55
|
+
openInPopup: (route: string, size?: {
|
|
56
|
+
width?: number;
|
|
57
|
+
height?: number;
|
|
58
|
+
}) => void;
|
|
48
59
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-auth-utils",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.12",
|
|
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.
|
|
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.
|
|
65
|
-
"@nuxt/devtools": "
|
|
66
|
-
"@nuxt/eslint-config": "^0.7.
|
|
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.
|
|
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.
|
|
75
|
-
"nuxt": "^3.15.
|
|
82
|
+
"eslint": "^9.19.0",
|
|
83
|
+
"nuxt": "^3.15.4",
|
|
76
84
|
"typescript": "5.6.3",
|
|
77
|
-
"vitest": "^3.0.
|
|
85
|
+
"vitest": "^3.0.5",
|
|
78
86
|
"vue-tsc": "^2.2.0"
|
|
79
87
|
}
|
|
80
88
|
}
|