nuxt-auth-utils 0.5.10 → 0.5.11

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
@@ -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
 
@@ -216,6 +222,7 @@ It can also be set using environment variables:
216
222
  - Facebook
217
223
  - GitHub
218
224
  - GitLab
225
+ - Gitea
219
226
  - Google
220
227
  - Hubspot
221
228
  - Instagram
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "auth-utils",
3
3
  "configKey": "auth",
4
- "version": "0.5.10",
4
+ "version": "0.5.11",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "0.8.4",
7
7
  "unbuild": "2.0.0"
package/dist/module.mjs CHANGED
@@ -97,6 +97,12 @@ const module = defineNuxtModule({
97
97
  authenticate: {}
98
98
  });
99
99
  runtimeConfig.oauth = defu(runtimeConfig.oauth, {});
100
+ runtimeConfig.oauth.gitea = defu(runtimeConfig.oauth.gitea, {
101
+ clientId: "",
102
+ clientSecret: "",
103
+ redirectURL: "",
104
+ baseURL: ""
105
+ });
100
106
  runtimeConfig.oauth.github = defu(runtimeConfig.oauth.github, {
101
107
  clientId: "",
102
108
  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,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
+ }
@@ -76,6 +76,7 @@ export function defineOAuthGitHubEventHandler({ config, onSuccess, onError }) {
76
76
  return onError(event, error);
77
77
  }
78
78
  user.email = primaryEmail.email;
79
+ user.email_verified = primaryEmail.verified;
79
80
  }
80
81
  return onSuccess(event, {
81
82
  user,
@@ -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
- const scope = config.scope && config.scope.length > 0 ? config.scope : ["User.Read"];
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
  );
@@ -1,5 +1,5 @@
1
1
  import type { H3Event, H3Error } from 'h3';
2
- export type OAuthProvider = 'atlassian' | 'auth0' | 'authentik' | 'battledotnet' | 'cognito' | 'discord' | 'dropbox' | 'facebook' | '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 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 & {});
3
3
  export type OnError = (event: H3Event, error: H3Error) => Promise<void> | void;
4
4
  export interface OAuthConfig<TConfig, TUser = any, TTokens = any> {
5
5
  config?: TConfig;
@@ -45,4 +45,11 @@ export interface UserSessionComposable {
45
45
  * Clear the user session and remove the session cookie.
46
46
  */
47
47
  clear: () => Promise<void>;
48
+ /**
49
+ * Open the OAuth route in a popup that auto-closes when successful.
50
+ */
51
+ openInPopup: (route: string, size?: {
52
+ width?: number;
53
+ height?: number;
54
+ }) => void;
48
55
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-auth-utils",
3
- "version": "0.5.10",
3
+ "version": "0.5.11",
4
4
  "description": "Add Authentication to Nuxt applications with secured & sealed cookies sessions.",
5
5
  "repository": {
6
6
  "type": "git",