diff --git a/docs/content/1.getting-started/2.options.md b/docs/content/1.getting-started/2.options.md index 2118d4a0..bbd756e7 100644 --- a/docs/content/1.getting-started/2.options.md +++ b/docs/content/1.getting-started/2.options.md @@ -38,7 +38,7 @@ Auto refesh tokens The function that get called if the `autoRefresh` fail -## `maxAgeRefreshToken` +## `cookieMaxAge` - Default: `604800` @@ -46,6 +46,18 @@ Need to be the same as specified in your directus config; this is the max amount Auto refesh tokens +## `cookieSameSite` + +- Default: `lax` + +The SameSite attribute for auth cookies. + +## `cookieSecure` + +- Default: false + +The Secure attribute for auth cookies. + ## `fetchUserParams` - No default - **Optional** diff --git a/docs/content/4.Types/8.DirectusInvite.md b/docs/content/4.Types/8.DirectusInvite.md index 841d9427..2a047028 100644 --- a/docs/content/4.Types/8.DirectusInvite.md +++ b/docs/content/4.Types/8.DirectusInvite.md @@ -9,7 +9,7 @@ export interface DirectusInviteCreation { invite_url?: string }; -export interface DirectusInviteAccept { +export interface DirectusAcceptInvite { token: string; password: string }; diff --git a/playground/nuxt.config.ts b/playground/nuxt.config.ts index aa1293bc..598c0294 100644 --- a/playground/nuxt.config.ts +++ b/playground/nuxt.config.ts @@ -5,5 +5,6 @@ export default defineNuxtConfig({ directus: { url: 'http://localhost:8055/', devtools: true, + maxAgeRefreshToken: 10000, } }) diff --git a/src/module.ts b/src/module.ts index 816767d9..ca7ae788 100644 --- a/src/module.ts +++ b/src/module.ts @@ -1,7 +1,7 @@ import { resolve } from 'path' import { fileURLToPath } from 'url' import { defu } from 'defu' -import { defineNuxtModule, addPlugin, addImportsDir, isNuxt2 } from '@nuxt/kit' +import { defineNuxtModule, addPlugin, addImportsDir } from '@nuxt/kit' import { joinURL } from 'ufo' import { DirectusQueryParams } from './runtime/types' @@ -60,11 +60,34 @@ export interface ModuleOptions { cookieNameRefreshToken?: string; /** - * The max age for the refresh token cookie in seconds. + * The max age for auth cookies in seconds. * This should match your directus env key REFRESH_TOKEN_TTL * @type string + * @default 604800 + */ + cookieMaxAge?: number; + + /** + * The max age for auth cookies in seconds. + * This should match your directus env key REFRESH_TOKEN_TTL + * @type string + * @default 604800 */ maxAgeRefreshToken?: number; + + /** + * The SameSite attribute for auth cookies. + * @type string + * @default 'lax' + */ + cookieSameSite?: 'strict' | 'lax' | 'none' | undefined; + + /** + * The Secure attribute for auth cookies. + * @type boolean + * @default false + */ + cookieSecure?: boolean; } export default defineNuxtModule({ @@ -83,28 +106,41 @@ export default defineNuxtModule({ devtools: false, cookieNameToken: 'directus_token', cookieNameRefreshToken: 'directus_refresh_token', - maxAgeRefreshToken: 604800 - }, - setup (options, nuxt) { - nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {} - nuxt.options.runtimeConfig.public.directus = defu(nuxt.options.runtimeConfig.public.directus, { - url: options.url, - autoFetch: options.autoFetch, - autoRefresh: options.autoRefresh, - onAutoRefreshFailure: options.onAutoRefreshFailure, - fetchUserParams: options.fetchUserParams, - token: options.token, - devtools: options.devtools, - cookieNameToken: options.cookieNameToken, - cookieNameRefreshToken: options.cookieNameRefreshToken, - maxAgeRefreshToken: options.maxAgeRefreshToken - }) + // Nuxt Cookies Docs @ https://nuxt.com/docs/api/composables/use-cookie + cookieMaxAge: 604800, + cookieSameSite: 'lax', + cookieSecure: false + }, + setup(options, nuxt) { + nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {}; + nuxt.options.runtimeConfig.public.directus = defu( + nuxt.options.runtimeConfig.public.directus, + { + url: options.url, + autoFetch: options.autoFetch, + autoRefresh: options.autoRefresh, + onAutoRefreshFailure: options.onAutoRefreshFailure, + fetchUserParams: options.fetchUserParams, + token: options.token, + devtools: options.devtools, + cookieNameToken: options.cookieNameToken, + cookieNameRefreshToken: options.cookieNameRefreshToken, + cookieMaxAge: options.cookieMaxAge || options.maxAgeRefreshToken, + cookieSameSite: options.cookieSameSite, + cookieSecure: options.cookieSecure + }) + const runtimeDir = fileURLToPath(new URL('./runtime', import.meta.url)) nuxt.options.build.transpile.push(runtimeDir) addPlugin(resolve(runtimeDir, 'plugin')) addImportsDir(resolve(runtimeDir, 'composables')) + if (options.maxAgeRefreshToken) { + console.warn( + 'maxAgeRefreshToken is deprecated, please use cookieMaxAge instead' + ); + } if (options.devtools) { const adminUrl = joinURL(nuxt.options.runtimeConfig.public.directus.url, '/admin/') diff --git a/src/runtime/composables/useDirectusAuth.ts b/src/runtime/composables/useDirectusAuth.ts index 266d1a78..48241ab1 100644 --- a/src/runtime/composables/useDirectusAuth.ts +++ b/src/runtime/composables/useDirectusAuth.ts @@ -186,6 +186,8 @@ export const useDirectusAuth = () => { register, inviteUser, acceptInvite, - loginWithProvider + main, + loginWithProvider, + setAuthCookies } } diff --git a/src/runtime/composables/useDirectusToken.ts b/src/runtime/composables/useDirectusToken.ts index f4cc8de0..011bd357 100644 --- a/src/runtime/composables/useDirectusToken.ts +++ b/src/runtime/composables/useDirectusToken.ts @@ -8,37 +8,36 @@ export const useDirectusToken = () => { const baseUrl = useDirectusUrl() const config = useRuntimeConfig().public - const token = (): CookieRef => { + /** + * Get or set cookie. + * @param name + * @private + */ + const _getOrSetCookie = (name: string) => { nuxtApp._cookies = nuxtApp._cookies || {} - if (nuxtApp._cookies[config.directus.cookieNameToken]) { - return nuxtApp._cookies[config.directus.cookieNameToken] + if (nuxtApp._cookies[name]) { + return nuxtApp._cookies[name] } - const cookie = useCookie(config.directus.cookieNameToken) - nuxtApp._cookies[config.directus.cookieNameToken] = cookie + const cookie = useCookie(name, { + maxAge: config.directus.cookieMaxAge, + sameSite: config.directus.cookieSameSite, + secure: config.directus.cookieSecure + }) + nuxtApp._cookies[name] = cookie return cookie } - const refreshToken = (): CookieRef => { - nuxtApp._cookies = nuxtApp._cookies || {} - if (nuxtApp._cookies[config.directus.cookieNameRefreshToken]) { - return nuxtApp._cookies[config.directus.cookieNameRefreshToken] - } + const token = (): CookieRef => { + return _getOrSetCookie(config.directus.cookieNameToken) + } - const cookie = useCookie(config.directus.cookieNameRefreshToken, { maxAge: config.directus.maxAgeRefreshToken }) - nuxtApp._cookies[config.directus.cookieNameRefreshToken] = cookie - return cookie + const refreshToken = (): CookieRef => { + return _getOrSetCookie(config.directus.cookieNameRefreshToken) } const expires = (): CookieRef => { - nuxtApp._cookies = nuxtApp._cookies || {} - if (nuxtApp._cookies.directus_token_expired_at) { - return nuxtApp._cookies.directus_token_expired_at - } - - const cookie = useCookie('directus_token_expired_at') - nuxtApp._cookies.directus_token_expired_at = cookie - return cookie + return _getOrSetCookie('directus_token_expired_at') } const refreshTokens = async (): Promise => { @@ -46,7 +45,7 @@ export const useDirectusToken = () => { const body = { refresh_token: refreshToken().value } - const data = await $fetch<{data: DirectusAuthResponse}>('/auth/refresh', { + const data = await $fetch<{ data: DirectusAuthResponse }>('/auth/refresh', { baseURL: baseUrl, body, method: 'POST' diff --git a/src/runtime/plugin.ts b/src/runtime/plugin.ts index 887664ea..9bc850ee 100644 --- a/src/runtime/plugin.ts +++ b/src/runtime/plugin.ts @@ -5,7 +5,7 @@ import { useDirectusUser } from './composables/useDirectusUser'; import { useDirectusAuth } from './composables/useDirectusAuth'; export default defineNuxtPlugin(async (nuxtApp) => { - + const config = useRuntimeConfig(); const { fetchUser } = useDirectusAuth(); const { token, checkAutoRefresh } = useDirectusToken(); @@ -19,12 +19,10 @@ export default defineNuxtPlugin(async (nuxtApp) => { } } - nuxtApp.hook('app:created', async () => { - if (process.server) { - await checkAutoRefresh(); - await checkIfUserExists(); - } - }) + // do the checks server-side, instead of using hook 'app:created', + // as this hook is not called on SSR=true (static generation) + await checkAutoRefresh(); + await checkIfUserExists(); nuxtApp.hook('page:start', async () => { if (process.client) { diff --git a/tsconfig.json b/tsconfig.json index 9dd826f9..18453631 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,3 +1,6 @@ { - "extends": "./playground/.nuxt/tsconfig.json" -} + "compilerOptions": { + "moduleResolution": "node", + "module": "ESNext" + } +} \ No newline at end of file