Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#797, #878): set baseURL via environment variables and improve internal url detection #913

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"dev:prepare": "nuxt-module-build build --stub",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs"
"docs:preview": "vitepress preview docs",
"test:unit": "vitest"
},
"dependencies": {
"@nuxt/kit": "^3.12.4",
Expand Down Expand Up @@ -61,6 +62,7 @@
"ts-essentials": "^9.4.2",
"typescript": "^5.5.4",
"vitepress": "^1.3.1",
"vitest": "^1.6.0",
"vue-tsc": "^2.0.29"
},
"packageManager": "[email protected]+sha512.38dc6fba8dba35b39340b9700112c2fe1e12f10b17134715a4aa98ccf7bb035e76fd981cf0bb384dfa98f8d6af5481c2bef2f4266a24bfa20c34eb7147ce0b5e"
Expand Down
2 changes: 1 addition & 1 deletion playground-authjs/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default defineNuxtConfig({
globalAppMiddleware: {
isEnabled: true
},
baseURL: `http://localhost:${process.env.PORT || 3000}`
baseURL: `http://localhost:${process.env.PORT || 3000}/api/auth`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a breaking change

},
routeRules: {
'/with-caching': {
Expand Down
117 changes: 111 additions & 6 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 22 additions & 22 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ import {
useLogger
} from '@nuxt/kit'
import { defu } from 'defu'
import { joinURL } from 'ufo'
import { genInterface } from 'knitwork'
import type { DeepRequired } from 'ts-essentials'
import type { NuxtModule } from 'nuxt/schema'
import { getOriginAndPathnameFromURL, isProduction } from './runtime/helpers'
import { isProduction } from './runtime/helpers'
import type {
AuthProviders,
ModuleOptions,
Expand All @@ -26,6 +25,8 @@ import type {

const topLevelDefaults = {
isEnabled: true,
baseURL: '/api/auth',
disableInternalRouting: false as boolean,
disableServerSideAuth: false,
originEnvKey: 'AUTH_ORIGIN',
sessionRefresh: {
Expand Down Expand Up @@ -107,26 +108,16 @@ export default defineNuxtModule<ModuleOptions>({
const logger = useLogger(PACKAGE_NAME)

// 0. Assemble all options
const { origin, pathname = '/api/auth' } = getOriginAndPathnameFromURL(
userOptions.baseURL ?? ''
)

const selectedProvider = userOptions.provider?.type ?? 'authjs'

const options = {
...defu(userOptions, topLevelDefaults, {
computed: {
origin,
pathname,
fullBaseUrl: joinURL(origin ?? '', pathname)
}
}),
const options = defu({
// We use `as` to infer backend types correctly for runtime-usage (everything is set, although for user everything was optional)
provider: defu(
userOptions.provider,
defaultsByBackend[selectedProvider]
) as DeepRequired<AuthProviders>
}
}, userOptions, topLevelDefaults)

// 1. Check if module should be enabled at all
if (!options.isEnabled) {
Expand All @@ -136,15 +127,24 @@ export default defineNuxtModule<ModuleOptions>({

logger.info('`nuxt-auth` setup starting')

// 2. Set up runtime configuration
// 2.1. Disable internal routing for `local` provider when not specified otherwise
// https://github.com/sidebase/nuxt-auth/issues/797
if (userOptions.disableInternalRouting === undefined && selectedProvider === 'local') {
options.disableInternalRouting = true
}

// 2.2. Set up runtime configuration
if (!isProduction) {
const authjsAddition
= selectedProvider === 'authjs'
? ', ensure that `NuxtAuthHandler({ ... })` is there, see https://sidebase.io/nuxt-auth/configuration/nuxt-auth-handler'
: ''
logger.info(
`Selected provider: ${selectedProvider}. Auth API location is \`${options.computed.fullBaseUrl}\`${authjsAddition}`
)
const loggerMessages = [
`Selected provider: ${selectedProvider}.`,
// TODO Before merging PR: write the docs how `baseURL` can be changed in runtime (env `NUXT_PUBLIC_AUTH_BASE_URL`, `runtimeConfig.public.auth.baseURL`, etc.).
`Auth API location is \`${options.baseURL}\`, it can be changed using TODO.`
]
if (selectedProvider === 'authjs') {
loggerMessages.push('Ensure that the `NuxtAuthHandler({ ... })` is there, see https://auth.sidebase.io/guide/authjs/nuxt-auth-handler')
}

logger.info(loggerMessages.join(' '))
}

nuxt.options.runtimeConfig = nuxt.options.runtimeConfig || { public: {} }
Expand Down
21 changes: 12 additions & 9 deletions src/runtime/composables/authjs/useAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import type { AppProvider, BuiltInProviderType } from 'next-auth/providers/index
import { defu } from 'defu'
import { type Ref, readonly } from 'vue'
import { appendHeader } from 'h3'
import { determineCallbackUrl } from '../../utils/url'
import { getRequestURLWN, joinPathToApiURLWN, makeCWN, navigateToAuthPageWN } from '../../utils/callWithNuxt'
import { determineCallbackUrl, resolveApiUrlPath } from '../../utils/url'
import { _fetch } from '../../utils/fetch'
import { isNonEmptyObject } from '../../utils/checkSessionResult'
import type { CommonUseAuthReturn, GetSessionOptions, SignInFunc, SignOutFunc } from '../../types'
import { useTypedBackendConfig } from '../../helpers'
import { getRequestURLWN } from '../common/getRequestURL'
import type { SessionData } from './useAuthState'
import { navigateToAuthPageWN } from './utils/navigateToAuthPage'
import type { NuxtApp } from '#app/nuxt'
import { callWithNuxt } from '#app/nuxt'
import { createError, useAuthState, useNuxtApp, useRequestHeaders, useRuntimeConfig } from '#imports'
Expand Down Expand Up @@ -49,7 +50,9 @@ async function getCsrfToken() {
const headers = await getRequestCookies(nuxt)
return _fetch<{ csrfToken: string }>(nuxt, '/csrf', { headers }).then(response => response.csrfToken)
}
const getCsrfTokenWithNuxt = makeCWN(getCsrfToken)
function getCsrfTokenWithNuxt(nuxt: NuxtApp) {
return callWithNuxt(nuxt, getCsrfToken)
}

/**
* Trigger a sign in flow for the passed `provider`. If no provider is given the sign in page for all providers will be shown.
Expand All @@ -61,17 +64,16 @@ const getCsrfTokenWithNuxt = makeCWN(getCsrfToken)
type SignInResult = void | { error: string | null, status: number, ok: boolean, url: any }
const signIn: SignInFunc<SupportedProviders, SignInResult> = async (provider, options, authorizationParams) => {
const nuxt = useNuxtApp()
const runtimeConfig = await callWithNuxt(nuxt, useRuntimeConfig)

// 1. Lead to error page if no providers are available
const configuredProviders = await getProviders()
if (!configuredProviders) {
const errorUrl = await joinPathToApiURLWN(nuxt, 'error')
const errorUrl = resolveApiUrlPath('error', runtimeConfig)
return navigateToAuthPageWN(nuxt, errorUrl)
}

// 2. If no `provider` was given, either use the configured `defaultProvider` or `undefined` (leading to a forward to the `/login` page with all providers)
const runtimeConfig = await callWithNuxt(nuxt, useRuntimeConfig)

const backendConfig = useTypedBackendConfig(runtimeConfig, 'authjs')
if (typeof provider === 'undefined') {
// NOTE: `provider` might be an empty string
Expand All @@ -87,7 +89,7 @@ const signIn: SignInFunc<SupportedProviders, SignInResult> = async (provider, op
callbackUrl = await determineCallbackUrl(runtimeConfig.public.auth, () => getRequestURLWN(nuxt))
}

const signinUrl = await joinPathToApiURLWN(nuxt, 'signin')
const signinUrl = resolveApiUrlPath('signin', runtimeConfig)

const queryParams = callbackUrl ? `?${new URLSearchParams({ callbackUrl })}` : ''
const hrefSignInAllProviderPage = `${signinUrl}${queryParams}`
Expand Down Expand Up @@ -140,7 +142,6 @@ const signIn: SignInFunc<SupportedProviders, SignInResult> = async (provider, op

// At this point the request succeeded (i.e., it went through)
const error = new URL(data.url).searchParams.get('error')
// eslint-disable-next-line ts/no-use-before-define
await getSessionWithNuxt(nuxt)

return {
Expand Down Expand Up @@ -222,7 +223,9 @@ async function getSession(getSessionOptions?: GetSessionOptions): Promise<Sessio
headers
})
}
const getSessionWithNuxt = makeCWN(getSession)
function getSessionWithNuxt(nuxt: NuxtApp) {
return callWithNuxt(nuxt, getSession)
}

/**
* Sign out the current user.
Expand Down
Loading
Loading