Dyrecteddyrected
Guides

Adding Authentication

Add a users collection with login, JWT auth, and protected routes.

Authentication in Dyrected starts with an auth-enabled collection and a signing secret. From there you get login, JWT sessions (JWT is a JSON Web Token — a signed token the browser holds to prove who's logged in), and access rules you can apply to any collection or route. This guide sets all of that up and ends with protecting both your API and your frontend pages.

1. Add an auth collection

Turn any collection into an auth collection by setting auth: true. Dyrected adds the email and password fields for you, so you only declare the extra fields you want — here, a display name:

// dyrected.config.ts  (same for both frameworks)
export default defineConfig({
  collections: [
    {
      slug: 'users',
      auth: true,
      fields: [
        { name: 'name', label: 'Name', type: 'text' },
      ],
    },
  ],
})

2. Set the JWT secret

Dyrected signs session tokens with a secret you provide. Set it in your environment before anyone logs in:

# .env.local
DYRECTED_JWT_SECRET=a-long-random-secret-at-least-32-chars

The same secret must be set in every environment. Rotating it invalidates all existing sessions.


3. Log in

Logging in exchanges an email and password for a token and the user record. Store the token (a cookie is typical) and send it on future requests. Here it is in the plain SDK and wired into each framework:

import { createClient } from '@dyrected/sdk'

const client = createClient({ baseUrl: process.env.NEXT_PUBLIC_DYRECTED_URL! })

const { token, user } = await client.collection('users').login(
  '[email protected]',
  'hunter2'
)
// app/login/actions.ts
'use server'
import { cookies } from 'next/headers'
import { createClient } from '@dyrected/sdk'

const client = createClient({ baseUrl: process.env.NEXT_PUBLIC_DYRECTED_URL! })

export async function login(email: string, password: string) {
  const { token, user } = await client.collection('users').login(email, password)
  
  cookies().set('dyrected-token', token, { httpOnly: true, path: '/' })
  return user
}
<!-- components/LoginForm.vue -->
<script setup lang="ts">
const { login, user, logout } = useDyrectedAuth('users')

async function submit(e: Event) {
  const form = e.target as HTMLFormElement
  await login(form.email.value, form.password.value)
}
</script>

<template>
  <div v-if="user">
    Hello, {{ user.name }} — <button @click="logout">Log out</button>
  </div>
  <form v-else @submit.prevent="submit">
    <input name="email" type="email" placeholder="Email" />
    <input name="password" type="password" placeholder="Password" />
    <button type="submit">Log in</button>
  </form>
</template>

4. Protect API routes

With login working, restrict who can touch your data. Each collection takes an access block whose functions receive the signed-in user and return whether the action is allowed. This orders collection is readable and writable only by logged-in users, and each user can edit only their own orders:

// dyrected.config.ts — access functions work the same in both frameworks
{
  slug: 'orders',
  access: {
    read:   ({ user }) => !!user,
    create: ({ user }) => !!user,
    update: ({ user, doc }) => user?.sub === doc?.userId,
    delete: ({ user }) => user?.roles?.includes('admin'),
  },
}

5. Protect frontend routes

Access rules guard your data, but you also want to keep signed-out visitors away from pages like a dashboard. Redirect them at the routing layer by checking for the token:

// middleware.ts
import { NextRequest, NextResponse } from 'next/server'

export function middleware(req: NextRequest) {
  const token = req.cookies.get('dyrected-token')?.value
  if (!token) {
    return NextResponse.redirect(new URL('/login', req.url))
  }
  return NextResponse.next()
}

export const config = {
  matcher: ['/dashboard/:path*', '/account/:path*'],
}
// middleware/auth.ts
export default defineNuxtRouteMiddleware(() => {
  const { user } = useDyrectedAuth('users')
  if (!user.value) return navigateTo('/login')
})

Apply it to a page:

<!-- pages/dashboard.vue -->
<script setup>
definePageMeta({ middleware: 'auth' })
</script>

6. Get the current user

To show who's signed in, read the current user from the stored token. On the server you pass the token explicitly; in Nuxt the composable keeps it reactive for you:

// In a Server Component
import { cookies } from 'next/headers'
import { createClient } from '@dyrected/sdk'

const client = createClient({ baseUrl: process.env.NEXT_PUBLIC_DYRECTED_URL! })

async function getUser() {
  const token = cookies().get('dyrected-token')?.value
  if (!token) return null

  client.setToken(token)
  try {
    return await client.collection('users').me()
  } catch {
    return null
  }
}
<script setup lang="ts">
const { user } = useDyrectedAuth('users')
// reactive — updates automatically on login/logout
</script>

<template>
  <span v-if="user">{{ user.name }}</span>
</template>

Auth endpoint reference

All endpoints are prefixed with /api/collections/{slug}.

MethodPathDescription
POST/{slug}/loginEmail + password login. Returns { token, user }.
POST/{slug}/logoutStateless — returns { success: true }. Client discards the token.
GET/{slug}/meReturns the current user document (requires Authorization: Bearer <token>).
POST/{slug}/refresh-tokenExchange a valid JWT for a fresh one with a new expiry.
POST/{slug}/forgot-passwordSend a password-reset email. Accepts { email, resetUrl? }. Always returns 200.
POST/{slug}/reset-passwordReset password using the token from the reset email. Accepts { token, password }.
POST/{slug}/inviteSend an invite email. Requires auth. Accepts { email }.
POST/{slug}/accept-inviteCreate an account from an invite token. Accepts { token, password, ...extraFields }. Returns { token, user }.

The password field is always stripped from API read responses.


Next steps

On this page