Skip to content

awecode/nuxt-better-auth-layer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

55 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Auth Layer

Nuxt layer providing magic link authentication with better-auth.

Setup

Install layer and dependencies

pnpx giget gh:awecode/nuxt-better-auth-layer layers/auth
pnpm install better-auth aws4fetch
cp -r layers/auth/server/assets server/ 2>/dev/null || (mkdir -p server && cp -r layers/auth/server/assets server/) # Because nitro does not seem to copy the assets folder inside layers to build

Configure better-auth

The layer provides a default configuration for better-auth in the file server/utils/auth.config.ts. You can modify this to your needs. Client configuration is in the file composables/auth.client.config.ts.

The default configuration uses drizzle adapter and expects drizzle db instance available with useDb() exported from server/utils/db.ts.

Migrate database

Generate the better-auth schema with the following command. Change the schema file paths to match your own if necessary.

npx @better-auth/cli@latest generate --config layers/auth/server/utils/auth.ts --output server/db/auth_schema.ts --yes
echo -e "\nexport * from './auth_schema'" >> server/db/schema.ts
npx eslint --fix server/db/auth_schema.ts

Migrate the database.

pnpm drizzle-kit generate
pnpm drizzle-kit migrate

Using with Tailwind

Tailwind components are provided with the layer. To use them, install Tailwind. And add @source '../../../layers/'; to your app/assets/css/main.css to prevent the layer's tailwind classes from being purged by Tailwind's tree-shaking.

echo -e "\n@source '../../../layers/';" >> app/assets/css/main.css

Configure environment variables

# SES Configuration (required for production for sending emails)
SES_REGION=us-east-1
SES_ACCESS_KEY_ID=your_access_key_id
SES_SECRET_KEY=your_secret_key
[email protected]

# Access restrictions (optional - see below)
NUXT_AUTH_ALLOWED_DOMAINS=example.com,company.org
[email protected],[email protected]
[email protected],[email protected]

# API route protection (optional - see below)
NUXT_AUTH_AUTHENTICATED_ONLY_API_ROUTES=/api/user,/api/profile
NUXT_AUTH_ADMIN_ONLY_API_ROUTES=/api/admin,/api/management

# Auth redirects (optional - defaults provided)
NUXT_PUBLIC_AUTH_REDIRECT_USER_TO=/
NUXT_PUBLIC_AUTH_REDIRECT_NEW_USER_TO=/welcome
NUXT_PUBLIC_AUTH_REDIRECT_ERROR_TO=/auth/error
NUXT_PUBLIC_AUTH_REDIRECT_GUEST_TO=/login
NUXT_PUBLIC_AUTH_AUTH_REQUIRED_BY_DEFAULT=true

useAuth Composable

const { 
  client,          // Better-auth client
  session,         // Current session (reactive)
  user,           // Current user (reactive)
  loggedIn,       // Boolean computed from session
  signIn,         // Sign-in methods
  signOut,        // Sign out function
  fetchSession,   // Manually refetch session
  options         // Runtime config options
} = useAuth()

Components

MagicLinkLogin

Email-based authentication component. Sends both a clickable link and a one-time token that users can enter manually.

<template>
  <MagicLinkLogin />
</template>

Page Protection

Require Authentication

definePageMeta({
  auth: true
})

Guest Only (redirect authenticated users)

definePageMeta({
  auth: { only: 'guest' }
})

Custom Redirects

definePageMeta({
  auth: {
    redirectUserTo: '/dashboard',
    redirectGuestTo: '/signup'
  }
})

Disable Auth (public page)

definePageMeta({
  auth: false
})

Session Utilities

Server-side utilities for working with authentication sessions and users:

// Get session (returns null if not authenticated)
const session = await getAuthSession(event)

// Get user (returns undefined if not authenticated)
const user = await getUser(event)

// Throw error if not authenticated (401)
await requireAuthenticated(event)

// Throw error if not authenticated or not admin (401/403)
await requireAdmin(event)

getAuthSession(event)

Retrieves the current session from the event context. If not already cached, fetches it from the auth API.

getUser(event)

Convenience function that returns the user from the session, or undefined if not authenticated.

requireAuthenticated(event)

Throws a 401 Unauthorized error if the user is not authenticated. Use this for manual session validation.

requireAdmin(event)

Throws a 401 Unauthorized error if not authenticated, or 403 Forbidden if the user doesn't have admin role.

API Route Protection

Route Protection via Nuxt Config

You can protect multiple API routes at once using nuxt config or environment variables. This applies protection automatically via middleware without needing to specify in each event handler.

export default defineNuxtConfig({
  runtimeConfig: {
    auth: {
        authenticatedOnlyApiRoutes: '/api/user,/api/profile',
        adminOnlyApiRoutes: '/api/admin,/api/superadmin',
      },
  },
})
  • You can also use the NUXT_AUTH_AUTHENTICATED_ONLY_API_ROUTES and NUXT_AUTH_ADMIN_ONLY_API_ROUTES environment variables to set the routes.
  • Protecting /api/user will also protect /api/user and anything under it like /api/user/profile and /api/user/settings but won't protect something like /api/user-registration.

Individual Route Protection

Allowing only authenticated users

Using handler wrapper:

// server/api/authenticated.ts
export default defineAuthenticatedHandler(async (event) => {
  const { user } = event.context.auth
  return { message: `Hello user - ${user.email}` }
})

Or using manual validation:

// server/api/authenticated.ts
export default defineEventHandler(async (event) => {
  await requireAuthenticated(event)
  const user = await getUser(event)
  return { message: `Hello user - ${user.email}` }
})

Allowing only admin users

Using handler wrapper:

// server/api/admin.ts
export default defineAdminHandler(async (event) => {
  const { user } = event.context.auth
  return { message: `Hello admin - ${user.email}` }
})

Or using manual validation:

// server/api/admin.ts
export default defineEventHandler(async (event) => {
  await requireAdmin(event)
  const user = await getUser(event)
  return { message: `Hello admin - ${user.email}` }
})

API Endpoints

  • POST /api/auth/magic-link-send - Send magic link email

    { email: '[email protected]' }
  • POST /api/auth/magic-link-verify - Verify one-time token

    { token: 'ABC123' }

Bearer Token Authentication

For web applications, use the default cookie-based authentication. Bearer tokens are needed for mobile apps or other clients that can't use cookies. Use the token received during login as the bearer token.

# Example API call with Bearer token
curl -X POST https://yourapp.com/api/protected \
  -H "Authorization: Bearer t0k3ng035h3r3..." \
  -H "Content-Type: application/json" \
  -d '{"data": "example"}'

Access Restrictions by Email Address or Domain

You can restrict authentication to specific email domains or individual email addresses using environment variables. To enable this, use the allowDomains and/or allowEmails hook utils in better auth config hooks.

// server/utils/auth.ts
import { createAuthMiddleware } from 'better-auth/api'
import { allowDomains, allowEmails, setAdminForEmail } from '../lib/hook-utils'

export const auth = betterAuth({
  // ...
  hooks: {
    before: createAuthMiddleware(async (ctx) => {
      // Only allow emails from configured domains as users
      allowDomains(ctx)
      // Only allow configured emails as users
      allowEmails(ctx)
    }),
  },
  databaseHooks: {
    user: {
      create: {
        before: async (user) => {
          // Automatically set admin role for configured emails
          return setAdminForEmail(user)
        },
      },
    },
  },
})

Allowed Domains

Restrict sign-ups to specific email domains:

# Allow only users with @example.com or @company.org emails
NUXT_AUTH_ALLOWED_DOMAINS=example.com,company.org
# Allow any domain (default behavior)
NUXT_AUTH_ALLOWED_DOMAINS=*

Allowed Emails

Restrict sign-ups to specific email addresses:

# Allow only these specific email addresses
[email protected],[email protected]
# Allow any email (default behavior)
NUXT_AUTH_ALLOWED_EMAILS=*

Admin Role Assignment

Automatically assign admin role to specific email addresses during user creation:

# Automatically set admin role for these email addresses
[email protected],[email protected]

Email Templates

Customize the magic link email template at server/assets/magic-link.html.

Available template variables:

  • {{email}} - User's email
  • {{token}} - One-time token
  • {{url}} - Magic link URL
  • {{date}} - Current date
  • {{time}} - Current time (UTC)
  • {{useragent}} - Short Browser/OS info like Firefox, Linux

Credits

About

Nuxt Layer Providing Authentication with Better Auth

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published