Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
1451d41
feat: use more h3 native utilities in nuxt auth handler
BracketJohn Dec 1, 2022
862a9b6
cleanup: move fetch and url helpers to utils, improve some typing
BracketJohn Dec 1, 2022
64f95f2
cleanup: restructure useSession to prepare for sync, move url and nav…
BracketJohn Dec 1, 2022
86ab0d8
feat: move all state to one composable, resolve empty object todo, st…
BracketJohn Dec 1, 2022
b9f1664
feat: switch to plugin based approach fully
BracketJohn Dec 1, 2022
319c6b6
cleanup: no more async _fetch as its unused
BracketJohn Dec 1, 2022
568f817
cleanup/feat!: rename lastSync to lastRefreshedAt and expose + docume…
BracketJohn Dec 1, 2022
77cf882
fix: use `callWithNuxt` in nested composable calls
BracketJohn Dec 1, 2022
54d7b27
polishing: improve docs feature section, return promise from async fu…
BracketJohn Dec 1, 2022
20c1285
refactor: use url based on request event on client
BracketJohn Dec 1, 2022
5afda2a
Merge branch 'main' into make_useSession_sync
BracketJohn Dec 1, 2022
c8aec8b
refactor: only get session if it was not fetched server-side
BracketJohn Dec 1, 2022
f086505
refactor: dont make status readonly, return promise directly
BracketJohn Dec 1, 2022
5e5c9d6
refactor: stop using deprecated utility during module setup
BracketJohn Dec 1, 2022
ab7c448
feat: add note to local middleware docs, a bit better logging by modu…
BracketJohn Dec 1, 2022
b2f3fb7
polish: take over some env var config from next-auth nuxt example
BracketJohn Dec 1, 2022
19a118c
Merge branch 'main' into make_useSession_sync
BracketJohn Dec 1, 2022
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
174 changes: 82 additions & 92 deletions README.md

Large diffs are not rendered by default.

5 changes: 1 addition & 4 deletions docs/content/1.welcome/1.index.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,7 @@ Done! You can now use all user-related functionality, for example:
::code-group
```ts [Application side]
// file: e.g ~/pages/login.vue
const { status, data, signIn, signOut } = await useSession({
// Whether a session is required. If it is, a redirect to the signin page will happen if no active session exists
required: true
})
const { status, data, signIn, signOut } = useSession()

status.value // Session status: `unauthenticated`, `loading`, `authenticated`
data.value // Session data, e.g., expiration, user.email, ...
Expand Down
2 changes: 1 addition & 1 deletion docs/content/4.usage/0.index.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ To create your custom sign-in page you can use `signIn` to directly start a prov
</template>

<script setup lang="ts">
const { signIn } = await useSession({ required: false })
const { signIn } = useSession()
</script>
```

Expand Down
11 changes: 11 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@nuxt/kit": "^3.0.0-rc.12",
"defu": "^6.1.0",
"next-auth": "^4.14.0",
"requrl": "^3.0.2",
"ufo": "^1.0.0"
},
"devDependencies": {
Expand Down
11 changes: 6 additions & 5 deletions playground/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<p>See all available authentication & session information below. Navigate to different sub-pages to test out the app.</p>
<pre>Status: {{ status }}</pre>
<pre>Data: {{ data || 'no session data present, are you logged in?' }}</pre>
<pre>Last refreshed at: {{ lastRefreshedAt || 'no refresh happened' }}</pre>
<pre>Decoded JWT token: {{ token || 'no token present, are you logged in?' }}</pre>
<pre>CSRF Token: {{ csrfToken }}</pre>
<pre>Providers: {{ providers }}</pre>
Expand All @@ -19,12 +20,12 @@
-> globally protected page
</nuxt-link>
<br>
<nuxt-link to="/protected/inline">
-> inline protected page
<nuxt-link to="/protected/locally">
-> locally protected page
</nuxt-link>
<br>
<nuxt-link to="/protected/named">
-> named protected page
<nuxt-link to="/always-unprotected">
-> page that is always unprotected
</nuxt-link>
<br>
<nuxt-link to="/api/protected/inline" external>
Expand All @@ -45,7 +46,7 @@
<script setup lang="ts">
import { useSession, useRoute, useFetch, useRequestHeaders } from '#imports'

const { data, status, getCsrfToken, getProviders } = await useSession({ required: false })
const { data, status, lastRefreshedAt, getCsrfToken, getProviders } = useSession()

const providers = await getProviders()
const csrfToken = await getCsrfToken()
Expand Down
11 changes: 0 additions & 11 deletions playground/middleware/auth.global.ts

This file was deleted.

7 changes: 0 additions & 7 deletions playground/middleware/auth.ts

This file was deleted.

5 changes: 4 additions & 1 deletion playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@ import NuxtAuth from '..'

export default defineNuxtConfig({
// @ts-expect-error See https://github.com/nuxt/framework/issues/8931
modules: [NuxtAuth]
modules: [NuxtAuth],
auth: {
enableGlobalAppMiddleware: true
}
})
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<template>
<div>I'm a secret! My protection works via an named middleware.</div>
<div>I'm always public, even when the global auth middleware is enabled!</div>
</template>

<script setup lang="ts">
import { definePageMeta } from '#imports'

definePageMeta({
middleware: ['auth']
auth: false
})
</script>
4 changes: 2 additions & 2 deletions playground/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
sign in (github)
</button>
<br>
<button @click="signIn(undefined, { callbackUrl: '/protected/inline' })">
<button @click="signIn(undefined, { callbackUrl: '/protected/named' })">
sign in (with redirect to protected page)
</button>
<br>
Expand All @@ -29,5 +29,5 @@
<script setup lang="ts">
import { useSession } from '#imports'

const { getSession, signIn, signOut } = await useSession({ required: false })
const { getSession, signIn, signOut } = useSession()
</script>
2 changes: 1 addition & 1 deletion playground/pages/protected/globally.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<template>
<div>I'm a secret! My protection works via a global middleware.</div>
<div>I'm a secret! My protection works via a global middleware. If you turned off the global middleware, then I'll also be visible without authentication :(</div>
</template>
13 changes: 0 additions & 13 deletions playground/pages/protected/inline.vue

This file was deleted.

12 changes: 12 additions & 0 deletions playground/pages/protected/locally.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<template>
<div>I'm a secret! My protection works via the named `nuxt-auth` module middleware.</div>
</template>

<script setup lang="ts">
import { definePageMeta } from '#imports'

// Note: This is only for testing, it does not make sense to do this with `enableGlobalAppMiddleware` turned on
definePageMeta({
middleware: 'auth'
})
</script>
2 changes: 1 addition & 1 deletion playground/server/middleware/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { getServerSession } from '#auth'

export default eventHandler(async (event) => {
// Only protect a certain backend route
if (!event.req.url?.startsWith('/api/protected/middleware')) {
if (!event.node.req.url?.startsWith('/api/protected/middleware')) {
return
}

Expand Down
54 changes: 47 additions & 7 deletions src/module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineNuxtModule, useLogger, addImportsDir, createResolver, resolveModule, addTemplate } from '@nuxt/kit'
import { defineNuxtModule, useLogger, addImportsDir, createResolver, addTemplate, addPlugin, extendViteConfig } from '@nuxt/kit'
import defu from 'defu'
import { joinURL } from 'ufo'

Expand Down Expand Up @@ -29,13 +29,42 @@ interface ModuleOptions {
* @default /api/auth
*/
basePath: string | undefined
/**
* Whether to refresh the session every `X` milliseconds. Set this to `false` to turn it off. The session will only be refreshed if a session already exists.
*
* Setting this to `true` will refresh the session every second.
* Setting this to `false` will turn off session refresh.
* Setting this to a number `X` will refresh the session every `X` milliseconds.
*
* @example 1000
* @default false
*
*/
enableSessionRefreshPeriodically: number | boolean
/**
* Whether to refresh the session every time the browser window is refocused.
*
* @example false
* @default true
*/
enableSessionRefreshOnWindowFocus: boolean
/**
* Whether to add a global authentication middleware that protects all pages.
*
* @example true
* @default false
*/
enableGlobalAppMiddleware: boolean
}

const PACKAGE_NAME = 'nuxt-auth'
const defaults: ModuleOptions & { basePath: string } = {
isEnabled: true,
origin: undefined,
basePath: '/api/auth'
basePath: '/api/auth',
enableSessionRefreshPeriodically: false,
enableSessionRefreshOnWindowFocus: true,
enableGlobalAppMiddleware: false
}
export default defineNuxtModule<ModuleOptions>({
meta: {
Expand All @@ -52,11 +81,11 @@ export default defineNuxtModule<ModuleOptions>({
return
}

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

// 2. Set up runtime configuration
const isOriginSet = Boolean(moduleOptions.origin)
// TODO: see if we can figure out localhost + port dynamically from the nuxt instance
// TODO: see if we can figure out localhost + port dynamically from the nuxt instance _for dev mode only_
const usedOrigin = moduleOptions.origin ?? 'http://localhost:3000'

const options = defu(moduleOptions, {
Expand All @@ -78,12 +107,12 @@ export default defineNuxtModule<ModuleOptions>({
isOriginSet
})
nuxt.options.runtimeConfig.public.auth = defu(nuxt.options.runtimeConfig.public.auth, {
...options,
url
})

// 3. Locate runtime directory
const { resolve } = createResolver(import.meta.url)
const resolveRuntimeModule = (path: string) => resolveModule(path, { paths: resolve('./runtime') })

// 4. Add nuxt-auth composables
const composables = resolve('./runtime/composables')
Expand All @@ -97,7 +126,7 @@ export default defineNuxtModule<ModuleOptions>({
nitroConfig.externals = defu(typeof nitroConfig.externals === 'object' ? nitroConfig.externals : {}, {
inline: [resolve('./runtime')]
})
nitroConfig.alias['#auth'] = resolveRuntimeModule('./server/services')
nitroConfig.alias['#auth'] = resolve('./runtime/server/services')
})

addTemplate({
Expand All @@ -115,6 +144,17 @@ export default defineNuxtModule<ModuleOptions>({
options.references.push({ path: resolve(nuxt.options.buildDir, 'types/auth.d.ts') })
})

logger.success('Auth module setup done')
// 6. Add plugin for initial load
addPlugin(resolve('./runtime/plugin'))

// 7. Setup next-auth env-variables just to be safe, see https://github.com/nextauthjs/next-auth/blob/6280fe9e10bd123aeab576398d1e807a5ac37edc/apps/playground-nuxt/src/module.ts#L32-L38
extendViteConfig((config) => {
config.define = config.define || {}
config.define['process.env.NEXTAUTH_URL'] = JSON.stringify(url)
config.define['process.env.NEXTAUTH_URL_INTERNAL'] = JSON.stringify(url)
config.define['process.env.VERCEL_URL'] = JSON.stringify(process.env.VERCEL_URL)
})

logger.success('`nuxt-auth` setup done')
}
})
Loading