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 a2bae96e..4c60fc55 100644 --- a/docs/content/v0.6/2.configuration/2.nuxt-config.md +++ b/docs/content/v0.6/2.configuration/2.nuxt-config.md @@ -224,6 +224,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] diff --git a/playground-local/nuxt.config.ts b/playground-local/nuxt.config.ts index de98c89b..71b79d36 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' }, 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 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 756abe8c..fac8ade4 100644 --- a/src/module.ts +++ b/src/module.ts @@ -38,7 +38,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 03bf1457..f00f1b80 100644 --- a/src/runtime/helpers.ts +++ b/src/runtime/helpers.ts @@ -44,7 +44,11 @@ export const useTypedBackendConfig = (runtimeC * @param obj * @param pointer */ -export const jsonPointerGet = (obj: Record, pointer: string): string | Record => { +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 [] } @@ -56,10 +60,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 } diff --git a/src/runtime/types.ts b/src/runtime/types.ts index b4e6e1eb..fde0a9d7 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, /** @@ -158,8 +158,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. */