From e44b7c726b4df8083636baecca6141cd3b3f885a Mon Sep 17 00:00:00 2001 From: Daniel Winkelmann Date: Sun, 14 May 2023 16:03:25 +0200 Subject: [PATCH 01/26] feat: add generic to "jsonPointerGet" --- src/runtime/composables/local/useAuth.ts | 2 +- src/runtime/helpers.ts | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/runtime/composables/local/useAuth.ts b/src/runtime/composables/local/useAuth.ts index 2054f8c6..634be7e1 100644 --- a/src/runtime/composables/local/useAuth.ts +++ b/src/runtime/composables/local/useAuth.ts @@ -25,7 +25,7 @@ const signIn: SignInFunc = async (credentials, signInOptions, params: signInParams ?? {} }) - const extractedToken = jsonPointerGet(response, config.token.signInResponseTokenPointer) + const extractedToken = jsonPointerGet(response, config.token.signInResponseTokenPointer) if (typeof extractedToken !== 'string') { console.error(`Auth: string token expected, received instead: ${JSON.stringify(extractedToken)}. Tried to find token at ${config.token.signInResponseTokenPointer} in ${JSON.stringify(response)}`) return diff --git a/src/runtime/helpers.ts b/src/runtime/helpers.ts index 03bf1457..0d39c392 100644 --- a/src/runtime/helpers.ts +++ b/src/runtime/helpers.ts @@ -44,7 +44,8 @@ export const useTypedBackendConfig = (runtimeC * @param obj * @param pointer */ -export const jsonPointerGet = (obj: Record, pointer: string): string | Record => { +export const jsonPointerGet = (obj: T, pointer: string): TResult => { + let result: TResult = obj as unknown as TResult const unescape = (str: string) => str.replace(/~1/g, '/').replace(/~0/g, '~') const parse = (pointer: string) => { if (pointer === '') { return [] } @@ -56,10 +57,11 @@ export const jsonPointerGet = (obj: Record, pointer: string): strin for (let i = 0; i < refTokens.length; ++i) { const tok = refTokens[i] - if (!(typeof obj === 'object' && tok in obj)) { + if (!(typeof result === 'object' && result && tok in result)) { throw new Error('Invalid reference token: ' + tok) } - obj = obj[tok] + result = (result as any)[tok] } - return obj + + return result } From adfb6b13bbbec68c4d48ea92f47c691ed310c50c Mon Sep 17 00:00:00 2001 From: Daniel Winkelmann Date: Sun, 14 May 2023 16:29:06 +0200 Subject: [PATCH 02/26] feat: implement the sessionDataJsonPointer --- playground-local/nuxt.config.ts | 3 ++- playground-local/server/api/auth/login.post.ts | 4 ++-- src/module.ts | 3 ++- src/runtime/composables/local/useAuth.ts | 7 ++++--- src/runtime/helpers.ts | 5 ++++- src/runtime/types.ts | 17 ++++++++++++++--- 6 files changed, 28 insertions(+), 11 deletions(-) diff --git a/playground-local/nuxt.config.ts b/playground-local/nuxt.config.ts index 3c990693..2d99367d 100644 --- a/playground-local/nuxt.config.ts +++ b/playground-local/nuxt.config.ts @@ -15,7 +15,8 @@ export default defineNuxtConfig({ token: { signInResponseTokenPointer: '/token/accessToken' }, - sessionDataType: { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" } + sessionDataType: { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" }, + sessionDataResponseTokenPointer: '/data/user' }, globalAppMiddleware: { isEnabled: true diff --git a/playground-local/server/api/auth/login.post.ts b/playground-local/server/api/auth/login.post.ts index 5fa9a0e8..c7ecd303 100644 --- a/playground-local/server/api/auth/login.post.ts +++ b/playground-local/server/api/auth/login.post.ts @@ -19,10 +19,10 @@ export default eventHandler(async (event) => { name: 'User ' + username } - const accessToken = sign({ ...user, scope: ['test', 'user'] }, SECRET, { expiresIn }) + const accessToken = sign({ data: { user }, scope: ['test', 'user'] }, SECRET, { expiresIn }) refreshTokens[refreshToken] = { accessToken, - user + data: { user } } return { diff --git a/src/module.ts b/src/module.ts index 2b5e7b97..e7292976 100644 --- a/src/module.ts +++ b/src/module.ts @@ -37,7 +37,8 @@ const defaultsByBackend: { [key in SupportedAuthProviders]: DeepRequired = async (credentials, signInOptions, params: signInParams ?? {} }) - const extractedToken = jsonPointerGet(response, config.token.signInResponseTokenPointer) + const extractedToken = extractObjectWithJsonPointer(response, config.token.signInResponseTokenPointer) if (typeof extractedToken !== 'string') { console.error(`Auth: string token expected, received instead: ${JSON.stringify(extractedToken)}. Tried to find token at ${config.token.signInResponseTokenPointer} in ${JSON.stringify(response)}`) return @@ -80,7 +80,8 @@ const getSession: GetSessionFunc = async (getSessionO loading.value = true try { - data.value = await _fetch(nuxt, path, { method, headers }) + const result = await _fetch(nuxt, path, { method, headers }) + data.value = extractObjectWithJsonPointer(result, config.sessionDataResponseTokenPointer) } catch { // Clear all data: Request failed so we must not be authenticated data.value = null diff --git a/src/runtime/helpers.ts b/src/runtime/helpers.ts index 0d39c392..f00f1b80 100644 --- a/src/runtime/helpers.ts +++ b/src/runtime/helpers.ts @@ -44,8 +44,11 @@ export const useTypedBackendConfig = (runtimeC * @param obj * @param pointer */ -export const jsonPointerGet = (obj: T, pointer: string): TResult => { +export const extractObjectWithJsonPointer = (obj: T, pointer?: string): TResult => { let result: TResult = obj as unknown as TResult + if (!pointer || pointer === '/') { + return result + } const unescape = (str: string) => str.replace(/~1/g, '/').replace(/~0/g, '~') const parse = (pointer: string) => { if (pointer === '') { return [] } diff --git a/src/runtime/types.ts b/src/runtime/types.ts index 4d17dfa6..17bf20ff 100644 --- a/src/runtime/types.ts +++ b/src/runtime/types.ts @@ -15,7 +15,7 @@ interface GlobalMiddlewareOptions { isEnabled: boolean /** * Whether to enforce authentication if the target-route does not exist. Per default the middleware redirects - * to Nuxts' default 404 page instead of forcing a sign-in if the target does not exist. This is to avoid a + * to Nuxtjs default 404 page instead of forcing a sign-in if the target does not exist. This is to avoid a * user-experience and developer-experience of having to sign-in only to see a 404 page afterwards. * * Note: Setting this to `false` this may lead to `vue-router` + node related warnings like: "Error [ERR_HTTP_HEADERS_SENT] ...", @@ -133,7 +133,7 @@ type ProviderLocal = { * Header name to be used in requests that need to be authenticated, e.g., to be used in the `getSession` request. * * @default Authorization - * @exmaple Auth + * @example Auth */ headerName?: string, /** @@ -151,8 +151,19 @@ type ProviderLocal = { * @advanced_array_example { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" } */ sessionDataType?: SessionDataObject, + /** + * How to extract the authentication-token from the sign-in response. + * + * E.g., setting this to `/data/user` and returning an object like `{ data: { user: { id:number, name: string } }, status: 'ok' }` from the `getSession` endpoint will + * storing the 'User' object typed as the type created via the 'sessionDataType' prop. + * + * This follows the JSON Pointer standard, see it's RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 + * + * @default / Access the root of the session response object + * @example /data/user Access the `data/user` property of the session response object + */ + sessionDataResponseTokenPointer?: string } - /** * Configuration for the `authjs`-provider. */ From ad8231defe1cceb2c5336c1038e415aadd754287 Mon Sep 17 00:00:00 2001 From: Daniel Winkelmann Date: Sun, 14 May 2023 16:30:48 +0200 Subject: [PATCH 03/26] feat: add to docs --- docs/content/v0.6/2.configuration/2.nuxt-config.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/content/v0.6/2.configuration/2.nuxt-config.md b/docs/content/v0.6/2.configuration/2.nuxt-config.md index 5c03d248..bd0f1fe6 100644 --- a/docs/content/v0.6/2.configuration/2.nuxt-config.md +++ b/docs/content/v0.6/2.configuration/2.nuxt-config.md @@ -215,6 +215,18 @@ type ProviderLocal = { * @advanced_array_example { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" } */ sessionDataType?: SessionDataObject, + /** + * How to extract the authentication-token from the sign-in response. + * + * E.g., setting this to `/data/user` and returning an object like `{ data: { user: { id:number, name: string } }, status: 'ok' }` from the `getSession` endpoint will + * storing the 'User' object typed as the type created via the 'sessionDataType' prop. + * + * This follows the JSON Pointer standard, see it's RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 + * + * @default / Access the root of the session response object + * @example /data/user Access the `data/user` property of the session response object + */ + sessionDataResponseTokenPointer?: string } ``` ```ts [SessionConfig] From 20d18a63443ca2ff7be0fb266aeec32a9834bdbd Mon Sep 17 00:00:00 2001 From: Valentin Hutter Date: Thu, 30 Nov 2023 14:55:25 +0100 Subject: [PATCH 04/26] feat: move pointer and sessionDataType to the SessionConfig --- .../v0.6/2.configuration/2.nuxt-config.md | 44 +++++++++---------- .../2.session-access-and-management.md | 4 +- playground-local/nuxt.config.ts | 8 ++-- src/module.ts | 8 ++-- src/runtime/composables/local/useAuth.ts | 8 ++-- src/runtime/types.ts | 42 +++++++++--------- 6 files changed, 58 insertions(+), 56 deletions(-) diff --git a/docs/content/v0.6/2.configuration/2.nuxt-config.md b/docs/content/v0.6/2.configuration/2.nuxt-config.md index 4c60fc55..d9d2b0b2 100644 --- a/docs/content/v0.6/2.configuration/2.nuxt-config.md +++ b/docs/content/v0.6/2.configuration/2.nuxt-config.md @@ -216,26 +216,6 @@ type ProviderLocal = { */ sameSiteAttribute?: boolean | 'lax' | 'strict' | 'none' | undefined, }, - /** - * Define an interface for the session data object that `nuxt-auth` expects to receive from the `getSession` endpoint. - * - * @default { id: 'string | number' } - * @example { id: 'string', name: 'string', email: 'string' } - * @advanced_array_example { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" } - */ - sessionDataType?: SessionDataObject, - /** - * How to extract the authentication-token from the sign-in response. - * - * E.g., setting this to `/data/user` and returning an object like `{ data: { user: { id:number, name: string } }, status: 'ok' }` from the `getSession` endpoint will - * storing the 'User' object typed as the type created via the 'sessionDataType' prop. - * - * This follows the JSON Pointer standard, see it's RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 - * - * @default / Access the root of the session response object - * @example /data/user Access the `data/user` property of the session response object - */ - sessionDataResponseTokenPointer?: string } ``` ```ts [SessionConfig] @@ -254,14 +234,34 @@ type SessionConfig = { * @default false * */ - enableRefreshPeriodically: number | boolean + enableRefreshPeriodically: number | boolean, /** * Whether to refresh the session every time the browser window is refocused. * * @example false * @default true */ - enableRefreshOnWindowFocus: boolean + enableRefreshOnWindowFocus: boolean, + /** + * Define an interface for the session data object that `nuxt-auth` expects to receive from the `getSession` endpoint. + * + * @default { id: 'string | number' } + * @example { id: 'string', name: 'string', email: 'string' } + * @advanced_array_example { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" } + */ + dataType?: SessionDataObject, + /** + * How to extract the session-data from the session response. + * + * E.g., setting this to `/data/user` and returning an object like `{ data: { user: { id:number, name: string } }, status: 'ok' }` from the `getSession` endpoint will + * storing the 'User' object typed as the type created via the 'dataType' prop. + * + * This follows the JSON Pointer standard, see it's RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 + * + * @default / Access the root of the session response object + * @example /data/user Access the `data/user` property of the session response object + */ + dataResponsePointer?: string } ``` ```ts [GlobalMiddlewareOptions] diff --git a/docs/content/v0.6/3.application-side/2.session-access-and-management.md b/docs/content/v0.6/3.application-side/2.session-access-and-management.md index 302bfa1c..6e61c5f3 100644 --- a/docs/content/v0.6/3.application-side/2.session-access-and-management.md +++ b/docs/content/v0.6/3.application-side/2.session-access-and-management.md @@ -118,7 +118,7 @@ inferface SessionData { id: string | number } -// Option B: You configured `auth.provider.sessionDataType` to something like ` { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account' }` +// Option B: You configured `auth.session.dataType` to something like ` { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account' }` inferface SessionData { id: string email: string @@ -128,7 +128,7 @@ inferface SessionData { ``` :: -### About `auth.provider.sessionDataType` +### About `auth.session.dataType` This is a configuration option available to dynamically type the `SessionData` that the `local` provider will return when accessing `data.value`. Read more about this in the [nuxt.config.ts configuration documentation](/nuxt-auth/v0.6/configuration/nuxt-config) of the `local` provider. diff --git a/playground-local/nuxt.config.ts b/playground-local/nuxt.config.ts index 71b79d36..e38f466f 100644 --- a/playground-local/nuxt.config.ts +++ b/playground-local/nuxt.config.ts @@ -4,6 +4,7 @@ export default defineNuxtConfig({ transpile: ['jsonwebtoken'] }, auth: { + baseURL: 'http://localhost:3001/api/auth', provider: { type: 'local', endpoints: { @@ -15,15 +16,14 @@ export default defineNuxtConfig({ token: { signInResponseTokenPointer: '/token/accessToken' }, - sessionDataType: { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" }, - sessionDataResponseTokenPointer: '/data/user' }, session: { // Whether to refresh the session every time the browser window is refocused. enableRefreshOnWindowFocus: true, - // Whether to refresh the session every `X` milliseconds. Set this to `false` to turn it off. The session will only be refreshed if a session already exists. - enableRefreshPeriodically: 5000 + enableRefreshPeriodically: 5000, + dataType: { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" }, + dataResponsePointer: '/data/user' }, globalAppMiddleware: { isEnabled: true diff --git a/src/module.ts b/src/module.ts index fac8ade4..ea785a3f 100644 --- a/src/module.ts +++ b/src/module.ts @@ -10,7 +10,9 @@ const topLevelDefaults = { isEnabled: true, session: { enableRefreshPeriodically: false, - enableRefreshOnWindowFocus: true + enableRefreshOnWindowFocus: true, + dataType: { id: 'string | number' }, + dataResponsePointer: '/' }, globalAppMiddleware: { isEnabled: false, @@ -38,8 +40,6 @@ const defaultsByBackend: { [key in SupportedAuthProviders]: DeepRequired({ ` const getServerSession: typeof import('${resolve('./runtime/server/services')}').getServerSession`, ` const getToken: typeof import('${resolve('./runtime/server/services')}').getToken`, ` const NuxtAuthHandler: typeof import('${resolve('./runtime/server/services')}').NuxtAuthHandler`, - options.provider.type === 'local' ? genInterface('SessionData', (options.provider as any).sessionDataType) : '', + options.provider.type === 'local' ? genInterface('SessionData', (options.session as any).dataType) : '', '}' ].join('\n') }) diff --git a/src/runtime/composables/local/useAuth.ts b/src/runtime/composables/local/useAuth.ts index 39ad3185..77953f00 100644 --- a/src/runtime/composables/local/useAuth.ts +++ b/src/runtime/composables/local/useAuth.ts @@ -25,9 +25,10 @@ const signIn: SignInFunc = async (credentials, signInOptions, params: signInParams ?? {} }) - const extractedToken = extractObjectWithJsonPointer(response, config.token.signInResponseTokenPointer) + const { dataResponsePointer: dataResponseSessionPointer } = useRuntimeConfig().public.auth.session + const extractedToken = extractObjectWithJsonPointer(response, dataResponseSessionPointer) if (typeof extractedToken !== 'string') { - console.error(`Auth: string token expected, received instead: ${JSON.stringify(extractedToken)}. Tried to find token at ${config.token.signInResponseTokenPointer} in ${JSON.stringify(response)}`) + console.error(`Auth: string token expected, received instead: ${JSON.stringify(extractedToken)}. Tried to find token at ${dataResponseSessionPointer} in ${JSON.stringify(response)}`) return } @@ -81,7 +82,8 @@ const getSession: GetSessionFunc = async (getSessionO loading.value = true try { const result = await _fetch(nuxt, path, { method, headers }) - data.value = extractObjectWithJsonPointer(result, config.sessionDataResponseTokenPointer) + const { dataType: sessionDataType } = useRuntimeConfig().public.auth.session + data.value = extractObjectWithJsonPointer(result, sessionDataType) } catch { // Clear all data: Request failed so we must not be authenticated data.value = null diff --git a/src/runtime/types.ts b/src/runtime/types.ts index fde0a9d7..0562897d 100644 --- a/src/runtime/types.ts +++ b/src/runtime/types.ts @@ -150,26 +150,6 @@ type ProviderLocal = { */ sameSiteAttribute?: boolean | 'lax' | 'strict' | 'none' | undefined, }, - /** - * Define an interface for the session data object that `nuxt-auth` expects to receive from the `getSession` endpoint. - * - * @default { id: 'string | number' } - * @example { id: 'string', name: 'string', email: 'string' } - * @advanced_array_example { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" } - */ - sessionDataType?: SessionDataObject, - /** - * How to extract the authentication-token from the sign-in response. - * - * E.g., setting this to `/data/user` and returning an object like `{ data: { user: { id:number, name: string } }, status: 'ok' }` from the `getSession` endpoint will - * storing the 'User' object typed as the type created via the 'sessionDataType' prop. - * - * This follows the JSON Pointer standard, see it's RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 - * - * @default / Access the root of the session response object - * @example /data/user Access the `data/user` property of the session response object - */ - sessionDataResponseTokenPointer?: string } /** * Configuration for the `authjs`-provider. @@ -230,7 +210,27 @@ type SessionConfig = { * @example false * @default true */ - enableRefreshOnWindowFocus: boolean + enableRefreshOnWindowFocus: boolean, + /** + * Define an interface for the session data object that `nuxt-auth` expects to receive from the `getSession` endpoint. + * + * @default { id: 'string | number' } + * @example { id: 'string', name: 'string', email: 'string' } + * @advanced_array_example { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" } + */ + dataType?: SessionDataObject, + /** + * How to extract the session-data from the session response. + * + * E.g., setting this to `/data/user` and returning an object like `{ data: { user: { id:number, name: string } }, status: 'ok' }` from the `getSession` endpoint will + * storing the 'User' object typed as the type created via the 'dataType' prop. + * + * This follows the JSON Pointer standard, see it's RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 + * + * @default / Access the root of the session response object + * @example /data/user Access the `data/user` property of the session response object + */ + dataResponsePointer?: string } /** From d4761b4281267bc234a3ea2ac5ea33601221012c Mon Sep 17 00:00:00 2001 From: Valentin Hutter Date: Thu, 30 Nov 2023 15:45:36 +0100 Subject: [PATCH 05/26] fix: remove default baseUrl --- playground-local/nuxt.config.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/playground-local/nuxt.config.ts b/playground-local/nuxt.config.ts index e38f466f..4ab2c175 100644 --- a/playground-local/nuxt.config.ts +++ b/playground-local/nuxt.config.ts @@ -4,7 +4,6 @@ export default defineNuxtConfig({ transpile: ['jsonwebtoken'] }, auth: { - baseURL: 'http://localhost:3001/api/auth', provider: { type: 'local', endpoints: { From 01ca02396e1c1951024fe9fd4f8c42d47ec145d4 Mon Sep 17 00:00:00 2001 From: Valentin Hutter Date: Thu, 30 Nov 2023 16:55:58 +0100 Subject: [PATCH 06/26] fix merge conflicts from main --- src/runtime/composables/local/useAuth.ts | 6 +++--- src/runtime/helpers.ts | 21 ++++++++------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/runtime/composables/local/useAuth.ts b/src/runtime/composables/local/useAuth.ts index 14c84427..706f1c63 100644 --- a/src/runtime/composables/local/useAuth.ts +++ b/src/runtime/composables/local/useAuth.ts @@ -2,7 +2,7 @@ import { readonly, Ref } from 'vue' import { callWithNuxt } from '#app/nuxt' import { CommonUseAuthReturn, SignOutFunc, SignInFunc, GetSessionFunc, SecondarySignInOptions } from '../../types' import { _fetch } from '../../utils/fetch' -import { extractObjectWithJsonPointer, useTypedBackendConfig } from '../../helpers' +import { jsonPointerGet, useTypedBackendConfig } from '../../helpers' import { getRequestURLWN } from '../../utils/callWithNuxt' import { useAuthState } from './useAuthState' // @ts-expect-error - #auth not defined @@ -26,7 +26,7 @@ const signIn: SignInFunc = async (credentials, signInOptions, }) const { dataResponsePointer: dataResponseSessionPointer } = useRuntimeConfig().public.auth.session - const extractedToken = extractObjectWithJsonPointer(response, dataResponseSessionPointer) + const extractedToken = jsonPointerGet(response, dataResponseSessionPointer) if (typeof extractedToken !== 'string') { console.error(`Auth: string token expected, received instead: ${JSON.stringify(extractedToken)}. Tried to find token at ${dataResponseSessionPointer} in ${JSON.stringify(response)}`) return @@ -87,7 +87,7 @@ const getSession: GetSessionFunc = async (getSessionO try { const result = await _fetch(nuxt, path, { method, headers }) const { dataType: sessionDataType } = useRuntimeConfig().public.auth.session - data.value = extractObjectWithJsonPointer(result, sessionDataType) + data.value = jsonPointerGet(result, sessionDataType) } catch { // Clear all data: Request failed so we must not be authenticated data.value = null diff --git a/src/runtime/helpers.ts b/src/runtime/helpers.ts index 638af570..cc8f8652 100644 --- a/src/runtime/helpers.ts +++ b/src/runtime/helpers.ts @@ -49,18 +49,12 @@ export const useTypedBackendConfig = ( * @param obj * @param pointer */ -export const jsonPointerGet = ( - obj: Record, - pointer: string -): string | Record => { +export const jsonPointerGet = (obj: T, pointer: string): TResult => { + let result: TResult = obj as unknown as TResult const unescape = (str: string) => str.replace(/~1/g, '/').replace(/~0/g, '~') const parse = (pointer: string) => { - if (pointer === '') { - return [] - } - if (pointer.charAt(0) !== '/') { - throw new Error('Invalid JSON pointer: ' + pointer) - } + if (pointer === '') { return [] } + if (pointer.charAt(0) !== '/') { throw new Error('Invalid JSON pointer: ' + pointer) } return pointer.substring(1).split(/\//).map(unescape) } @@ -68,10 +62,11 @@ export const jsonPointerGet = ( for (let i = 0; i < refTokens.length; ++i) { const tok = refTokens[i] - if (!(typeof obj === 'object' && tok in obj)) { + if (!(typeof result === 'object' && result && tok in result)) { throw new Error('Invalid reference token: ' + tok) } - obj = obj[tok] + result = (result as any)[tok] } - return obj + + return result } From 9fe12dae174acbcb8fde7e3e8ad6fa0c397d5b51 Mon Sep 17 00:00:00 2001 From: Valentin Hutter Date: Thu, 30 Nov 2023 17:03:48 +0100 Subject: [PATCH 07/26] fix: merge conflict with SessionConfig --- docs/content/2.configuration/2.nuxt-config.md | 8 -------- src/module.ts | 6 +++--- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/docs/content/2.configuration/2.nuxt-config.md b/docs/content/2.configuration/2.nuxt-config.md index 8cc75ac1..844822cf 100644 --- a/docs/content/2.configuration/2.nuxt-config.md +++ b/docs/content/2.configuration/2.nuxt-config.md @@ -367,14 +367,6 @@ type ProviderRefresh = { */ maxAgeInSeconds?: number, }, - /** - * Define an interface for the session data object that `nuxt-auth` expects to receive from the `getSession` endpoint. - * - * @default { id: 'string | number' } - * @example { id: 'string', name: 'string', email: 'string' } - * @advanced_array_example { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" } - */ - sessionDataType?: SessionDataObject, } ``` ```ts [SessionConfig] diff --git a/src/module.ts b/src/module.ts index dd3525dd..be018963 100644 --- a/src/module.ts +++ b/src/module.ts @@ -23,7 +23,9 @@ const topLevelDefaults = { isEnabled: true, session: { enableRefreshPeriodically: false, - enableRefreshOnWindowFocus: true + enableRefreshOnWindowFocus: true, + dataType: { id: 'string | number' }, + dataResponsePointer: '/' }, globalAppMiddleware: { isEnabled: false, @@ -55,7 +57,6 @@ const defaultsByBackend: { maxAgeInSeconds: 30 * 60, sameSiteAttribute: 'lax' }, - sessionDataType: { id: 'string | number' } }, refresh: { @@ -82,7 +83,6 @@ const defaultsByBackend: { signInResponseRefreshTokenPointer: '/refreshToken', maxAgeInSeconds: 60 * 60 * 24 * 7 // 7 days }, - sessionDataType: { id: 'string | number' } }, authjs: { From f03425761d9c1077d6eff345664e89f357f6d864 Mon Sep 17 00:00:00 2001 From: Valentin Hutter Date: Thu, 30 Nov 2023 17:13:49 +0100 Subject: [PATCH 08/26] reset to main branch state --- playground-local/server/api/auth/login.post.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/playground-local/server/api/auth/login.post.ts b/playground-local/server/api/auth/login.post.ts index c7ecd303..847c5034 100644 --- a/playground-local/server/api/auth/login.post.ts +++ b/playground-local/server/api/auth/login.post.ts @@ -19,10 +19,10 @@ export default eventHandler(async (event) => { name: 'User ' + username } - const accessToken = sign({ data: { user }, scope: ['test', 'user'] }, SECRET, { expiresIn }) + const accessToken = sign({ ...user, scope: ['test', 'user'] }, SECRET, { expiresIn }) refreshTokens[refreshToken] = { accessToken, - data: { user } + user } return { @@ -31,4 +31,4 @@ export default eventHandler(async (event) => { refreshToken } } -}) +}) \ No newline at end of file From e788160b125689ec37487aa4cba6c1f131aeea9f Mon Sep 17 00:00:00 2001 From: Valentin Hutter Date: Thu, 30 Nov 2023 22:37:32 +0100 Subject: [PATCH 09/26] fix: lint --- playground-local/nuxt.config.ts | 2 +- playground-local/server/api/auth/login.post.ts | 2 +- src/module.ts | 4 ++-- src/runtime/types.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/playground-local/nuxt.config.ts b/playground-local/nuxt.config.ts index 4ab2c175..c98addce 100644 --- a/playground-local/nuxt.config.ts +++ b/playground-local/nuxt.config.ts @@ -14,7 +14,7 @@ export default defineNuxtConfig({ }, token: { signInResponseTokenPointer: '/token/accessToken' - }, + } }, session: { // Whether to refresh the session every time the browser window is refocused. diff --git a/playground-local/server/api/auth/login.post.ts b/playground-local/server/api/auth/login.post.ts index 847c5034..5fa9a0e8 100644 --- a/playground-local/server/api/auth/login.post.ts +++ b/playground-local/server/api/auth/login.post.ts @@ -31,4 +31,4 @@ export default eventHandler(async (event) => { refreshToken } } -}) \ No newline at end of file +}) diff --git a/src/module.ts b/src/module.ts index be018963..dc819125 100644 --- a/src/module.ts +++ b/src/module.ts @@ -56,7 +56,7 @@ const defaultsByBackend: { headerName: 'Authorization', maxAgeInSeconds: 30 * 60, sameSiteAttribute: 'lax' - }, + } }, refresh: { @@ -82,7 +82,7 @@ const defaultsByBackend: { refreshToken: { signInResponseRefreshTokenPointer: '/refreshToken', maxAgeInSeconds: 60 * 60 * 24 * 7 // 7 days - }, + } }, authjs: { diff --git a/src/runtime/types.ts b/src/runtime/types.ts index bfdecb89..688b348a 100644 --- a/src/runtime/types.ts +++ b/src/runtime/types.ts @@ -436,4 +436,4 @@ export type SignInFunc = ( primaryOptions: PrimarySignInOptions, signInOptions?: SecondarySignInOptions, paramsOptions?: Record -) => Promise; \ No newline at end of file +) => Promise; From 4aa8a12857dc29679ec6f8dcf1ad119dfd0564be Mon Sep 17 00:00:00 2001 From: Valentin Hutter Date: Tue, 5 Dec 2023 10:59:17 +0100 Subject: [PATCH 10/26] feat: make session refresh params optional --- docs/content/2.configuration/2.nuxt-config.md | 6 +++--- src/runtime/types.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/content/2.configuration/2.nuxt-config.md b/docs/content/2.configuration/2.nuxt-config.md index 844822cf..a1e87329 100644 --- a/docs/content/2.configuration/2.nuxt-config.md +++ b/docs/content/2.configuration/2.nuxt-config.md @@ -356,7 +356,7 @@ type ProviderRefresh = { * @default /refreshToken Access the `refreshToken` property of the sign-in response object * @example / Access the root of the sign-in response object, useful when your endpoint returns a plain, non-object string as the refreshToken */ - signInResponseRefreshTokenPointer?: string + signInResponseRefreshTokenPointer?: string, /** * Maximum age to store the authentication token for. After the expiry time the token is automatically deleted on the application side, i.e., in the users' browser. * @@ -385,14 +385,14 @@ type SessionConfig = { * @default false * */ - enableRefreshPeriodically: number | boolean + enableRefreshPeriodically?: number | boolean, /** * Whether to refresh the session every time the browser window is refocused. * * @example false * @default true */ - enableRefreshOnWindowFocus: boolean + enableRefreshOnWindowFocus?: boolean, /** * Define an interface for the session data object that `nuxt-auth` expects to receive from the `getSession` endpoint. * diff --git a/src/runtime/types.ts b/src/runtime/types.ts index 688b348a..8a039f4a 100644 --- a/src/runtime/types.ts +++ b/src/runtime/types.ts @@ -266,14 +266,14 @@ type SessionConfig = { * @default false * */ - enableRefreshPeriodically: number | boolean; + enableRefreshPeriodically?: number | boolean; /** * Whether to refresh the session every time the browser window is refocused. * * @example false * @default true */ - enableRefreshOnWindowFocus: boolean; + enableRefreshOnWindowFocus?: boolean; /** * Define an interface for the session data object that `nuxt-auth` expects to receive from the `getSession` endpoint. * From 3ad2296f35bf914e7c6bc3117d7febce7dea535d Mon Sep 17 00:00:00 2001 From: Valentin Hutter Date: Tue, 5 Dec 2023 11:01:10 +0100 Subject: [PATCH 11/26] feat: rebase `jsonPointerGet` function on @danielwinkelmann's original modifications --- src/runtime/helpers.ts | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/runtime/helpers.ts b/src/runtime/helpers.ts index cc8f8652..f26aa243 100644 --- a/src/runtime/helpers.ts +++ b/src/runtime/helpers.ts @@ -49,12 +49,23 @@ export const useTypedBackendConfig = ( * @param obj * @param pointer */ -export const jsonPointerGet = (obj: T, pointer: string): TResult => { - let result: TResult = obj as unknown as TResult +export const jsonPointerGet = ( + obj: T, + pointer: string +): TResult => { + let result = obj as unknown as TResult + if (pointer === '/') { + return result + } + const unescape = (str: string) => str.replace(/~1/g, '/').replace(/~0/g, '~') const parse = (pointer: string) => { - if (pointer === '') { return [] } - if (pointer.charAt(0) !== '/') { throw new Error('Invalid JSON pointer: ' + pointer) } + if (pointer === '') { + return [] + } + if (pointer.charAt(0) !== '/') { + throw new Error('Invalid JSON pointer: ' + pointer) + } return pointer.substring(1).split(/\//).map(unescape) } @@ -62,11 +73,11 @@ export const jsonPointerGet = (obj: T, point for (let i = 0; i < refTokens.length; ++i) { const tok = refTokens[i] - if (!(typeof result === 'object' && result && tok in result)) { + if (!(typeof result === 'object' && tok in result)) { throw new Error('Invalid reference token: ' + tok) } - result = (result as any)[tok] + result = result[tok] } - + return result } From df753ae40d582cf0ea6e01ab788e07796aab46e3 Mon Sep 17 00:00:00 2001 From: Valentin Hutter Date: Tue, 5 Dec 2023 11:04:33 +0100 Subject: [PATCH 12/26] refacto: set the return type when extracting json pointers --- src/runtime/composables/refresh/useAuth.ts | 8 ++++---- src/runtime/server/plugins/refresh-token.server.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/runtime/composables/refresh/useAuth.ts b/src/runtime/composables/refresh/useAuth.ts index 2c72f68d..07175688 100644 --- a/src/runtime/composables/refresh/useAuth.ts +++ b/src/runtime/composables/refresh/useAuth.ts @@ -32,7 +32,7 @@ const signIn: ReturnType['signIn'] = async ( params: signInParams ?? {} }) - const extractedToken = jsonPointerGet( + const extractedToken = jsonPointerGet( response, config.token.signInResponseTokenPointer ) @@ -47,7 +47,7 @@ const signIn: ReturnType['signIn'] = async ( return } - const extractedRefreshToken = jsonPointerGet( + const extractedRefreshToken = jsonPointerGet( response, config.refreshToken.signInResponseRefreshTokenPointer ) @@ -96,7 +96,7 @@ const refresh = async () => { } }) - const extractedToken = jsonPointerGet( + const extractedToken = jsonPointerGet( response, config.token.signInResponseTokenPointer ) @@ -112,7 +112,7 @@ const refresh = async () => { } if (!config.refreshOnlyToken) { - const extractedRefreshToken = jsonPointerGet( + const extractedRefreshToken = jsonPointerGet( response, config.refreshToken.signInResponseRefreshTokenPointer ) diff --git a/src/runtime/server/plugins/refresh-token.server.ts b/src/runtime/server/plugins/refresh-token.server.ts index c7d90ec1..be65f093 100644 --- a/src/runtime/server/plugins/refresh-token.server.ts +++ b/src/runtime/server/plugins/refresh-token.server.ts @@ -27,7 +27,7 @@ export default defineNuxtPlugin({ headers }) - const extractedToken = jsonPointerGet( + const extractedToken = jsonPointerGet( response, config.provider.token.signInResponseTokenPointer ) @@ -44,7 +44,7 @@ export default defineNuxtPlugin({ // check if refereshTokenOnly if (!configToken.refreshOnlyToken) { - const extractedRefreshToken = jsonPointerGet( + const extractedRefreshToken = jsonPointerGet( response, config.provider.refreshToken.signInResponseRefreshTokenPointer ) From 134d70f98649f7792c4fc8e3c4ec45d9ed1fec9f Mon Sep 17 00:00:00 2001 From: Valentin Hutter Date: Tue, 5 Dec 2023 11:05:32 +0100 Subject: [PATCH 13/26] fix: should use the pointer to retrieve the user session not datatype + improves returned error --- src/runtime/composables/local/useAuth.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/runtime/composables/local/useAuth.ts b/src/runtime/composables/local/useAuth.ts index 706f1c63..3a607721 100644 --- a/src/runtime/composables/local/useAuth.ts +++ b/src/runtime/composables/local/useAuth.ts @@ -6,7 +6,7 @@ import { jsonPointerGet, useTypedBackendConfig } from '../../helpers' import { getRequestURLWN } from '../../utils/callWithNuxt' import { useAuthState } from './useAuthState' // @ts-expect-error - #auth not defined -import type { SessionData } from '#auth' +import { SessionData } from '#auth' import { useNuxtApp, useRuntimeConfig, nextTick, navigateTo } from '#imports' type Credentials = { username?: string, email?: string, password?: string } & Record @@ -25,10 +25,9 @@ const signIn: SignInFunc = async (credentials, signInOptions, params: signInParams ?? {} }) - const { dataResponsePointer: dataResponseSessionPointer } = useRuntimeConfig().public.auth.session - const extractedToken = jsonPointerGet(response, dataResponseSessionPointer) + const extractedToken = jsonPointerGet(response, config.token.signInResponseTokenPointer) if (typeof extractedToken !== 'string') { - console.error(`Auth: string token expected, received instead: ${JSON.stringify(extractedToken)}. Tried to find token at ${dataResponseSessionPointer} in ${JSON.stringify(response)}`) + console.error(`Auth: string token expected, received instead: ${JSON.stringify(extractedToken)}. Tried to find token at ${config.token.signInResponseTokenPointer} in ${JSON.stringify(response)}`) return } @@ -86,9 +85,13 @@ const getSession: GetSessionFunc = async (getSessionO loading.value = true try { const result = await _fetch(nuxt, path, { method, headers }) - const { dataType: sessionDataType } = useRuntimeConfig().public.auth.session - data.value = jsonPointerGet(result, sessionDataType) - } catch { + const { dataResponsePointer: sessionDataResponsePointer } = useRuntimeConfig().public.auth.session + data.value = jsonPointerGet(result, sessionDataResponsePointer) + } catch (err) { + if (!data.value && err instanceof Error) { + console.error(`Session: unable to extract session, ${err.message}`) + } + // Clear all data: Request failed so we must not be authenticated data.value = null rawToken.value = null From 53b6edd323d6542536d63415b3b13fe04d6cfdbb Mon Sep 17 00:00:00 2001 From: Valentin Hutter Date: Tue, 5 Dec 2023 11:10:05 +0100 Subject: [PATCH 14/26] revert: import as type --- src/runtime/composables/local/useAuth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/composables/local/useAuth.ts b/src/runtime/composables/local/useAuth.ts index 3a607721..5e98ff39 100644 --- a/src/runtime/composables/local/useAuth.ts +++ b/src/runtime/composables/local/useAuth.ts @@ -6,7 +6,7 @@ import { jsonPointerGet, useTypedBackendConfig } from '../../helpers' import { getRequestURLWN } from '../../utils/callWithNuxt' import { useAuthState } from './useAuthState' // @ts-expect-error - #auth not defined -import { SessionData } from '#auth' +import type { SessionData } from '#auth' import { useNuxtApp, useRuntimeConfig, nextTick, navigateTo } from '#imports' type Credentials = { username?: string, email?: string, password?: string } & Record From 2044dce06beefa616ea23fd6dec3ecceee927a52 Mon Sep 17 00:00:00 2001 From: Valentin Hutter Date: Tue, 5 Dec 2023 12:03:35 +0100 Subject: [PATCH 15/26] fix lint --- src/runtime/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/helpers.ts b/src/runtime/helpers.ts index f26aa243..5cd4f8fa 100644 --- a/src/runtime/helpers.ts +++ b/src/runtime/helpers.ts @@ -78,6 +78,6 @@ export const jsonPointerGet = ( } result = result[tok] } - + return result } From 4613e42b756da4513fd46ea8f7f23efd0cda4cbe Mon Sep 17 00:00:00 2001 From: Valentin Hutter Date: Tue, 5 Dec 2023 14:13:26 +0100 Subject: [PATCH 16/26] fix: type issue - forgotten changes from daniel --- src/runtime/helpers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime/helpers.ts b/src/runtime/helpers.ts index 5cd4f8fa..127356d9 100644 --- a/src/runtime/helpers.ts +++ b/src/runtime/helpers.ts @@ -73,10 +73,10 @@ export const jsonPointerGet = ( for (let i = 0; i < refTokens.length; ++i) { const tok = refTokens[i] - if (!(typeof result === 'object' && tok in result)) { + if (!(typeof result === 'object' && result && tok in result)) { throw new Error('Invalid reference token: ' + tok) } - result = result[tok] + result = (result as any)[tok] } return result From ca2af9373b28fd8a8791d4c90f9962c69e92cbd2 Mon Sep 17 00:00:00 2001 From: Valentin Hutter Date: Mon, 8 Jan 2024 14:43:13 +0100 Subject: [PATCH 17/26] move the session data type back to the providers (local and refresh) --- docs/content/2.configuration/2.nuxt-config.md | 49 ++++++++++--------- .../2.session-access-and-management.md | 4 +- playground-local/nuxt.config.ts | 9 ++-- src/module.ts | 14 ++++-- src/runtime/composables/local/useAuth.ts | 2 +- src/runtime/types.ts | 49 ++++++++++--------- 6 files changed, 73 insertions(+), 54 deletions(-) diff --git a/docs/content/2.configuration/2.nuxt-config.md b/docs/content/2.configuration/2.nuxt-config.md index a1e87329..80cc872d 100644 --- a/docs/content/2.configuration/2.nuxt-config.md +++ b/docs/content/2.configuration/2.nuxt-config.md @@ -219,6 +219,31 @@ type ProviderLocal = { */ sameSiteAttribute?: boolean | 'lax' | 'strict' | 'none' | undefined, }, + /* + * Settings for the session-data that `nuxt-auth` receives from the `getSession` endpoint. + */ + session?: { + /** + * Define an interface for the session data object that `nuxt-auth` expects to receive from the `getSession` endpoint. + * + * @default { id: 'string | number' } + * @example { id: 'string', name: 'string', email: 'string' } + * @advanced_array_example { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" } + */ + dataType?: SessionDataObject; + /** + * How to extract the session-data from the session response. + * + * E.g., setting this to `/data/user` and returning an object like `{ data: { user: { id:number, name: string } }, status: 'ok' }` from the `getSession` endpoint will + * storing the 'User' object typed as the type created via the 'dataType' prop. + * + * This follows the JSON Pointer standard, see it's RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 + * + * @default / Access the root of the session response object + * @example /data/user Access the `data/user` property of the session response object + */ + dataResponsePointer?: string; + } } ``` @@ -385,34 +410,14 @@ type SessionConfig = { * @default false * */ - enableRefreshPeriodically?: number | boolean, + enableRefreshPeriodically: number | boolean, /** * Whether to refresh the session every time the browser window is refocused. * * @example false * @default true */ - enableRefreshOnWindowFocus?: boolean, - /** - * Define an interface for the session data object that `nuxt-auth` expects to receive from the `getSession` endpoint. - * - * @default { id: 'string | number' } - * @example { id: 'string', name: 'string', email: 'string' } - * @advanced_array_example { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" } - */ - dataType?: SessionDataObject, - /** - * How to extract the session-data from the session response. - * - * E.g., setting this to `/data/user` and returning an object like `{ data: { user: { id:number, name: string } }, status: 'ok' }` from the `getSession` endpoint will - * storing the 'User' object typed as the type created via the 'dataType' prop. - * - * This follows the JSON Pointer standard, see it's RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 - * - * @default / Access the root of the session response object - * @example /data/user Access the `data/user` property of the session response object - */ - dataResponsePointer?: string + enableRefreshOnWindowFocus: boolean } ``` ```ts [GlobalMiddlewareOptions] diff --git a/docs/content/3.application-side/2.session-access-and-management.md b/docs/content/3.application-side/2.session-access-and-management.md index 50230e8e..217f699b 100644 --- a/docs/content/3.application-side/2.session-access-and-management.md +++ b/docs/content/3.application-side/2.session-access-and-management.md @@ -171,7 +171,7 @@ inferface SessionData { id: string | number } -// Option B: You configured `auth.session.dataType` to something like ` { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account' }` +// Option B: You configured `auth.provider.session.dataType` to something like ` { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account' }` inferface SessionData { id: string email: string @@ -181,7 +181,7 @@ inferface SessionData { ``` :: -### About `auth.session.dataType` +### About `auth.provider.session.dataType` This is a configuration option available to dynamically type the `SessionData` that the `local` provider will return when accessing `data.value`. Read more about this in the [nuxt.config.ts configuration documentation](/nuxt-auth/v0.6/configuration/nuxt-config) of the `local` provider. diff --git a/playground-local/nuxt.config.ts b/playground-local/nuxt.config.ts index c98addce..19434d56 100644 --- a/playground-local/nuxt.config.ts +++ b/playground-local/nuxt.config.ts @@ -4,6 +4,7 @@ export default defineNuxtConfig({ transpile: ['jsonwebtoken'] }, auth: { + baseURL: 'http://localhost:3001', provider: { type: 'local', endpoints: { @@ -14,15 +15,17 @@ export default defineNuxtConfig({ }, token: { signInResponseTokenPointer: '/token/accessToken' + }, + session: { + dataType: { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" }, + dataResponsePointer: '/data/user' } }, session: { // Whether to refresh the session every time the browser window is refocused. enableRefreshOnWindowFocus: true, // Whether to refresh the session every `X` milliseconds. Set this to `false` to turn it off. The session will only be refreshed if a session already exists. - enableRefreshPeriodically: 5000, - dataType: { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" }, - dataResponsePointer: '/data/user' + enableRefreshPeriodically: 5000 }, globalAppMiddleware: { isEnabled: true diff --git a/src/module.ts b/src/module.ts index dc819125..1b282547 100644 --- a/src/module.ts +++ b/src/module.ts @@ -23,9 +23,7 @@ const topLevelDefaults = { isEnabled: true, session: { enableRefreshPeriodically: false, - enableRefreshOnWindowFocus: true, - dataType: { id: 'string | number' }, - dataResponsePointer: '/' + enableRefreshOnWindowFocus: true }, globalAppMiddleware: { isEnabled: false, @@ -56,6 +54,10 @@ const defaultsByBackend: { headerName: 'Authorization', maxAgeInSeconds: 30 * 60, sameSiteAttribute: 'lax' + }, + session: { + dataType: { id: 'string | number' }, + dataResponsePointer: '/' } }, @@ -82,6 +84,10 @@ const defaultsByBackend: { refreshToken: { signInResponseRefreshTokenPointer: '/refreshToken', maxAgeInSeconds: 60 * 60 * 24 * 7 // 7 days + }, + session: { + dataType: { id: 'string | number' }, + dataResponsePointer: '/' } }, @@ -198,7 +204,7 @@ export default defineNuxtModule({ options.provider.type === 'local' ? genInterface( 'SessionData', - (options.session as any).dataType + (options.provider.session as any).dataType ) : '', '}' diff --git a/src/runtime/composables/local/useAuth.ts b/src/runtime/composables/local/useAuth.ts index 5e98ff39..ecf667a6 100644 --- a/src/runtime/composables/local/useAuth.ts +++ b/src/runtime/composables/local/useAuth.ts @@ -85,7 +85,7 @@ const getSession: GetSessionFunc = async (getSessionO loading.value = true try { const result = await _fetch(nuxt, path, { method, headers }) - const { dataResponsePointer: sessionDataResponsePointer } = useRuntimeConfig().public.auth.session + const { dataResponsePointer: sessionDataResponsePointer } = useRuntimeConfig().public.provider.session data.value = jsonPointerGet(result, sessionDataResponsePointer) } catch (err) { if (!data.value && err instanceof Error) { diff --git a/src/runtime/types.ts b/src/runtime/types.ts index 8a039f4a..be94ab40 100644 --- a/src/runtime/types.ts +++ b/src/runtime/types.ts @@ -161,6 +161,31 @@ type ProviderLocal = { */ sameSiteAttribute?: boolean | 'lax' | 'strict' | 'none' | undefined; }; + /** + * Settings for the session-data that `nuxt-auth` receives from the `getSession` endpoint. + */ + session?: { + /* + * Define an interface for the session data object that `nuxt-auth` expects to receive from the `getSession` endpoint. + * + * @default { id: 'string | number' } + * @example { id: 'string', name: 'string', email: 'string' } + * @advanced_array_example { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" } + */ + dataType?: SessionDataObject; + /** + * How to extract the session-data from the session response. + * + * E.g., setting this to `/data/user` and returning an object like `{ data: { user: { id:number, name: string } }, status: 'ok' }` from the `getSession` endpoint will + * storing the 'User' object typed as the type created via the 'dataType' prop. + * + * This follows the JSON Pointer standard, see it's RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 + * + * @default / Access the root of the session response object + * @example /data/user Access the `data/user` property of the session response object + */ + dataResponsePointer?: string; + }; }; /** @@ -266,34 +291,14 @@ type SessionConfig = { * @default false * */ - enableRefreshPeriodically?: number | boolean; + enableRefreshPeriodically: number | boolean; /** * Whether to refresh the session every time the browser window is refocused. * * @example false * @default true */ - enableRefreshOnWindowFocus?: boolean; - /** - * Define an interface for the session data object that `nuxt-auth` expects to receive from the `getSession` endpoint. - * - * @default { id: 'string | number' } - * @example { id: 'string', name: 'string', email: 'string' } - * @advanced_array_example { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" } - */ - dataType?: SessionDataObject; - /** - * How to extract the session-data from the session response. - * - * E.g., setting this to `/data/user` and returning an object like `{ data: { user: { id:number, name: string } }, status: 'ok' }` from the `getSession` endpoint will - * storing the 'User' object typed as the type created via the 'dataType' prop. - * - * This follows the JSON Pointer standard, see it's RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 - * - * @default / Access the root of the session response object - * @example /data/user Access the `data/user` property of the session response object - */ - dataResponsePointer?: string; + enableRefreshOnWindowFocus: boolean; }; /** From 4387181bc34531c3dfb41f6dfe8aa8a3c6ecc283 Mon Sep 17 00:00:00 2001 From: Valentin Hutter Date: Mon, 8 Jan 2024 14:46:57 +0100 Subject: [PATCH 18/26] forget to remove baseurl while testing --- playground-local/nuxt.config.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/playground-local/nuxt.config.ts b/playground-local/nuxt.config.ts index 19434d56..8f42e7a8 100644 --- a/playground-local/nuxt.config.ts +++ b/playground-local/nuxt.config.ts @@ -4,7 +4,6 @@ export default defineNuxtConfig({ transpile: ['jsonwebtoken'] }, auth: { - baseURL: 'http://localhost:3001', provider: { type: 'local', endpoints: { From 6fade21673f016dd3d71a6fcb8805c11b6334345 Mon Sep 17 00:00:00 2001 From: Valentin Hutter Date: Mon, 8 Jan 2024 16:19:35 +0100 Subject: [PATCH 19/26] fix: use config directly --- src/runtime/composables/local/useAuth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/composables/local/useAuth.ts b/src/runtime/composables/local/useAuth.ts index 516febad..b648356c 100644 --- a/src/runtime/composables/local/useAuth.ts +++ b/src/runtime/composables/local/useAuth.ts @@ -85,7 +85,7 @@ const getSession: GetSessionFunc = async (getSessionO loading.value = true try { const result = await _fetch(nuxt, path, { method, headers }) - const { dataResponsePointer: sessionDataResponsePointer } = useRuntimeConfig().public.provider.session + const { dataResponsePointer: sessionDataResponsePointer } = config.session data.value = jsonPointerGet(result, sessionDataResponsePointer) } catch (err) { if (!data.value && err instanceof Error) { From d596f398e9811367eae29d7c78d77fc3629d1fe1 Mon Sep 17 00:00:00 2001 From: Valentin Hutter Date: Tue, 9 Jan 2024 08:37:58 +0100 Subject: [PATCH 20/26] docs: add session data type to the refresh docs --- docs/content/2.configuration/2.nuxt-config.md | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/content/2.configuration/2.nuxt-config.md b/docs/content/2.configuration/2.nuxt-config.md index 80cc872d..9244e27a 100644 --- a/docs/content/2.configuration/2.nuxt-config.md +++ b/docs/content/2.configuration/2.nuxt-config.md @@ -392,6 +392,31 @@ type ProviderRefresh = { */ maxAgeInSeconds?: number, }, + /* + * Settings for the session-data that `nuxt-auth` receives from the `getSession` endpoint. + */ + session?: { + /** + * Define an interface for the session data object that `nuxt-auth` expects to receive from the `getSession` endpoint. + * + * @default { id: 'string | number' } + * @example { id: 'string', name: 'string', email: 'string' } + * @advanced_array_example { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" } + */ + dataType?: SessionDataObject; + /** + * How to extract the session-data from the session response. + * + * E.g., setting this to `/data/user` and returning an object like `{ data: { user: { id:number, name: string } }, status: 'ok' }` from the `getSession` endpoint will + * storing the 'User' object typed as the type created via the 'dataType' prop. + * + * This follows the JSON Pointer standard, see it's RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 + * + * @default / Access the root of the session response object + * @example /data/user Access the `data/user` property of the session response object + */ + dataResponsePointer?: string; + } } ``` ```ts [SessionConfig] From 9af592011f375801434c2feabe6fb59a2122d7cb Mon Sep 17 00:00:00 2001 From: Valentin Hutter Date: Thu, 14 Mar 2024 12:44:02 +0100 Subject: [PATCH 21/26] fix(playground): reset to default pointer '/' Co-authored-by: Marsel Shayhin <18054980+phoenix-ru@users.noreply.github.com> --- playground-local/nuxt.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playground-local/nuxt.config.ts b/playground-local/nuxt.config.ts index 8f42e7a8..da225ea4 100644 --- a/playground-local/nuxt.config.ts +++ b/playground-local/nuxt.config.ts @@ -17,7 +17,7 @@ export default defineNuxtConfig({ }, session: { dataType: { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" }, - dataResponsePointer: '/data/user' + dataResponsePointer: '/' } }, session: { From e43a549e201fb3a858ef98a92331b6dd03d300a0 Mon Sep 17 00:00:00 2001 From: Marsel Shayhin <18054980+phoenix-ru@users.noreply.github.com> Date: Thu, 16 May 2024 10:39:11 +0200 Subject: [PATCH 22/26] Update useAuth.ts --- src/runtime/composables/local/useAuth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/composables/local/useAuth.ts b/src/runtime/composables/local/useAuth.ts index 3921bf7b..11d31c8b 100644 --- a/src/runtime/composables/local/useAuth.ts +++ b/src/runtime/composables/local/useAuth.ts @@ -26,7 +26,7 @@ const signIn: SignInFunc = async (credentials, signInOptions, params: signInParams ?? {} }) - const extractedToken = jsonPointerGet(response, config.token.signInResponseTokenPointer) + const extractedToken = jsonPointerGet(response, config.token.signInResponseTokenPointer) if (typeof extractedToken !== 'string') { console.error(`Auth: string token expected, received instead: ${JSON.stringify(extractedToken)}. Tried to find token at ${config.token.signInResponseTokenPointer} in ${JSON.stringify(response)}`) return From 8aa3e2b2dc91438266c19f6d58d40b1705532f26 Mon Sep 17 00:00:00 2001 From: Marsel Shayhin <18054980+phoenix-ru@users.noreply.github.com> Date: Thu, 16 May 2024 10:40:28 +0200 Subject: [PATCH 23/26] Update useAuth.ts --- src/runtime/composables/refresh/useAuth.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/runtime/composables/refresh/useAuth.ts b/src/runtime/composables/refresh/useAuth.ts index 1782cd60..6e2c8ea9 100644 --- a/src/runtime/composables/refresh/useAuth.ts +++ b/src/runtime/composables/refresh/useAuth.ts @@ -32,7 +32,7 @@ const signIn: ReturnType['signIn'] = async ( params: signInParams ?? {} }) - const extractedToken = jsonPointerGet( + const extractedToken = jsonPointerGet( response, config.token.signInResponseTokenPointer ) @@ -47,7 +47,7 @@ const signIn: ReturnType['signIn'] = async ( return } - const extractedRefreshToken = jsonPointerGet( + const extractedRefreshToken = jsonPointerGet( response, config.refreshToken.signInResponseRefreshTokenPointer ) @@ -95,7 +95,7 @@ const refresh = async () => { body: objectFromJsonPointer(refreshRequestTokenPointer, refreshToken.value) }) - const extractedToken = jsonPointerGet( + const extractedToken = jsonPointerGet( response, config.token.signInResponseTokenPointer ) @@ -111,7 +111,7 @@ const refresh = async () => { } if (!config.refreshOnlyToken) { - const extractedRefreshToken = jsonPointerGet( + const extractedRefreshToken = jsonPointerGet( response, config.refreshToken.signInResponseRefreshTokenPointer ) From 9f8a71c067471ba1ef5ecee8582df83f5ed2c9c0 Mon Sep 17 00:00:00 2001 From: Marsel Shayhin <18054980+phoenix-ru@users.noreply.github.com> Date: Thu, 16 May 2024 10:43:27 +0200 Subject: [PATCH 24/26] Update helpers.ts --- src/runtime/helpers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime/helpers.ts b/src/runtime/helpers.ts index 125c39bd..9bd44cea 100644 --- a/src/runtime/helpers.ts +++ b/src/runtime/helpers.ts @@ -49,10 +49,10 @@ export const useTypedBackendConfig = ( * @param obj * @param pointer */ -export function jsonPointerGet ( +export function jsonPointerGet > ( obj: Record, pointer: string -): string | Record { +): TResult { const refTokens = Array.isArray(pointer) ? pointer : jsonPointerParse(pointer) for (let i = 0; i < refTokens.length; ++i) { From 4381445f3bc55a10c12bd6712e6b50b6925b9540 Mon Sep 17 00:00:00 2001 From: Marsel Shayhin <18054980+phoenix-ru@users.noreply.github.com> Date: Thu, 16 May 2024 10:46:42 +0200 Subject: [PATCH 25/26] Update helpers.ts --- src/runtime/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/helpers.ts b/src/runtime/helpers.ts index 9bd44cea..0e17d424 100644 --- a/src/runtime/helpers.ts +++ b/src/runtime/helpers.ts @@ -62,7 +62,7 @@ export function jsonPointerGet > ( } obj = obj[tok] } - return obj + return obj as TResult } /** From 697b5daf4c1854e1532cca5d635f4ea07b0db85f Mon Sep 17 00:00:00 2001 From: Marsel Shayhin <18054980+phoenix-ru@users.noreply.github.com> Date: Thu, 16 May 2024 11:05:33 +0200 Subject: [PATCH 26/26] fix: usage of `/` in json pointer get --- src/runtime/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/helpers.ts b/src/runtime/helpers.ts index 0e17d424..5ef69bcc 100644 --- a/src/runtime/helpers.ts +++ b/src/runtime/helpers.ts @@ -130,7 +130,7 @@ export function objectFromJsonPointer (pointer: string | string[], value: any): * Adapted from https://github.com/manuelstofer/json-pointer/blob/931b0f9c7178ca09778087b4b0ac7e4f505620c2/index.js#L217-L221 */ function jsonPointerParse (pointer: string): string[] { - if (pointer === '') { + if (pointer === '' || pointer === '/') { return [] } if (pointer.charAt(0) !== '/') {