Aura Auth
Getting Started

Quick Start

Create your Aura Auth instance and start building authentication flows in simple steps.

Installation

Install Aura Auth in your project:

npm install @aura-stack/auth

Environment setup

Create a .env or .env.local file at the root of your project to store secrets securely.

Never commit your .env and .env.local files. Always use a secret manager in production.

Secret Key

A secret value used to sign and encrypt sessions and CSRF tokens. This should be a secure random value of at least 32 bytes (256 bits) in length, encoded in base64 or hex format.

Use openssl rand -base64 32 or openssl rand -hex 32 to generate a secure random value.

.env
AURA_AUTH_SECRET=

Salting Key

A random value used for salting the derivation keys for signing and encryption. This should also be a secure random value of at least 32 bytes (256 bits) in length, encoded in base64 or hex format.

Use openssl rand -base64 32 or openssl rand -hex 32 to generate a secure random value.

.env
AURA_AUTH_SALT=

Base URL

The base URL of your application, used to construct the full URLs.

.env
AURA_AUTH_BASE_URL=http://localhost:3000

Create an Auth instance

Create an auth.ts file inside a lib directory to configure your Auth instance.

@/lib/auth.ts
import { createAuth } from "@aura-stack/auth"

export const auth = createAuth({
  oauth: ["github", "gitlab", "bitbucket"],
})

export const { handlers, jose, api } = auth

Once the auth instance is created, you could export the handlers, jose utilities, and API methods for use in your application.

Mount HTTP Handlers

Mount the HTTP handlers in your server or API routes to handle the HTTP requests for authentication flows.

Create a new file in your server or API directory to manage the auth routes or create a route that delegates to the auth handlers.

app/api/auth/[...aura]/route.ts
import { handlers } from "@/lib/auth"

export const { GET, POST, PATCH } = handlers
pages/api/auth/[...aura].ts
import { handlers } from "@/lib/auth"
import type { NextApiRequest, NextApiResponse } from "next"

const getBaseURL = (request: NextApiRequest) => {
  const protocol = request.headers["x-forwarded-proto"] ?? "http"
  const host = request.headers["x-forwarded-host"] ?? request.headers.host
  return `${protocol}://${host}`
}

export const handler = async (req: NextApiRequest, res: NextApiResponse) => {
  const method = req.method ?? "GET"
  const handler = handlers[method as keyof typeof handlers]
  if (!handler) {
    return res.status(405).json({ error: `Method ${method} Not Allowed` })
  }
  const url = new URL(req.url!, getBaseURL(req))
  const webRequest = new Request(url, {
    method,
    headers: new Headers(req.headers as Record<string, string>),
    body: method !== "GET" && req.body ? JSON.stringify(req.body) : undefined,
  })
  try {
    const response = await handler(webRequest)
    if (response.status >= 300 && response.status < 400) {
      const location = response.headers.get("location")
      if (location) {
        response.headers.forEach((value, key) => res.setHeader(key, value))
        return res.redirect(response.status, location)
      }
    }
    response.headers.forEach((value, key) => res.setHeader(key, value))
    const data = await response.json()
    return res.status(response.status).json(data)
  } catch {
    return res.status(500).json({ error: "Internal Server Error" })
  }
}

export default handler
app/routes/api.auth.$.tsx
import { handlers } from "~/lib/auth"
import type { Route } from "./+types/api.auth.$"

export const loader = async ({ request }: Route.LoaderArgs) => {
  return handlers.GET(request)
}

export const action = async ({ request }: Route.ActionArgs) => {
  return handlers.ALL(request)
}
src/routes/api/auth.$.ts
import { handlers } from "@/lib/auth"
import { createFileRoute } from "@tanstack/react-router"

export const Route = createFileRoute("/api/auth/$")({
  server: {
    handlers: {
      GET: async ({ request }) => {
        return await handlers.GET(request)
      },
      POST: async ({ request }) => {
        return await handlers.POST(request)
      },
      PATCH: async ({ request }) => {
        return await handlers.PATCH(request)
      },
    },
  },
})
server/api/auth/[...aura].ts
import { handlers } from "#shared/auth"

export default defineEventHandler(async (event) => {
  const webRequest = toWebRequest(event)
  return await handlers.ALL(webRequest)
})
src/index.ts
import { Elysia } from "elysia"
import { toHandler } from "@/lib/auth"

const app = new Elysia()

app.all("/api/auth/*", toHandler)

app.listen(3000)
src/index.ts
import { Hono } from "hono"
import { toHandler, withAuth } from "@/lib/auth"

const app = new Hono()

app.all("/api/auth/*", toHandler)

export default app
src/index.ts
import express, { type Express } from "express"
import { toHandler, withAuth } from "@/lib/auth.js"

const app: Express = express()

app.use(express.json())
app.use(express.urlencoded({ extended: true }))

app.all("/api/auth/*", toHandler)

app.listen(PORT)
api/auth/index.ts
import { handlers } from "../_auth.js"

export const GET = async (request: Request) => {
  return await handlers.GET(request)
}

export const POST = async (request: Request) => {
  return await handlers.POST(request)
}

export const PATCH = async (request: Request) => {
  return await handlers.PATCH(request)
}
src/index.ts
import { handlers } from "./auth"

export default {
  async fetch(request): Promise<Response> {
    const pathname = new URL(request.url).pathname
    if (pathname === "/") {
      return new Response("Welcome to Aura Auth Cloudflare Worker App!")
    }
    if (pathname.startsWith("/api/auth/")) {
      return await handlers.ALL(request)
    }
    return new Response("Not Found", { status: 404 })
  },
} satisfies ExportedHandler<Env>
supabase/functions/auth/index.ts
import { handlers } from "../_shared/auth.ts"

import "@supabase/functions-js/edge-runtime.d.ts"

Deno.serve(async (request) => {
  const pathname = new URL(request.url).pathname
  if (pathname === "/") {
    return new Response("Welcome to Aura Auth Cloudflare Worker App!")
  }
  if (pathname.startsWith("/api/auth/")) {
    return await handlers.ALL(request)
  }
  return new Response("Not Found", { status: 404 })
})

Create Client Auth Instance

Create an auth-client.ts file in the same directory to create a client instance that can be used in your frontend code.

@/lib/auth-client.ts
import { createAuthClient } from "@aura-stack/auth/client"

export const authClient = createAuthClient({
  basePath: "/api/auth",
  baseURL: "http://localhost:3000",
})

export const { getSession, signIn, signInCredentials, updateSession, signOut } = authClient

On this page