Examples

Examples

Here are some common usage examples for elysia-auth.

Basic Authentication

import { Elysia } from 'elysia'
import { ElysiaAuth, authGuard } from 'elysia-auth'
import Google from '@auth/core/providers/google'
 
const app = new Elysia()
  .use(ElysiaAuth({
    providers: [
      Google({
        clientId: process.env.AUTH_GOOGLE_ID,
        clientSecret: process.env.AUTH_GOOGLE_SECRET
      })
    ],
    secret: process.env.AUTH_SECRET
  }))
  .get('/', () => 'Welcome to the home page')
  .group('/admin', app => app
    .use(authGuard())
    .get('/dashboard', () => 'Admin Dashboard')
  )
  .listen(3000)

Multi-Provider Setup

import { Elysia } from 'elysia'
import { ElysiaAuth } from 'elysia-auth'
import Google from '@auth/core/providers/google'
import GitHub from '@auth/core/providers/github'
import Credentials from '@auth/core/providers/credentials'
 
const app = new Elysia()
  .use(ElysiaAuth({
    providers: [
      Google({
        clientId: process.env.AUTH_GOOGLE_ID,
        clientSecret: process.env.AUTH_GOOGLE_SECRET
      }),
      GitHub({
        clientId: process.env.AUTH_GITHUB_ID,
        clientSecret: process.env.AUTH_GITHUB_SECRET
      }),
      Credentials({
        credentials: {
          email: { type: "email" },
          password: { type: "password" }
        },
        async authorize(credentials) {
          const user = await db.user.findUnique({
            where: { email: credentials.email }
          })
          if (user && verifyPassword(credentials.password, user.password)) {
            return user
          }
          return null
        }
      })
    ],
    secret: process.env.AUTH_SECRET
  }))

Role-Based Access Control

import { Elysia } from 'elysia'
import { authGuard } from 'elysia-auth'
 
// Define custom user type with roles
type User = {
  id: string
  email: string
  role: 'admin' | 'user'
}
 
const app = new Elysia()
  .use(authGuard<User>({
    getUserData: async (authUser) => {
      const user = await db.user.findUnique({
        where: { id: authUser.sub }
      })
      return {
        id: user.id,
        email: user.email,
        role: user.role
      }
    }
  }))
  .get('/admin', ({ session }) => {
    if (session.user.role !== 'admin') {
      throw new Error('Admin access required')
    }
    return 'Admin Panel'
  })

API Routes with Authentication

Either pass paths you want to protect in protectPaths(["/api","/api2"]) this will be added 1 time before routes

import { Elysia } from 'elysia'
import { protectPaths } from 'elysia-auth'
 
const app = new Elysia()
  .use(protectPaths(['/api']))
  .group('/api', app => app
    .get('/users', async () => {
      const users = await db.user.findMany()
      return users
    })
    .post('/users', async ({ body }) => {
      const user = await db.user.create({
        data: body
      })
      return user
    })
  )

Or you can use authGaurd() it will protect all routes this is under this middleware. this is scoped based, means it will only valid for single Elysia instance means in the below example, this is only applied to postsModule

import { Elysia } from 'elysia'
import { authGaurd } from 'elysia-auth'
 
const postsModule = new Elysia()
    .use(authGaurd())
    .get('/users', async () => {
      const users = await db.user.findMany()
      return users
    })
    .post('/users', async ({ body }) => {
      const user = await db.user.create({
        data: body
      })
      return user
    })

Session

After adding protectPaths or authGaurd there will be session object in Elysia Context

const postModules = new Elysia().use(authGuard()).get("/secret-api", (ctx) => {
  const { user, isAuthenticated, authUser } = ctx.session;
 
  return {
    user,
    isAuthenticated,
    authUser,
  };
});

Frontend Integration

For frontend you can either use template engine, means frontend is also on Elysia app. In that case you can simply go to /auth/signin for prebuild sign in form.

But in case of different client and server

You have to use form with post method and action will be ${backend_url}/auth/signin/github in case of github, github will be changed respectivly to outh, in case of credentials action will be ${backend_url}/auth/callback/credentials

You can use the following REST API endpoints from your client-side code. NB: Make sure to include the csrfToken in the request body for all sign-in and sign-out requests.

if you don’t want to manually hit endpoint then you can use @auth/react. It provides built-in handlers for credentials signin and oauth signin.

  • signInCredentials for credentials signin - it takes email, password, redirect url
  • signInOAuth for oauth signin - it takes provider and redirect url
  • signOut for signout - it takes redirect url

More details on @auth/react docs

If you different frontend then make sure to add these options in AuthOptions in ElysiaAuth()

It is so that after authentication user redirect back to frontend

  // add redirect in callback, where the baseUrl will be backend url and url will be the callbackUrl (in this case it should be frontend Url)
  callbacks: {
    redirect: async ({ baseUrl, url }) => {
      if (url.startsWith("http")) return url;
 
      // Allows relative callback URLs
      if (url.startsWith("/")) return `${baseUrl}${url}`;
 
      // Allows callback URLs on the same origin
      if (new URL(url).origin === baseUrl) return url;
 
      return baseUrl;
    },
  },
 
  // add custom signin & error page redirecting frontend
  pages: {
    signIn: "http://localhost:5173/signin",
    error: "http://localhost:5173?error=Auth+Error",
  },

Custom Error Handling

import { Elysia } from 'elysia'
import { authGuard } from 'elysia-auth'
 
const app = new Elysia()
  .use(authGuard({
    unauthorizedStatus: 403,
    onFailure: (set) => {
      set.status = 403
      return {
        error: 'Authentication required',
        code: 'AUTH_REQUIRED',
        redirect: '/auth/signin'
      }
    }
  }))

WebSocket Authentication

import { Elysia } from 'elysia'
import { authGuard } from 'elysia-auth'
 
const app = new Elysia()
  .ws('/chat', {
    beforeHandle: ({ session }) => {
      if (!session.isAuthenticated) {
        throw new Error('Authentication required')
      }
    },
    message: ({ data, session }) => {
      return {
        message: data,
        user: session.user.email
      }
    }
  })

Next Steps