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

Feature: Option to remove server side auth #610

Merged
merged 37 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
d7148df
feat: Added option to remove server
KyleSmith0905 Dec 14, 2023
acc827d
Merge branch 'sidebase:main' into option-to-remove-server
KyleSmith0905 Dec 14, 2023
a85d3bf
lint: remove extra semicolon.
KyleSmith0905 Dec 15, 2023
d255810
docs: Added disableServerSideAuth to Nuxt Config docs.
KyleSmith0905 Dec 18, 2023
e6c1251
doc: Added consistent spacing in disableServerSideAuth JSDoc.
KyleSmith0905 Dec 18, 2023
21ec51e
Added route-based rules for cache-support.
KyleSmith0905 Dec 31, 2023
53e82fd
Merge pull request #1 from KyleSmith0905/use-nuxt-route-rules
KyleSmith0905 Dec 31, 2023
91d8da5
Made port a variable on nuxt config base url.
KyleSmith0905 Jan 1, 2024
66de79d
Capitalize PORT on Authjs Playground Nuxt Config (oops, I hope this f…
KyleSmith0905 Jan 1, 2024
6671ea5
Change `playground-authjs` CI port to 3000.
KyleSmith0905 Jan 1, 2024
4bc4de3
Merge branch 'option-to-remove-server' of https://github.com/KyleSmit…
KyleSmith0905 Jan 1, 2024
e040030
Merge branch 'main' into option-to-remove-server
zoey-kaiser Jan 2, 2024
acf4777
Merge branch 'main' into option-to-remove-server
zoey-kaiser Jan 8, 2024
86ffbae
Merge branch 'main' into option-to-remove-server
zoey-kaiser Jan 30, 2024
559a0ae
Remove cache on a route that wasn't supposed to be cached
KyleSmith0905 Feb 4, 2024
cce8fd6
Merge branch 'main' of https://github.com/sidebase/nuxt-auth into opt…
KyleSmith0905 Mar 15, 2024
9cb0b73
Store route matcher between sessions.
KyleSmith0905 Mar 24, 2024
e507404
fix: Typo referencing token when retrieving cookie.
KyleSmith0905 Mar 24, 2024
f89f85a
fix(docs): Typo with incorrect spelling of false in route config page
KyleSmith0905 Mar 24, 2024
98afc89
Merge branch 'option-to-remove-server' of https://github.com/KyleSmit…
KyleSmith0905 Mar 24, 2024
eccff95
fix(playground): Fixed created at variable being used for cached at.
KyleSmith0905 Mar 24, 2024
139efdc
Merge branch 'option-to-remove-server' of https://github.com/KyleSmit…
KyleSmith0905 Mar 24, 2024
68d84f5
fix(docs): Clarified what disable server side fetching does behind th…
KyleSmith0905 Mar 24, 2024
cc042f5
fix(docs): Added JSDoc example and fix misspelling on route rules types.
KyleSmith0905 Mar 24, 2024
8ac22da
fix(docs): Clarified that global setting results in caching.
KyleSmith0905 Mar 24, 2024
6c10a9a
Merge branch 'option-to-remove-server' of https://github.com/KyleSmit…
KyleSmith0905 Mar 24, 2024
9843c30
fix(playground): On SWR pages, display time.
KyleSmith0905 Mar 24, 2024
48f7425
fix: Fixed route rules incorrectly referenced.
KyleSmith0905 Mar 26, 2024
59a663c
fix: import from `#import` in route matcher to fix CI error
KyleSmith0905 Mar 26, 2024
15f134f
fix: import from `#import` in route matcher to fix CI error
KyleSmith0905 Mar 26, 2024
2219da7
Merge branch 'option-to-remove-server' of https://github.com/KyleSmit…
KyleSmith0905 Mar 27, 2024
dfb2ac7
Merge branch 'main' into option-to-remove-server
zoey-kaiser Apr 5, 2024
10f1307
fix: Prevent auth from route rules from erroring when undefined.
KyleSmith0905 Apr 6, 2024
08160bf
fix: Reverted CI changes.
KyleSmith0905 Apr 6, 2024
0d0a167
fix(CI): Add env variable to AuthJS example on build.
KyleSmith0905 Apr 7, 2024
24a4ab4
Merge branch 'main' into option-to-remove-server
zoey-kaiser Apr 8, 2024
0f8baa5
Merge branch 'main' into option-to-remove-server
phoenix-ru Apr 18, 2024
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
9 changes: 7 additions & 2 deletions .github/workflows/ci.yaml
KyleSmith0905 marked this conversation as resolved.
Show resolved Hide resolved
KyleSmith0905 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ jobs:
# Check building
- run: pnpm build

# start prod-app and curl from it
- run: "timeout 60 pnpm start & (sleep 45 && curl --fail localhost:$PORT)"
env:
AUTH_ORIGIN: "http://localhost:3001"
PORT: 3001
- name: Run Playwright tests using Vitest
run: pnpm test:e2e

Expand Down Expand Up @@ -133,5 +138,5 @@ jobs:
# start prod-app and curl from it
- run: "timeout 60 pnpm start & (sleep 45 && curl --fail localhost:$PORT)"
env:
AUTH_ORIGIN: "http://localhost:3001"
PORT: 3001
AUTH_ORIGIN: "http://localhost:3000"
PORT: 3000
85 changes: 85 additions & 0 deletions docs/content/1.getting-started/4.caching-content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
description: "Learn how to configure your project to support caching"
---

::alert{type="info"}
If you are using the following routeRules (`swr`, `isr`, `prerender`), you will need to read this. When prerendering your entire site using `nuxi generate`, this is done automatically.
::

# Caching Content

Often hosting providers offer caching on the edge. Most websites can experience incredible speeds (and cost savings) by taking advantage of caching. No cold start, no processing requests, no parsing Javascript... just HTML served immediately from a CDN.

By default we send the user's authentication data down to the client in the HTML. This might not be ideal if you're caching your pages. Users may be able to see other user's authentication data if not handled properly.

To add caching to your Nuxt app, follow the [Nuxt documentation on hybrid rendering](https://nuxt.com/docs/guide/concepts/rendering#hybrid-rendering).

## Configuration

::alert{type="warning"}
If you find yourself needing to server-rendered auth methods like `getProviders()`, you must set the `baseURL` option on the `auth` object. This applies in development too.
::

### Page Specific Cache Rules

If only a few of your pages are cached. Head over to the Nuxt config `routeRules`, add the `auth` key to your cached routes. Set `disableServerSideAuth` to `true`.

```ts
export default defineNuxtConfig({
modules: ['@sidebase/nuxt-auth'],
auth: {
// Optional - Needed for getProviders() method to work server-side
baseURL: 'http://localhost:3000',
},
routeRules: {
'/': {
swr: 86400000,
auth: {
disableServerSideAuth: true,
},
},
},
})
```

### Module Cache Rules

If all/most pages on your site are cached. Head over to the Nuxt config, add the `auth` key if not already there. Set `disableServerSideAuth` to `true`.

```ts
export default defineNuxtConfig({
modules: ['@sidebase/nuxt-auth'],
auth: {
disableServerSideAuth: true,
// Optional - Needed for getProviders() method to work server-side
baseURL: 'http://localhost:3000',
},
})
```

### Combining Configurations

Route-configured options take precedent over module-configured options. If you disabled server side auth in the module, you may still enable server side auth back by setting `auth.disableServerSideAuth` to `false`.

For example: It may be ideal to add caching to every page besides your profile page.

```ts
export default defineNuxtConfig({
modules: ['@sidebase/nuxt-auth'],
auth: {
disableServerSideAuth: true,
},
routeRules: {
// Server side auth is disabled on this page because of global setting
'/': {
swr: 86400000,
}
// Server side auth is enabled on this page - route rules takes priority.
'/profile': {
auth: {
disableServerSideAuth: false,
},
},
},
})
```
8 changes: 8 additions & 0 deletions docs/content/2.configuration/2.nuxt-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ interface ModuleOptions {
* Whether the module is enabled at all
*/
isEnabled?: boolean
/**
* Forces your server to send a "loading" authentication status on all requests, thus prompting the client to do a fetch. If your website has caching, this prevents the server from caching someone's authentication status.
*
* This effects the entire site, for route-specific rules, add `disableServerSideAuth` on `routeRules`.
*
* @default false
*/
disableServerSideAuth?: boolean;
/**
* Full url at which the app will run combined with the path to authentication. You can set this differently depending on your selected authentication-provider:
* - `authjs`: You must set the full URL, with origin and path in production. You can leave this empty in development
Expand Down
14 changes: 14 additions & 0 deletions docs/content/2.configuration/3.route-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Route Rules

Use the `auth`-key inside the `nuxt.config.ts` `routeRules` to configure page-specific settings.

```ts
interface RouteOptions {
/**
* Forces your server to send a "loading" status on a route, prompting the client to fetch on the client. If a specific page has caching, this prevents the server from caching someone's authentication status.
*
* @default false
*/
disableServerSideAuth: boolean;
}
```
9 changes: 9 additions & 0 deletions playground-authjs/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ export default defineNuxtConfig({
},
globalAppMiddleware: {
isEnabled: true
},
baseURL: `http://localhost:${process.env.PORT || 3000}`
},
routeRules: {
'/with-caching': {
swr: 86400000,
auth: {
disableServerSideAuth: true
}
}
}
})
4 changes: 4 additions & 0 deletions playground-authjs/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ definePageMeta({ auth: false })
-> guest mode
</nuxt-link>
<br>
<nuxt-link to="/with-caching">
-> cached page with swr
</nuxt-link>
<br>
<div>select one of the above actions to get started.</div>
</div>
</template>
16 changes: 16 additions & 0 deletions playground-authjs/pages/with-caching.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script setup lang="ts">
import { definePageMeta, ref, useState } from '#imports'

const clientRenderTime = ref<Date>(new Date())

const serverRenderTime = useState('server-render-date', () => new Date())

definePageMeta({ auth: false })
</script>

<template>
<div>
<p>Server Render Time: {{ serverRenderTime?.toISOString() }}</p>
<p>Client Render Time: {{ clientRenderTime?.toISOString() }}</p>
</div>
</template>
8 changes: 8 additions & 0 deletions playground-local/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,13 @@ export default defineNuxtConfig({
globalAppMiddleware: {
isEnabled: true
}
},
routeRules: {
'/with-caching': {
swr: 86400000,
auth: {
disableServerSideAuth: true
}
}
}
})
4 changes: 4 additions & 0 deletions playground-local/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ definePageMeta({ auth: false })
-> guest mode
</nuxt-link>
<br>
<nuxt-link to="/with-caching">
-> cached page with swr
</nuxt-link>
<br>
<div>select one of the above actions to get started.</div>
</div>
</template>
16 changes: 16 additions & 0 deletions playground-local/pages/with-caching.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script setup lang="ts">
import { definePageMeta, ref, useState } from '#imports'

const clientRenderTime = ref<Date>(new Date())

const serverRenderTime = useState('server-render-date', () => new Date())

definePageMeta({ auth: false })
</script>

<template>
<div>
<p>Server Render Time: {{ serverRenderTime?.toISOString() }}</p>
<p>Client Render Time: {{ clientRenderTime?.toISOString() }}</p>
</div>
</template>
8 changes: 8 additions & 0 deletions playground-refresh/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,13 @@ export default defineNuxtConfig({
globalAppMiddleware: {
isEnabled: true
}
},
routeRules: {
'/with-caching': {
swr: 86400000,
auth: {
disableServerSideAuth: true
}
}
}
})
4 changes: 4 additions & 0 deletions playground-refresh/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ definePageMeta({ auth: false })
-> guest mode
</nuxt-link>
<br>
<nuxt-link to="/with-caching">
-> cached page with swr
</nuxt-link>
<br>
<div>select one of the above actions to get started.</div>
</div>
</template>
16 changes: 16 additions & 0 deletions playground-refresh/pages/with-caching.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script setup lang="ts">
import { definePageMeta, ref, useState } from '#imports'

const clientRenderTime = ref<Date>(new Date())

const serverRenderTime = useState('server-render-date', () => new Date())

definePageMeta({ auth: false })
</script>

<template>
<div>
<p>Server Render Time: {{ serverRenderTime?.toISOString() }}</p>
<p>Client Render Time: {{ clientRenderTime?.toISOString() }}</p>
</div>
</template>
12 changes: 11 additions & 1 deletion src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type {

const topLevelDefaults = {
isEnabled: true,
disableServerSideAuth: false,
session: {
enableRefreshPeriodically: false,
enableRefreshOnWindowFocus: true
Expand Down Expand Up @@ -202,7 +203,16 @@ export default defineNuxtModule<ModuleOptions>({
(options.provider as any).sessionDataType
)
: '',
'}'
'}',
"declare module 'nitropack' {",
' interface NitroRouteRules {',
` auth?: import('${resolve('./runtime/types.ts')}').RouteOptions`,
' }',
' interface NitroRouteConfig {',
` auth?: import('${resolve('./runtime/types.ts')}').RouteOptions`,
' }',
'}',
'export {}'
].join('\n')
})

Expand Down
12 changes: 9 additions & 3 deletions src/runtime/composables/local/useAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { CommonUseAuthReturn, SignOutFunc, SignInFunc, GetSessionFunc, Seco
import { _fetch } from '../../utils/fetch'
import { jsonPointerGet, useTypedBackendConfig } from '../../helpers'
import { getRequestURLWN } from '../../utils/callWithNuxt'
import { formatToken } from '../../utils/local'
import { useAuthState } from './useAuthState'
// @ts-expect-error - #auth not defined
import type { SessionData } from '#auth'
Expand Down Expand Up @@ -74,13 +75,18 @@ const getSession: GetSessionFunc<SessionData | null | void> = async (getSessionO

const config = useTypedBackendConfig(useRuntimeConfig(), 'local')
const { path, method } = config.endpoints.getSession
const { data, loading, lastRefreshedAt, token, rawToken } = useAuthState()
const { data, loading, lastRefreshedAt, rawToken, token: tokenState, _internal } = useAuthState()

if (!token.value && !getSessionOptions?.force) {
let token = tokenState.value
// For cached responses, return the token directly from the cookie
token ??= formatToken(_internal.rawTokenCookie.value)
phoenix-ru marked this conversation as resolved.
Show resolved Hide resolved

if (!token && !getSessionOptions?.force) {
loading.value = false
return
}

const headers = new Headers(token.value ? { [config.token.headerName]: token.value } as HeadersInit : undefined)
const headers = new Headers(token ? { [config.token.headerName]: token } as HeadersInit : undefined)

loading.value = true
try {
Expand Down
27 changes: 19 additions & 8 deletions src/runtime/composables/local/useAuthState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import type { CookieRef } from '#app'
import { type CommonUseAuthStateReturn } from '../../types'
import { makeCommonAuthState } from '../commonAuthState'
import { useTypedBackendConfig } from '../../helpers'
import { useRuntimeConfig, useCookie, useState } from '#imports'
import { formatToken } from '../../utils/local'
import { useRuntimeConfig, useCookie, useState, onMounted } from '#imports'
// @ts-expect-error - #auth not defined
import type { SessionData } from '#auth'

Expand All @@ -12,6 +13,10 @@ interface UseAuthStateReturn extends CommonUseAuthStateReturn<SessionData> {
rawToken: CookieRef<string | null>,
setToken: (newToken: string | null) => void
clearToken: () => void
_internal: {
baseURL: string,
rawTokenCookie: CookieRef<string | null>
}
}

export const useAuthState = (): UseAuthStateReturn => {
Expand All @@ -24,12 +29,7 @@ export const useAuthState = (): UseAuthStateReturn => {
const rawToken = useState('auth:raw-token', () => _rawTokenCookie.value)
watch(rawToken, () => { _rawTokenCookie.value = rawToken.value })

const token = computed(() => {
if (rawToken.value === null) {
return null
}
return config.token.type.length > 0 ? `${config.token.type} ${rawToken.value}` : rawToken.value
})
const token = computed(() => formatToken(rawToken.value))

const setToken = (newToken: string | null) => {
rawToken.value = newToken
Expand All @@ -44,11 +44,22 @@ export const useAuthState = (): UseAuthStateReturn => {
rawToken
}

onMounted(() => {
phoenix-ru marked this conversation as resolved.
Show resolved Hide resolved
// When the page is cached on a server, set the token on the client
if (_rawTokenCookie.value && !rawToken.value) {
setToken(_rawTokenCookie.value)
}
})

return {
...commonAuthState,
...schemeSpecificState,
setToken,
clearToken
clearToken,
_internal: {
...commonAuthState._internal,
rawTokenCookie: _rawTokenCookie
}
}
}
export default useAuthState
Loading