Skip to content
Browse documentation
On this page

Auth and teams

How sign-in, sessions, teams, roles and invitations work with Better Auth.

Authentication uses Better Auth with its organization plugin. In Emberstack an organization is a team.

How it's wired

  • src/lib/betterauth.ts configures Better Auth with the Drizzle adapter on D1, email and password sign-in, and the organization plugin.
  • Passwords are hashed with PBKDF2 through Web Crypto, which is built into Workers and fast enough for their CPU limits.
  • src/pages/api/auth/[...all].ts hands every /api/auth/* request to Better Auth.
  • src/middleware.ts reads the session on each request and sets the request locals below.

Local

Value

user

The signed-in user, with a numeric id, or null

session

The Better Auth session, or null

organizationId

The team the user is working in

memberRole

The user's role in that team: owner, admin or member

Teams

When someone signs up, Emberstack creates a team named after them and makes them its owner. The team switcher in the dashboard changes the active team, and middleware always checks the user is still a member before it trusts that choice.

People are invited from the Team page. The invite form uses the inviteTeamMember action in src/actions/auth.ts.

Protecting pages

Every page and endpoint under /app checks for a user itself. There's no global guard.

---
if (!Astro.locals.user) return Astro.redirect('/login');
if (Astro.locals.memberRole !== 'owner') return new Response('Forbidden', { status: 403 });
---

Check roles on the server. Hiding a button in the UI isn't a permission check.

Calling Better Auth

Use auth.api.* from pages and endpoints, and pass the request headers so it acts as the current user:

import { auth } from '@/lib/betterauth';

await auth.api.changePassword({
  headers: Astro.request.headers,
  body: { currentPassword, newPassword },
});

When a call needs to set cookies, such as sign-in or sign-up, pass asResponse: true and copy the response headers onto yours. src/pages/login.astro shows how.

Adding providers and plugins

Look the plugin up in the current Better Auth docs, add it in betterauth.ts and its client plugin in betterauth-client.ts. If it adds tables or columns, mirror them in src/db/schema.ts and run a migration (see Database). OAuth providers go in socialProviders.