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)

MethodPathDescription
GET/auth/googleStart Google OAuth → redirect to consent
GET/auth/google/callbackOAuth callback → creates/finds teacher, sets cookie
GET/auth/meCurrent user (teacher or student)
POST/auth/magic-linkSend magic link to student (5/hr rate limit)
POST/auth/verify-magic-linkVerify token → set cookie
POST/auth/stop-impersonationExit teacher impersonation
POST/auth/logoutDestroy session, clear cookies
GET/auth/dev-login/:teacherIdDEV 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.