Skip to content

Commit

Permalink
enh: propagate the baseURL from server to client via plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
phoenix-ru committed Nov 28, 2024
1 parent 2ece891 commit 5d8bd04
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 11 deletions.
10 changes: 9 additions & 1 deletion src/runtime/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getHeader } from 'h3'
import authMiddleware from './middleware/auth'
import { getNitroRouteRules } from './utils/kit'
import { FetchConfigurationError } from './utils/fetch'
import { resolveApiBaseURL } from './utils/url'
import { _refreshHandler, addRouteMiddleware, defineNuxtPlugin, useAuth, useAuthState, useRuntimeConfig } from '#imports'

export default defineNuxtPlugin(async (nuxtApp) => {
Expand All @@ -10,11 +11,18 @@ export default defineNuxtPlugin(async (nuxtApp) => {
const { getSession } = useAuth()

// use runtimeConfig
const runtimeConfig = useRuntimeConfig().public.auth
const wholeRuntimeConfig = useRuntimeConfig()
const runtimeConfig = wholeRuntimeConfig.public.auth
const globalAppMiddleware = runtimeConfig.globalAppMiddleware

const routeRules = import.meta.server ? getNitroRouteRules(nuxtApp._route.path) : {}

// Set the correct `baseURL` on the server,
// because the client would not have access to environment variables
if (import.meta.server) {
runtimeConfig.baseURL = resolveApiBaseURL(wholeRuntimeConfig)
}

// Skip auth if we're prerendering
let nitroPrerender = false
if (nuxtApp.ssrContext) {
Expand Down
21 changes: 13 additions & 8 deletions src/runtime/utils/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,27 @@ export function resolveApiUrlPath(
return endpointPath
}

// When internal routing is enabled, drop everything except path
const onlyPathname = !runtimeConfig.public.auth.disableInternalRouting

const baseURL = resolveApiBaseURL(runtimeConfig, onlyPathname)
const baseURL = resolveApiBaseURL(runtimeConfig)
return joinURL(baseURL, endpointPath)
}

export function resolveApiBaseURL(runtimeConfig: RuntimeConfig, returnOnlyPathname: boolean): string {
export function resolveApiBaseURL(runtimeConfig: RuntimeConfig, returnOnlyPathname?: boolean): string {
const authRuntimeConfig = runtimeConfig.public.auth

// If the user has not specified `returnOnlyPathname`, infer it automatically.
// When internal routing is enabled, drop everything except path.
if (returnOnlyPathname === undefined) {
returnOnlyPathname = !runtimeConfig.public.auth.disableInternalRouting
}

// Default to static runtime config (still overridable using `NUXT_PUBLIC_AUTH_BASE_URL`)
let baseURL = authRuntimeConfig.baseURL

// Override base URL using environment variable specified in `originEnvKey` if any.
// By default, would use `AUTH_ORIGIN`, can be changed by user
if (authRuntimeConfig.originEnvKey) {
// Note: the `server` condition is here because Nuxt explicitly filters out all the env variables for the Client build,
// thus the check can be safely dropped. Instead of it, the `runtime/plugin` would set the `baseURL` on the runtime config.
if (import.meta.server !== false && authRuntimeConfig.originEnvKey) {
// Override base URL using environment variable specified in `originEnvKey` if any.
// By default, would use `AUTH_ORIGIN`, can be changed by user
const envBaseURL = process.env[authRuntimeConfig.originEnvKey]
if (envBaseURL) {
baseURL = envBaseURL
Expand Down
35 changes: 34 additions & 1 deletion tests/authjs.url.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { resolveApiUrlPath } from '../src/runtime/utils/url'
import { resolveApiBaseURL, resolveApiUrlPath } from '../src/runtime/utils/url'

/*
* This spec file covers usecases of the `authjs` provider.
Expand Down Expand Up @@ -177,6 +177,39 @@ describe('endpoint path construction', () => {
vi.stubEnv('NUXT_PUBLIC_AUTH_BASE_URL', '/other')
expect(testResolve(process.env.NUXT_PUBLIC_AUTH_BASE_URL as string)).toBe('/other/signin')
})

it('works with double assignment', () => {
// This test case is made specifically to check how `resolveApiUrlPath` would behave
// when a default `baseURL` value is being overwritten by `runtime/plugin` with a value provided by `resolveApiBaseURL`.

// 1. `baseURL` is set to a user-provided value `https://default.example.com/api/auth`;
const initialBaseURL = 'https://example.com/api/auth'

// 2. User also provides `originEnvKey` and sets the env to a different value `https://changed.example.com/auth/v2`;
const newBaseURL = 'https://changed.example.com/auth/v2'
const expectedNewBaseURL = '/auth/v2'
const envName = 'AUTH_ORIGIN'
vi.stubEnv(envName, newBaseURL)

const runtimeConfig = mockRuntimeConfig(initialBaseURL, envName)

// 3. `runtime/plugin` tries to resolve the base and gets `https://changed.example.com/auth/v2` as a result;
const resolvedNewBaseURL = resolveApiBaseURL(runtimeConfig)
expect(resolvedNewBaseURL).toBe(expectedNewBaseURL)

// Unstub the env to emulate the client and verify that the call produces a different result
vi.unstubAllEnvs()
expect(resolveApiBaseURL(runtimeConfig)).not.toBe(expectedNewBaseURL)

// 4. `runtime/plugin` overwrites the `baseURL`;
runtimeConfig.public.auth.baseURL = resolvedNewBaseURL

// 5. Another code calls `resolveApiUrlPath` / `resolveApiBaseURL` and should get the changed value exactly.
const resolvedBaseURL = resolveApiBaseURL(runtimeConfig)
expect(resolvedBaseURL).toBe(expectedNewBaseURL)
const resolvedApiUrlPath = resolveApiUrlPath('/', runtimeConfig)
expect(resolvedApiUrlPath).toBe(expectedNewBaseURL)
})
})
})

Expand Down
34 changes: 33 additions & 1 deletion tests/local.url.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { resolveApiUrlPath } from '../src/runtime/utils/url'
import { resolveApiBaseURL, resolveApiUrlPath } from '../src/runtime/utils/url'

describe('endpoint path construction', () => {
describe('relative baseURL', () => {
Expand Down Expand Up @@ -171,6 +171,38 @@ describe('endpoint path construction', () => {
vi.stubEnv('NUXT_PUBLIC_AUTH_BASE_URL', '/other')
expect(testResolve(process.env.NUXT_PUBLIC_AUTH_BASE_URL as string)).toBe('/other/signin')
})

it('works with double assignment', () => {
// This test case is made specifically to check how `resolveApiUrlPath` would behave
// when a default `baseURL` value is being overwritten by `runtime/plugin` with a value provided by `resolveApiBaseURL`.

// 1. `baseURL` is set to a user-provided value `https://default.example.com/api/auth`;
const initialBaseURL = 'https://example.com/api/auth'

// 2. User also provides `originEnvKey` and sets the env to a different value `https://changed.example.com/auth`;
const expectedNewBaseURL = 'https://changed.example.com/auth'
const envName = 'AUTH_ORIGIN'
vi.stubEnv(envName, expectedNewBaseURL)

const runtimeConfig = mockRuntimeConfig(initialBaseURL, envName)

// 3. `runtime/plugin` tries to resolve the base and gets `https://changed.example.com/auth` as a result;
const resolvedNewBaseURL = resolveApiBaseURL(runtimeConfig)
expect(resolvedNewBaseURL).toBe(expectedNewBaseURL)

// Unstub the env to emulate the client and verify that the call produces a different result
vi.unstubAllEnvs()
expect(resolveApiBaseURL(runtimeConfig)).not.toBe(expectedNewBaseURL)

// 4. `runtime/plugin` overwrites the `baseURL`;
runtimeConfig.public.auth.baseURL = resolvedNewBaseURL

// 5. Another code calls `resolveApiUrlPath` / `resolveApiBaseURL` and should get the changed value exactly.
const resolvedBaseURL = resolveApiBaseURL(runtimeConfig)
expect(resolvedBaseURL).toBe(expectedNewBaseURL)
const resolvedApiUrlPath = resolveApiUrlPath('/', runtimeConfig)
expect(resolvedApiUrlPath).toBe(expectedNewBaseURL)
})
})
})

Expand Down

0 comments on commit 5d8bd04

Please sign in to comment.