Skip to content

Commit

Permalink
feat: local with cookie session
Browse files Browse the repository at this point in the history
  • Loading branch information
benjipott committed Dec 4, 2023
1 parent 4bf3814 commit 29b92f3
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 28 deletions.
23 changes: 13 additions & 10 deletions src/module.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import {
defineNuxtModule,
useLogger,
createResolver,
addTemplate,
addImports,
addPlugin,
addRouteMiddleware,
addServerPlugin,
addImports,
addRouteMiddleware
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 {
AuthProviders,
ModuleOptions,
SupportedAuthProviders,
AuthProviders
SupportedAuthProviders
} from './runtime/types'

const topLevelDefaults = {
Expand Down Expand Up @@ -52,8 +52,11 @@ const defaultsByBackend: {
signInResponseTokenPointer: '/token',
type: 'Bearer',
headerName: 'Authorization',
sameSiteAttribute: 'lax',
name: 'auth:token',
maxAgeInSeconds: 30 * 60,
sameSiteAttribute: 'lax'
secure: false,
domain: undefined
},
sessionDataType: { id: 'string | number' }
},
Expand Down
20 changes: 13 additions & 7 deletions src/runtime/composables/local/useAuth.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { readonly, Ref } from 'vue'
import { callWithNuxt } from '#app/nuxt'
import { CommonUseAuthReturn, SignOutFunc, SignInFunc, GetSessionFunc, SecondarySignInOptions } from '../../types'
import { _fetch } from '../../utils/fetch'
import { readonly, Ref } from 'vue'
import { jsonPointerGet, useTypedBackendConfig } from '../../helpers'
import { CommonUseAuthReturn, GetSessionFunc, SecondarySignInOptions, SignInFunc, SignOutFunc } from '../../types'
import { getRequestURLWN } from '../../utils/callWithNuxt'
import { _fetch } from '../../utils/fetch'
import { useAuthState } from './useAuthState'
// @ts-expect-error - #auth not defined
import type { SessionData } from '#auth'
import { useNuxtApp, useRuntimeConfig, nextTick, navigateTo } from '#imports'
import { navigateTo, nextTick, useNuxtApp, useRuntimeConfig } from '#imports'

type Credentials = { username?: string, email?: string, password?: string } & Record<string, any>

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -80,7 +80,7 @@ const getSession: GetSessionFunc<SessionData | null | void> = 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 {
Expand Down Expand Up @@ -114,7 +114,13 @@ const signUp = async (credentials: Credentials, signInOptions?: SecondarySignInO
body: credentials
})

return signIn(credentials, signInOptions)
await nextTick(getSession)

const { callbackUrl, redirect = true, external } = signInOptions ?? {}
if (redirect) {
const urlToNavigateTo = callbackUrl ?? await getRequestURLWN(nuxt)
return navigateTo(urlToNavigateTo, { external })
}
}

interface UseAuthReturn extends CommonUseAuthReturn<typeof signIn, typeof signOut, typeof getSession, SessionData> {
Expand Down
22 changes: 16 additions & 6 deletions src/runtime/composables/local/useAuthState.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { computed, watch, ComputedRef } from 'vue'
import type { CookieRef } from '#app'
import type { CookieRef } from '#app/nuxt'
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'
import { useCookie, useRuntimeConfig, useState } from '#imports'
// @ts-expect-error - #auth not defined
import type { SessionData } from '#auth'

Expand All @@ -19,7 +19,7 @@ export const useAuthState = (): UseAuthStateReturn => {
const commonAuthState = makeCommonAuthState<SessionData>()

// 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<string | null>('auth:token', { default: () => null, maxAge: config.token.maxAgeInSeconds, sameSite: config.token.sameSiteAttribute })
const _rawTokenCookie = useCookie<string | null>(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 })
Expand All @@ -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) => {
Expand Down
31 changes: 26 additions & 5 deletions src/runtime/types.ts
Original file line number Diff line number Diff line change
@@ -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'

/**
Expand Down Expand Up @@ -119,6 +119,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.
*
Expand All @@ -142,14 +148,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;
Expand All @@ -159,8 +165,23 @@ 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.
*
Expand Down

0 comments on commit 29b92f3

Please sign in to comment.