From 36e55ce9eedcb46e8c6e33887adebd209f3552ed Mon Sep 17 00:00:00 2001 From: benjipott Date: Thu, 9 Nov 2023 07:20:58 +0100 Subject: [PATCH] feat: local with cookie session --- src/module.ts | 11 +++---- src/runtime/composables/local/useAuth.ts | 12 ++++---- src/runtime/composables/local/useAuthState.ts | 20 +++++++++---- src/runtime/types.ts | 29 ++++++++++++++++--- 4 files changed, 52 insertions(+), 20 deletions(-) diff --git a/src/module.ts b/src/module.ts index 756abe8c..87a17e73 100644 --- a/src/module.ts +++ b/src/module.ts @@ -1,10 +1,10 @@ -import { defineNuxtModule, useLogger, createResolver, addTemplate, addPlugin, addServerPlugin, addImports, addRouteMiddleware } from '@nuxt/kit' +import { addImports, addPlugin, addRouteMiddleware, addServerPlugin, addTemplate, createResolver, defineNuxtModule, useLogger } from '@nuxt/kit' import { defu } from 'defu' -import { joinURL } from 'ufo' import { genInterface } from 'knitwork' import type { DeepRequired } from 'ts-essentials' +import { joinURL } from 'ufo' import { getOriginAndPathnameFromURL, isProduction } from './runtime/helpers' -import type { ModuleOptions, SupportedAuthProviders, AuthProviders } from './runtime/types' +import type { AuthProviders, ModuleOptions, SupportedAuthProviders } from './runtime/types' const topLevelDefaults = { isEnabled: true, @@ -35,8 +35,9 @@ const defaultsByBackend: { [key in SupportedAuthProviders]: DeepRequired @@ -49,7 +49,7 @@ const signOut: SignOutFunc = async (signOutOptions) => { const config = useTypedBackendConfig(runtimeConfig, 'local') const { data, rawToken, token } = await callWithNuxt(nuxt, useAuthState) - const headers = new Headers({ [config.token.headerName]: token.value } as HeadersInit) + const headers = new Headers(config.token.headerName ? { [config.token.headerName]: token.value } as HeadersInit : undefined) data.value = null rawToken.value = null @@ -76,7 +76,7 @@ const getSession: GetSessionFunc = async (getSessionO return } - const headers = new Headers(token.value ? { [config.token.headerName]: token.value } as HeadersInit : undefined) + const headers = new Headers(token.value && config.token.headerName ? { [config.token.headerName]: token.value } as HeadersInit : undefined) loading.value = true try { diff --git a/src/runtime/composables/local/useAuthState.ts b/src/runtime/composables/local/useAuthState.ts index b12f6b6d..3c21f50f 100644 --- a/src/runtime/composables/local/useAuthState.ts +++ b/src/runtime/composables/local/useAuthState.ts @@ -1,9 +1,9 @@ -import { computed, watch, ComputedRef } from 'vue' import { CookieRef } from '#app' +import { useCookie, useRuntimeConfig, useState } from '#imports' +import { ComputedRef, computed, watch } from 'vue' +import { useTypedBackendConfig } from '../../helpers' import { CommonUseAuthStateReturn } from '../../types' import { makeCommonAuthState } from '../commonAuthState' -import { useTypedBackendConfig } from '../../helpers' -import { useRuntimeConfig, useCookie, useState } from '#imports' // @ts-expect-error - #auth not defined import type { SessionData } from '#auth' @@ -19,7 +19,7 @@ export const useAuthState = (): UseAuthStateReturn => { const commonAuthState = makeCommonAuthState() // Re-construct state from cookie, also setup a cross-component sync via a useState hack, see https://github.com/nuxt/nuxt/issues/13020#issuecomment-1397282717 - const _rawTokenCookie = useCookie('auth:token', { default: () => null, maxAge: config.token.maxAgeInSeconds, sameSite: config.token.sameSiteAttribute }) + const _rawTokenCookie = useCookie(config.token.name, { default: () => null, maxAge: config.token.maxAgeInSeconds, sameSite: config.token.sameSiteAttribute, secure: config.token.secure, domain: config.token.domain }) const rawToken = useState('auth:raw-token', () => _rawTokenCookie.value) watch(rawToken, () => { _rawTokenCookie.value = rawToken.value }) @@ -28,7 +28,17 @@ export const useAuthState = (): UseAuthStateReturn => { if (rawToken.value === null) { return null } - return config.token.type.length > 0 ? `${config.token.type} ${rawToken.value}` : rawToken.value + + if (config.token.type.length > 0) { + switch (config.token.type) { + case 'Cookie': + return `${config.token.name}=${rawToken.value}` + case 'Bearer': + default: + return `${config.token.type} ${rawToken.value}` + } + } + return rawToken.value }) const setToken = (newToken: string | null) => { diff --git a/src/runtime/types.ts b/src/runtime/types.ts index 3563f6de..49bf74da 100644 --- a/src/runtime/types.ts +++ b/src/runtime/types.ts @@ -1,5 +1,5 @@ -import type { Ref, ComputedRef } from 'vue' import { RouterMethod } from 'h3' +import type { ComputedRef, Ref } from 'vue' import { SupportedProviders } from './composables/authjs/useAuth' /** @@ -110,6 +110,12 @@ type ProviderLocal = { * Settings for the authentication-token that `nuxt-auth` receives from the `signIn` endpoint and that can be used to authenticate subsequent requests. */ token?: { + /** + * The name of the cookie to store the authentication-token in. + * + * @default auth:token Access the cookie `auth:token` from session + */ + name?: string, /** * How to extract the authentication-token from the sign-in response. * @@ -133,14 +139,14 @@ 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 - * @example Auth + * @example Cookie */ headerName?: 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. * * Note: Your backend may reject / expire the token earlier / differently. - * @default 1800 + * @default undefined * @example 60 * 60 * 24 */ maxAgeInSeconds?: number, @@ -150,7 +156,22 @@ type ProviderLocal = { * @default 'lax' * @example 'strict' */ - sameSiteAttribute?: boolean | 'lax' | 'strict' | 'none' | undefined, + sameSiteAttribute?: boolean | 'lax' | 'strict' | 'none' | undefined, + /** + * Specifies the boolean value for the Secure Set-Cookie attribute. When truthy, the Secure attribute is set; otherwise it is not. By default, the Secure attribute is not set. + * Note: Be careful when setting this to true, as compliant clients will not allow client-side JavaScript to see the cookie in document.cookie. + * @default false + * @example true + */ + secure?: boolean, + /** + * Specifies the value for the Domain Set-Cookie attribute. By default, no domain is set, and most clients will consider applying the cookie only to the current domain. + * + * @default undefined use + * @example 'domain.com' + */ + domain?: boolean, + }, /** * Define an interface for the session data object that `nuxt-auth` expects to receive from the `getSession` endpoint.