Skip to content

Authentication

PinTeach uses cookie-based session authentication with two auth flows:

  • Teachers: Google OAuth
  • Students: Magic Links (email-based, zero friction)

Session cookie: pinteach_session (30-day expiry)

Method Path Description
GET /auth/google Start Google OAuth → redirect to consent
GET /auth/google/callback OAuth callback → creates/finds teacher, sets cookie
GET /auth/me Current user (teacher or student)
POST /auth/magic-link Send magic link to student (5/hr rate limit)
POST /auth/verify-magic-link Verify token → set cookie
POST /auth/stop-impersonation Exit teacher impersonation
POST /auth/logout Destroy session, clear cookies
GET /auth/dev-login/:teacherId DEV ONLY: Direct login
1. Teacher clicks "Login with Google"
2. Redirect to Google consent screen
3. Google redirects back with auth code
4. Backend exchanges code for tokens
5. Creates/finds teacher record
6. Creates session in auth_sessions table
7. Sets pinteach_session cookie (30 days)
1. Student enters email on public page
2. Backend sends email with magic link token
3. Student clicks link in email
4. Backend verifies token → creates session
5. Sets pinteach_session cookie (30 days)
{
"email": "student@example.com",
"teacherSlug": "maria-garcia"
}

Response: { "sent": true }

Rate limited to 5/hr per IP.

{
"role": "teacher",
"teacher": {
"id": "uuid",
"name": "Maria Garcia",
"email": "maria@example.com",
"slug": "maria-garcia",
"avatarUrl": "...",
"timezone": "Europe/Madrid",
"onboardingCompleted": true
}
}
{
"role": "student",
"student": {
"id": "uuid",
"name": "John Smith",
"email": "john@example.com",
"timezone": "America/New_York"
},
"impersonating": false
}

Teachers can impersonate students to view their portal:

POST /teacher/students/:id/impersonate

A secondary cookie preserves the teacher session. Exit with POST /auth/stop-impersonation.

The Fastify auth plugin (plugins/auth.ts) decorates the request:

  • request.teacherId — set for teacher routes
  • request.studentId — set for student routes

Routes under /teacher/ require teacher auth. Routes under /student/ require student auth.

For local development only:

GET /api/auth/dev-login/00000000-0000-4000-a000-000000000001

Sets the session cookie without OAuth flow. Disabled in production.