From e44b7c726b4df8083636baecca6141cd3b3f885a Mon Sep 17 00:00:00 2001 From: Daniel Winkelmann Date: Sun, 14 May 2023 16:03:25 +0200 Subject: [PATCH 1/3] 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 2/3] 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 3/3] 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]