nuxt-auth-utils 0.5.27 → 0.5.29

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
@@ -219,6 +219,7 @@ It can also be set using environment variables:
219
219
  - Azure B2C
220
220
  - Battle.net
221
221
  - Bluesky (AT Protocol)
222
+ - Box.com
222
223
  - Discord
223
224
  - Dropbox
224
225
  - Facebook
@@ -239,8 +240,10 @@ It can also be set using environment variables:
239
240
  - OIDC / OpenID Connect (Generic)
240
241
  - Okta
241
242
  - Ory
243
+ - osu!
242
244
  - PayPal
243
245
  - Polar
246
+ - Riot Games
244
247
  - Salesforce
245
248
  - Seznam
246
249
  - Shopify Customer
@@ -303,7 +306,7 @@ if (await verifyPassword(hashedPassword, 'user_password')) {
303
306
  It also provides a `passwordNeedsRehash` function to check if a password needs to be rehashed. This is useful when the hash settings are changed, such as as increasing the scrypt cost parameters.
304
307
 
305
308
  ```ts
306
- const needsRehash = await passwordNeedsRehash(hashedPassword)
309
+ const needsRehash = passwordNeedsRehash(hashedPassword)
307
310
 
308
311
  if (needsRehash) {
309
312
  // Password needs to be rehashed
@@ -449,6 +452,8 @@ export default defineWebAuthnRegisterEventHandler({
449
452
  },
450
453
  })
451
454
  ```
455
+ > [!TIP]
456
+ > If you want to plug in and use the [Community sourced list of AAGUIDS](https://github.com/passkeydeveloper/passkey-authenticator-aaguids) to decorate your display of added passkeys, you can access the the ID via `credential.aaguid` inside `onSuccess`
452
457
 
453
458
  ```ts
454
459
  // server/api/webauthn/authenticate.post.ts
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "auth-utils",
3
3
  "configKey": "auth",
4
- "version": "0.5.27",
4
+ "version": "0.5.29",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -115,6 +115,12 @@ const module$1 = defineNuxtModule({
115
115
  redirectURL: "",
116
116
  baseURL: ""
117
117
  });
118
+ runtimeConfig.oauth.box = defu(runtimeConfig.oauth.box, {
119
+ clientId: "",
120
+ clientSecret: "",
121
+ redirectURL: "",
122
+ scope: []
123
+ });
118
124
  runtimeConfig.oauth.github = defu(runtimeConfig.oauth.github, {
119
125
  clientId: "",
120
126
  clientSecret: "",
@@ -400,6 +406,18 @@ const module$1 = defineNuxtModule({
400
406
  redirectURL: "",
401
407
  scope: []
402
408
  });
409
+ runtimeConfig.oauth.osu = defu(runtimeConfig.oauth.osu, {
410
+ clientId: "",
411
+ clientSecret: "",
412
+ redirectURL: "",
413
+ scope: []
414
+ });
415
+ runtimeConfig.oauth.riotgames = defu(runtimeConfig.oauth.riotgames, {
416
+ clientId: "",
417
+ clientSecret: "",
418
+ redirectURL: "",
419
+ scope: []
420
+ });
403
421
  }
404
422
  });
405
423
 
@@ -0,0 +1,95 @@
1
+ import type { OAuthConfig } from '#auth-utils';
2
+ export interface OAuthBoxConfig {
3
+ /**
4
+ * Box OAuth Client ID
5
+ * @default process.env.NUXT_OAUTH_BOX_CLIENT_ID
6
+ */
7
+ clientId?: string;
8
+ /**
9
+ * Box OAuth Client Secret
10
+ * @default process.env.NUXT_OAUTH_BOX_CLIENT_SECRET
11
+ */
12
+ clientSecret?: string;
13
+ /**
14
+ * Box OAuth Scope
15
+ * @default []
16
+ * @see https://developer.box.com/guides/api-calls/permissions-and-errors/scopes/
17
+ */
18
+ scope?: string[];
19
+ /**
20
+ * Box OAuth Authorization URL
21
+ * @default 'https://account.box.com/api/oauth2/authorize'
22
+ */
23
+ authorizationURL?: string;
24
+ /**
25
+ * Box OAuth Token URL
26
+ * @default 'https://api.box.com/oauth2/token'
27
+ */
28
+ tokenURL?: string;
29
+ /**
30
+ * Box User Info URL
31
+ * @default 'https://api.box.com/2.0/users/me'
32
+ */
33
+ userURL?: string;
34
+ /**
35
+ * Extra authorization parameters to provide to the authorization URL
36
+ * @see https://developer.box.com/guides/authentication/oauth2/oauth2-setup/
37
+ */
38
+ authorizationParams?: Record<string, string>;
39
+ /**
40
+ * Redirect URL to allow overriding for situations like prod failing to determine public hostname
41
+ * @default process.env.NUXT_OAUTH_BOX_REDIRECT_URL
42
+ */
43
+ redirectURL?: string;
44
+ }
45
+ /**
46
+ * Box User object returned from /2.0/users/me
47
+ * @see https://developer.box.com/reference/get-users-me/
48
+ * @see https://www.postman.com/boxdev/box-s-public-workspace/example/8119550-f7344611-7834-4040-a4ae-d6b3ef95bfdb
49
+ */
50
+ interface OAuthBoxUser {
51
+ type: 'user';
52
+ id: string;
53
+ name: string;
54
+ login: string;
55
+ created_at: string;
56
+ modified_at: string;
57
+ language: string;
58
+ timezone: string;
59
+ space_amount: number;
60
+ space_used: number;
61
+ max_upload_size: number;
62
+ status: 'active' | 'inactive' | 'cannot_delete_edit' | 'cannot_delete_edit_upload';
63
+ job_title?: string;
64
+ phone?: string;
65
+ address?: string;
66
+ avatar_url?: string;
67
+ }
68
+ /**
69
+ * Box OAuth tokens
70
+ * @see https://developer.box.com/reference/post-oauth2-token/
71
+ * @see https://www.postman.com/boxdev/box-s-public-workspace/example/8119550-70a8e5bd-4d25-494c-be2c-5409db9d1ace
72
+ */
73
+ interface OAuthBoxTokens {
74
+ access_token: string;
75
+ refresh_token: string;
76
+ expires_in: number;
77
+ token_type: 'bearer';
78
+ restricted_to?: Array<{
79
+ scope: string;
80
+ object?: {
81
+ type: string;
82
+ id: string;
83
+ };
84
+ }>;
85
+ }
86
+ /**
87
+ * Define an OAuth event handler for Box authentication.
88
+ * @see https://developer.box.com/guides/authentication/oauth2/
89
+ * @see https://www.postman.com/boxdev/box-s-public-workspace/collection/trhp912/box-platform-api
90
+ */
91
+ export declare function defineOAuthBoxEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthBoxConfig, {
92
+ user: OAuthBoxUser;
93
+ tokens: OAuthBoxTokens;
94
+ }>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
95
+ export {};
@@ -0,0 +1,69 @@
1
+ import { eventHandler, getQuery, sendRedirect, createError } from "h3";
2
+ import { withQuery } from "ufo";
3
+ import { defu } from "defu";
4
+ import { getOAuthRedirectURL, handleAccessTokenErrorResponse, handleInvalidState, handleMissingConfiguration, handleState, requestAccessToken } from "../utils.js";
5
+ import { useRuntimeConfig } from "#imports";
6
+ export function defineOAuthBoxEventHandler({ config, onSuccess, onError }) {
7
+ return eventHandler(async (event) => {
8
+ config = defu(config, useRuntimeConfig(event).oauth?.box, {
9
+ authorizationURL: "https://account.box.com/api/oauth2/authorize",
10
+ tokenURL: "https://api.box.com/oauth2/token",
11
+ userURL: "https://api.box.com/2.0/users/me",
12
+ authorizationParams: {}
13
+ });
14
+ const query = getQuery(event);
15
+ if (query.error) {
16
+ const errorMessageParts = [query.error, query.error_description].filter(Boolean).join(": ");
17
+ const error = createError({
18
+ statusCode: 401,
19
+ message: `Box login failed: ${errorMessageParts || "Unknown error"}`,
20
+ data: query
21
+ });
22
+ if (!onError) throw error;
23
+ return onError(event, error);
24
+ }
25
+ if (!config.clientId || !config.clientSecret) {
26
+ return handleMissingConfiguration(event, "box", ["clientId", "clientSecret"], onError);
27
+ }
28
+ const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
29
+ const state = await handleState(event);
30
+ if (!query.code) {
31
+ const scope = config.scope || [];
32
+ return sendRedirect(
33
+ event,
34
+ withQuery(config.authorizationURL, {
35
+ response_type: "code",
36
+ client_id: config.clientId,
37
+ redirect_uri: redirectURL,
38
+ scope: scope.join(" "),
39
+ state,
40
+ ...config.authorizationParams
41
+ })
42
+ );
43
+ }
44
+ if (query.state !== state) {
45
+ return handleInvalidState(event, "box", onError);
46
+ }
47
+ const tokens = await requestAccessToken(config.tokenURL, {
48
+ body: {
49
+ grant_type: "authorization_code",
50
+ client_id: config.clientId,
51
+ client_secret: config.clientSecret,
52
+ redirect_uri: redirectURL,
53
+ code: query.code
54
+ }
55
+ });
56
+ if (tokens.error) {
57
+ return handleAccessTokenErrorResponse(event, "box", tokens, onError);
58
+ }
59
+ const user = await $fetch(config.userURL, {
60
+ headers: {
61
+ Authorization: `Bearer ${tokens.access_token}`
62
+ }
63
+ });
64
+ return onSuccess(event, {
65
+ user,
66
+ tokens
67
+ });
68
+ });
69
+ }
@@ -56,6 +56,8 @@ export function defineOAuthKeycloakEventHandler({
56
56
  redirect_uri: redirectURL,
57
57
  code: query.code
58
58
  }
59
+ }).catch((error) => {
60
+ return handleAccessTokenErrorResponse(event, "keycloak", error?.data || error, onError);
59
61
  });
60
62
  if (tokens.error) {
61
63
  return handleAccessTokenErrorResponse(event, "keycloak", tokens, onError);
@@ -35,4 +35,21 @@ export interface OAuthKickConfig {
35
35
  */
36
36
  redirectURL?: string;
37
37
  }
38
- export declare function defineOAuthKickEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthKickConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
38
+ interface KickUser {
39
+ email: string;
40
+ name: string;
41
+ profile_picture: string;
42
+ user_id: string;
43
+ }
44
+ interface KickTokens {
45
+ access_token: string;
46
+ expires_in: number;
47
+ refresh_token: string;
48
+ scope: string;
49
+ token_type: string;
50
+ }
51
+ export declare function defineOAuthKickEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthKickConfig, {
52
+ user: KickUser;
53
+ tokens: KickTokens;
54
+ }>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
55
+ export {};
@@ -8,7 +8,6 @@ export interface OAuthOidcConfig {
8
8
  clientId?: string;
9
9
  /**
10
10
  * OAuth Client secret.
11
- * If unset, PKCE will be used where no client secret is needed.
12
11
  *
13
12
  * @default process.env.NUXT_OAUTH_OIDC_CLIENT_SECRET
14
13
  */
@@ -2,7 +2,7 @@ import { useRuntimeConfig } from "#imports";
2
2
  import { defu } from "defu";
3
3
  import { createError, eventHandler, getQuery, sendRedirect } from "h3";
4
4
  import { withQuery } from "ufo";
5
- import { getOAuthRedirectURL, handleAccessTokenErrorResponse, handleInvalidState, handleMissingConfiguration, handlePkceVerifier, handleState, requestAccessToken } from "../utils.js";
5
+ import { getOAuthRedirectURL, handleAccessTokenErrorResponse, handleInvalidState, handleMissingConfiguration, handlePkceVerifier, handleState, requestAccessToken, handleNonce, parseJwt } from "../utils.js";
6
6
  export function defineOAuthOidcEventHandler({ config, onSuccess, onError }) {
7
7
  return eventHandler(async (event) => {
8
8
  config = defu(config, useRuntimeConfig(event).oauth.oidc, {
@@ -24,7 +24,8 @@ export function defineOAuthOidcEventHandler({ config, onSuccess, onError }) {
24
24
  const oidcConfig = typeof config.openidConfig === "string" ? await $fetch(config.openidConfig) : config.openidConfig;
25
25
  const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
26
26
  const state = await handleState(event);
27
- const verifier = !config.clientSecret ? await handlePkceVerifier(event) : void 0;
27
+ const verifier = await handlePkceVerifier(event);
28
+ const nonce = handleNonce(event);
28
29
  if (!query.code) {
29
30
  config.scope = config.scope || [];
30
31
  const authQuery = {
@@ -32,13 +33,12 @@ export function defineOAuthOidcEventHandler({ config, onSuccess, onError }) {
32
33
  redirect_uri: redirectURL,
33
34
  scope: config.scope.join(" "),
34
35
  state,
36
+ nonce,
35
37
  response_type: "code",
36
38
  ...config.params?.authorization_endpoint
37
39
  };
38
- if (verifier) {
39
- authQuery.code_challenge = verifier.code_challenge;
40
- authQuery.code_challenge_method = verifier.code_challenge_method;
41
- }
40
+ authQuery.code_challenge = verifier.code_challenge;
41
+ authQuery.code_challenge_method = verifier.code_challenge_method;
42
42
  return sendRedirect(
43
43
  event,
44
44
  withQuery(oidcConfig.authorization_endpoint, authQuery)
@@ -53,17 +53,26 @@ export function defineOAuthOidcEventHandler({ config, onSuccess, onError }) {
53
53
  client_secret: config.clientSecret,
54
54
  redirect_uri: redirectURL,
55
55
  code: query.code,
56
+ code_verifier: verifier.code_verifier,
56
57
  ...config.params?.token_endpoint
57
58
  };
58
- if (verifier) {
59
- tokenQuery.code_verifier = verifier.code_verifier;
60
- }
61
59
  const tokens = await requestAccessToken(oidcConfig.token_endpoint, {
62
60
  body: tokenQuery
63
61
  });
64
62
  if (tokens.error) {
65
63
  return handleAccessTokenErrorResponse(event, "oidc", tokens, onError);
66
64
  }
65
+ if (tokens.id_token) {
66
+ const claims = parseJwt(tokens.id_token);
67
+ if (claims.nonce !== nonce) {
68
+ const error = createError({
69
+ statusCode: 401,
70
+ message: "OIDC login failed: nonce mismatch"
71
+ });
72
+ if (!onError) throw error;
73
+ return onError(event, error);
74
+ }
75
+ }
67
76
  let user = {};
68
77
  if (oidcConfig.userinfo_endpoint) {
69
78
  user = await $fetch(oidcConfig.userinfo_endpoint, {
@@ -0,0 +1,42 @@
1
+ import type { OAuthConfig } from '#auth-utils';
2
+ export interface OAuthOsuConfig {
3
+ /**
4
+ * osu! OAuth Client ID
5
+ * @default process.env.NUXT_OAUTH_OSU_CLIENT_ID
6
+ */
7
+ clientId?: string;
8
+ /**
9
+ * osu! OAuth Client Secret
10
+ * @default process.env.NUXT_OAUTH_OSU_CLIENT_SECRET
11
+ */
12
+ clientSecret?: string;
13
+ /**
14
+ * osu! OAuth Scope
15
+ *
16
+ * The identify scope is always implicitly provided.
17
+ * @default []
18
+ * @see https://osu.ppy.sh/docs/#scopes
19
+ */
20
+ scope?: string[];
21
+ /**
22
+ * osu! OAuth Authorization URL
23
+ * @default 'https://osu.ppy.sh/oauth/authorize'
24
+ */
25
+ authorizationURL?: string;
26
+ /**
27
+ * osu! OAuth Token URL
28
+ * @default 'https://osu.ppy.sh/oauth/token'
29
+ */
30
+ tokenURL?: string;
31
+ /**
32
+ * Extra authorization parameters to provide to the authorization URL
33
+ * @see 'https://osu.ppy.sh/docs/#authorization-code-grant'
34
+ */
35
+ authorizationParams?: Record<string, string>;
36
+ /**
37
+ * Redirect URL to to allow overriding for situations like prod failing to determine public hostname
38
+ * @default process.env.NUXT_OAUTH_OSU_REDIRECT_URL or current URL
39
+ */
40
+ redirectURL?: string;
41
+ }
42
+ export declare function defineOAuthOsuEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthOsuConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
@@ -0,0 +1,63 @@
1
+ import { eventHandler, getQuery, sendRedirect } from "h3";
2
+ import { withQuery } from "ufo";
3
+ import { defu } from "defu";
4
+ import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken, handleState, handleInvalidState } from "../utils.js";
5
+ import { useRuntimeConfig } from "#imports";
6
+ export function defineOAuthOsuEventHandler({ config, onSuccess, onError }) {
7
+ return eventHandler(async (event) => {
8
+ config = defu(config, useRuntimeConfig(event).oauth?.osu, {
9
+ authorizationURL: "https://osu.ppy.sh/oauth/authorize",
10
+ tokenURL: "https://osu.ppy.sh/oauth/token",
11
+ authorizationParams: {}
12
+ });
13
+ const query = getQuery(event);
14
+ if (!config.clientId || !config.clientSecret) {
15
+ return handleMissingConfiguration(event, "osu", ["clientId", "clientSecret"], onError);
16
+ }
17
+ if (query.error) {
18
+ return handleAccessTokenErrorResponse(event, "osu", query, onError);
19
+ }
20
+ const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
21
+ const state = await handleState(event);
22
+ if (!query.code) {
23
+ config.scope = config.scope || [];
24
+ return sendRedirect(
25
+ event,
26
+ withQuery(config.authorizationURL, {
27
+ response_type: "code",
28
+ client_id: config.clientId,
29
+ redirect_uri: redirectURL,
30
+ scope: config.scope.join(" "),
31
+ state,
32
+ ...config.authorizationParams
33
+ })
34
+ );
35
+ }
36
+ if (query.state !== state) {
37
+ return handleInvalidState(event, "osu", onError);
38
+ }
39
+ const tokens = await requestAccessToken(config.tokenURL, {
40
+ body: {
41
+ client_id: config.clientId,
42
+ client_secret: config.clientSecret,
43
+ grant_type: "authorization_code",
44
+ redirect_uri: redirectURL,
45
+ code: query.code
46
+ }
47
+ });
48
+ if (tokens.error) {
49
+ return handleAccessTokenErrorResponse(event, "osu", tokens, onError);
50
+ }
51
+ const accessToken = tokens.access_token;
52
+ const user = await $fetch("https://osu.ppy.sh/api/v2/me", {
53
+ headers: {
54
+ "user-agent": "Nuxt Auth Utils",
55
+ "Authorization": `Bearer ${accessToken}`
56
+ }
57
+ });
58
+ return onSuccess(event, {
59
+ tokens,
60
+ user
61
+ });
62
+ });
63
+ }
@@ -0,0 +1,74 @@
1
+ import type { OAuthConfig } from '#auth-utils';
2
+ export interface OAuthRiotGamesConfig {
3
+ /**
4
+ * RiotGames OAuth Client ID
5
+ * @default process.env.NUXT_OAUTH_RIOTGAMES_CLIENT_ID
6
+ */
7
+ clientId?: string;
8
+ /**
9
+ * RiotGames OAuth Client Secret
10
+ * @default process.env.NUXT_OAUTH_RIOTGAMES_CLIENT_SECRET
11
+ */
12
+ clientSecret?: string;
13
+ /**
14
+ * RiotGames OAuth Scope
15
+ * @default []
16
+ * @see https://beta.developer.riotgames.com/sign-on
17
+ * @example ['cpid', 'offline_access']
18
+ */
19
+ scope?: string[];
20
+ /**
21
+ * RiotGames OAuth Authorization URL
22
+ * @default 'https://auth.riotgames.com/authorize'
23
+ */
24
+ authorizationURL?: string;
25
+ /**
26
+ * Riot OAuth Token URL
27
+ * @default 'https://auth.riotgames.com/token'
28
+ */
29
+ tokenURL?: string;
30
+ /**
31
+ * RiotGames API URL
32
+ * @default 'https://auth.riotgames.com'
33
+ */
34
+ apiURL?: string;
35
+ /**
36
+ * Extra authorization parameters to provide to the authorization URL
37
+ */
38
+ authorizationParams?: Record<string, string>;
39
+ /**
40
+ * Redirect URL to to allow overriding for situations like prod failing to determine public hostname
41
+ * @default process.env.NUXT_OAUTH_RIOTGAMES_REDIRECT_URL
42
+ */
43
+ redirectURL?: string;
44
+ /**
45
+ * API routing values for Accounts Endpoint. You can query for any account in any region.
46
+ * @default 'europe'
47
+ */
48
+ region?: 'americas' | 'europe' | 'asia';
49
+ }
50
+ interface RiotGamesUser extends RiotGamesAccount, RiotGamesUserInfo {
51
+ }
52
+ interface RiotGamesAccount {
53
+ puuid: string;
54
+ gameName: string;
55
+ tagLine: string;
56
+ }
57
+ interface RiotGamesUserInfo {
58
+ sub: string;
59
+ cpid?: string;
60
+ jti: string;
61
+ }
62
+ interface RiotGamesTokens {
63
+ access_token: string;
64
+ refresh_token: string;
65
+ id_token: string;
66
+ expires_in: number;
67
+ scope: string;
68
+ token_type: string;
69
+ }
70
+ export declare function defineOAuthRiotGamesEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthRiotGamesConfig, {
71
+ user: RiotGamesUser;
72
+ tokens: RiotGamesTokens;
73
+ }>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
74
+ export {};
@@ -0,0 +1,83 @@
1
+ import { createError, eventHandler, getQuery, sendRedirect } from "h3";
2
+ import { withQuery } from "ufo";
3
+ import { defu } from "defu";
4
+ import { getOAuthRedirectURL, handleAccessTokenErrorResponse, handleInvalidState, handleMissingConfiguration, handleState, requestAccessToken } from "../utils.js";
5
+ import { useRuntimeConfig } from "#imports";
6
+ export function defineOAuthRiotGamesEventHandler({ config, onSuccess, onError }) {
7
+ return eventHandler(async (event) => {
8
+ config = defu(config, useRuntimeConfig(event).oauth?.riotgames, {
9
+ authorizationURL: "https://auth.riotgames.com/authorize",
10
+ tokenURL: "https://auth.riotgames.com/token",
11
+ apiURL: "https://auth.riotgames.com",
12
+ region: "europe"
13
+ });
14
+ const query = getQuery(event);
15
+ if (query.error) {
16
+ const error = createError({
17
+ statusCode: 401,
18
+ message: `RiotGames login failed: ${query.error || "Unknown error"}`,
19
+ data: query
20
+ });
21
+ if (!onError) throw error;
22
+ return onError(event, error);
23
+ }
24
+ if (!config.clientId || !config.clientSecret) {
25
+ return handleMissingConfiguration(event, "riotgames", ["clientId", "clientSecret"], onError);
26
+ }
27
+ const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
28
+ const state = await handleState(event);
29
+ if (!query.code) {
30
+ config.scope = config.scope || [];
31
+ if (!config.scope.includes("openid")) {
32
+ config.scope.push("openid");
33
+ }
34
+ return sendRedirect(
35
+ event,
36
+ withQuery(config.authorizationURL, {
37
+ client_id: config.clientId,
38
+ redirect_uri: redirectURL,
39
+ response_type: "code",
40
+ scope: config.scope.join(" "),
41
+ state,
42
+ ...config.authorizationParams
43
+ })
44
+ );
45
+ }
46
+ if (query.state !== state) {
47
+ return handleInvalidState(event, "riotgames", onError);
48
+ }
49
+ const tokens = await requestAccessToken(config.tokenURL, {
50
+ body: {
51
+ grant_type: "authorization_code",
52
+ client_id: config.clientId,
53
+ client_secret: config.clientSecret,
54
+ redirect_uri: redirectURL,
55
+ code: query.code
56
+ }
57
+ });
58
+ if (tokens.error) {
59
+ return handleAccessTokenErrorResponse(event, "riotgames", tokens, onError);
60
+ }
61
+ const accessToken = tokens.access_token;
62
+ const [account, userInfo] = await Promise.all([
63
+ $fetch(`https://${config.region}.api.riotgames.com/riot/account/v1/accounts/me`, {
64
+ headers: {
65
+ Authorization: `Bearer ${accessToken}`
66
+ }
67
+ }),
68
+ $fetch(`${config.apiURL}/userinfo`, {
69
+ headers: {
70
+ Authorization: `Bearer ${accessToken}`
71
+ }
72
+ })
73
+ ]);
74
+ const user = {
75
+ ...account,
76
+ ...userInfo
77
+ };
78
+ return onSuccess(event, {
79
+ user,
80
+ tokens
81
+ });
82
+ });
83
+ }
@@ -44,4 +44,28 @@ export interface OAuthTwitchConfig {
44
44
  */
45
45
  redirectURL?: string;
46
46
  }
47
- export declare function defineOAuthTwitchEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthTwitchConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
47
+ interface TwitchUser {
48
+ id: string;
49
+ login: string;
50
+ display_name: string;
51
+ type: string;
52
+ broadcaster_type: string;
53
+ description: string;
54
+ profile_image_url: string;
55
+ offline_image_url: string;
56
+ view_count: number;
57
+ email?: string;
58
+ created_at: string;
59
+ }
60
+ interface TwitchTokens {
61
+ access_token: string;
62
+ expires_in: number;
63
+ refresh_token: string;
64
+ scope: string[];
65
+ token_type: string;
66
+ }
67
+ export declare function defineOAuthTwitchEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthTwitchConfig, {
68
+ user: TwitchUser;
69
+ tokens: TwitchTokens;
70
+ }>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
71
+ export {};
@@ -1,4 +1,5 @@
1
1
  import { type H3Event } from 'h3';
2
+ import * as jose from 'jose';
2
3
  import type { OAuthProvider, OnError } from '#auth-utils';
3
4
  export declare function getOAuthRedirectURL(event: H3Event): string;
4
5
  /**
@@ -70,4 +71,6 @@ export declare function handlePkceVerifier(event: H3Event): Promise<{
70
71
  code_challenge_method?: undefined;
71
72
  }>;
72
73
  export declare function handleState(event: H3Event): Promise<string | undefined>;
74
+ export declare function parseJwt(token: string): jose.JWTPayload;
75
+ export declare function handleNonce(event: H3Event): string | undefined;
73
76
  export {};
@@ -121,3 +121,23 @@ export async function handleState(event) {
121
121
  });
122
122
  return state;
123
123
  }
124
+ export function parseJwt(token) {
125
+ return jose.decodeJwt(token);
126
+ }
127
+ export function handleNonce(event) {
128
+ const query = getQuery(event);
129
+ if (query.code) {
130
+ const nonce2 = getCookie(event, "nuxt-auth-nonce");
131
+ deleteCookie(event, "nuxt-auth-nonce");
132
+ return nonce2;
133
+ }
134
+ const nonce = encodeBase64Url(getRandomBytes(8));
135
+ setCookie(event, "nuxt-auth-nonce", nonce, {
136
+ httpOnly: true,
137
+ secure: !isDevelopment,
138
+ sameSite: "lax",
139
+ maxAge: OAUTH_COOKIE_MAX_AGE,
140
+ path: "/"
141
+ });
142
+ return nonce;
143
+ }
@@ -1,6 +1,6 @@
1
1
  import type { H3Event, H3Error } from 'h3';
2
2
  export type ATProtoProvider = 'bluesky';
3
- export type OAuthProvider = ATProtoProvider | 'atlassian' | 'auth0' | 'authentik' | 'azureb2c' | 'battledotnet' | 'cognito' | 'discord' | 'dropbox' | 'facebook' | 'gitea' | 'github' | 'gitlab' | 'google' | 'hubspot' | 'instagram' | 'kick' | 'keycloak' | 'line' | 'linear' | 'linkedin' | 'microsoft' | 'paypal' | 'polar' | 'spotify' | 'seznam' | 'steam' | 'strava' | 'tiktok' | 'twitch' | 'vk' | 'workos' | 'x' | 'xsuaa' | 'yandex' | 'zitadel' | 'apple' | 'livechat' | 'salesforce' | 'slack' | 'heroku' | 'roblox' | 'okta' | 'ory' | 'shopifyCustomer' | 'oicd' | (string & {});
3
+ export type OAuthProvider = ATProtoProvider | 'atlassian' | 'auth0' | 'authentik' | 'azureb2c' | 'battledotnet' | 'cognito' | 'discord' | 'dropbox' | 'facebook' | 'gitea' | 'github' | 'gitlab' | 'google' | 'hubspot' | 'instagram' | 'kick' | 'keycloak' | 'line' | 'linear' | 'linkedin' | 'microsoft' | 'paypal' | 'polar' | 'spotify' | 'seznam' | 'steam' | 'strava' | 'tiktok' | 'twitch' | 'vk' | 'workos' | 'x' | 'xsuaa' | 'yandex' | 'zitadel' | 'apple' | 'livechat' | 'salesforce' | 'slack' | 'heroku' | 'roblox' | 'okta' | 'ory' | 'shopifyCustomer' | 'oidc' | 'osu' | 'riotgames' | 'box' | (string & {});
4
4
  export type OnError = (event: H3Event, error: H3Error) => Promise<void> | void;
5
5
  export interface OAuthConfig<TConfig, TResult = {
6
6
  user: any;
@@ -86,7 +86,7 @@ export interface WebAuthnComposable {
86
86
  * @param user.displayName - The display name to register, used for convenience and personalization and should not be relied upon as a secure identifier for authentication processes
87
87
  * @returns true if the registration was successful
88
88
  */
89
- register: <T extends WebAuthnUser>(data: T) => Promise<boolean>;
89
+ register: <T extends WebAuthnUser>(user: T) => Promise<boolean>;
90
90
  /**
91
91
  * Authenticate a credential
92
92
  * @param userName - The user name to authenticate, can be an email address, a username, or any other form of unique ID chosen by the user
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-auth-utils",
3
- "version": "0.5.27",
3
+ "version": "0.5.29",
4
4
  "description": "Add Authentication to Nuxt applications with secured & sealed cookies sessions.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "license": "MIT",
10
10
  "type": "module",
11
- "packageManager": "pnpm@10.28.0",
11
+ "packageManager": "pnpm@10.29.3",
12
12
  "exports": {
13
13
  ".": {
14
14
  "types": "./dist/types.d.mts",
@@ -40,13 +40,13 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@adonisjs/hash": "^9.1.1",
43
- "@nuxt/kit": "^4.2.2",
43
+ "@nuxt/kit": "^4.3.1",
44
44
  "defu": "^6.1.4",
45
45
  "h3": "^1.15.4",
46
46
  "hookable": "^6.0.1",
47
47
  "jose": "^6.1.3",
48
48
  "ofetch": "^1.5.1",
49
- "openid-client": "^6.8.1",
49
+ "openid-client": "^6.8.2",
50
50
  "pathe": "^2.0.3",
51
51
  "scule": "^1.3.0",
52
52
  "uncrypto": "^0.1.3"
@@ -72,20 +72,20 @@
72
72
  }
73
73
  },
74
74
  "devDependencies": {
75
- "@iconify-json/simple-icons": "^1.2.66",
76
- "@nuxt/devtools": "3.1.1",
77
- "@nuxt/eslint-config": "^1.12.1",
75
+ "@iconify-json/simple-icons": "^1.2.71",
76
+ "@nuxt/devtools": "3.2.1",
77
+ "@nuxt/eslint-config": "^1.15.1",
78
78
  "@nuxt/module-builder": "^1.0.2",
79
- "@nuxt/schema": "^4.2.2",
80
- "@nuxt/test-utils": "^3.23.0",
79
+ "@nuxt/schema": "^4.3.1",
80
+ "@nuxt/test-utils": "^4.0.0",
81
81
  "@types/node": "latest",
82
- "@nuxt/ui": "^4.3.0",
82
+ "@nuxt/ui": "^4.4.0",
83
83
  "@simplewebauthn/types": "^12.0.0",
84
84
  "changelogen": "^0.6.2",
85
- "eslint": "^9.39.2",
86
- "nuxt": "^4.2.2",
85
+ "eslint": "^10.0.0",
86
+ "nuxt": "^4.3.1",
87
87
  "typescript": "5.9.3",
88
- "vitest": "^4.0.17",
89
- "vue-tsc": "^3.2.2"
88
+ "vitest": "^4.0.18",
89
+ "vue-tsc": "^3.2.4"
90
90
  }
91
91
  }