DevCon-Comm uses Supabase Auth with Google OAuth for hosted organizer access and keeps a local shared-password fallback for development environments. Local/dev runs use the shared-password flow unless APP_DATA_SOURCE=supabase explicitly enables the Supabase runtime.
- An owner adds an organizer email in
/organizer-console/organizers. - The email is stored in
public.admin_membershipswith roleownerororganizer. - The organizer signs in from
/organizer-console/login. - The organizer chooses Google sign-in from
/organizer-console/login. - Supabase handles the Google OAuth redirect and returns to
/api/auth/admin/callbackwith an authorization code. - Hono forwards the code to
/organizer-console/auth/callbackonPUBLIC_APP_URL, where the browser completes the Supabase PKCE exchange. - The browser posts the temporary Supabase access token to
/api/auth/admin/exchange. - Hono verifies the token, checks the verified email against active
admin_memberships, stores an app-owned row inadmin_sessions, and sets thedevcon_adminHTTP-only cookie. - The callback route clears the browser Supabase session and redirects into the organizer console.
- Organizer APIs call
requireAdmin, which validates the session cookie, active membership, role, and request origin.
The browser Supabase client uses tab-scoped sessionStorage for PKCE storage so the code verifier survives the external Google redirect without being shared across multiple organizer tabs. After the app-owned session cookie is created, the callback signs out of Supabase in the browser. The app cookie contains only an opaque random session token; the hashed token is stored in Supabase.
The login screen stores the intended organizer destination in session storage before starting Google OAuth. If Supabase falls back to the configured Site URL and returns the OAuth code to a public route, the router forwards that code to /organizer-console/auth/callback and resumes the organizer sign-in flow.
On Cloudflare, /api/* requests can be proxied from Pages to the API Worker. The Worker must still redirect browser-facing OAuth callbacks back to the Pages origin from PUBLIC_APP_URL or PUBLIC_FRONTEND_ORIGIN; the Worker origin does not serve the Vue organizer routes.
| Role | Access |
|---|---|
owner |
Full organizer access, can grant owner or organizer access, can disable other owners while keeping at least one active owner, and can review the audit log |
organizer |
Organizer console and admin mutations, including adding or disabling other organizers, but cannot grant or revoke owner access |
| Table | Purpose |
|---|---|
admin_memberships |
Organizer email allowlist, role, status, and last login |
admin_sessions |
Hashed app session tokens and expiry metadata |
admin_audit_log |
Security-sensitive admin actions with actor, target, request path, IP, user-agent, and compact metadata |
Owners can review recent admin activity at /organizer-console/audit-log. The ledger is backed by public.admin_audit_log and records successful organizer mutations such as login/logout, organizer allowlist changes, Luma imports, event and checklist edits, media uploads, feedback status changes, attendance CSV import/removal, speaker access changes, talk review actions, and quiz builder changes.
Audit metadata should stay small and non-sensitive. Store identifiers, counts, statuses, and changed field names rather than raw CSV contents, feedback text, OAuth provider tokens, or full request bodies.
Before the first hosted login, insert the first owner manually with the Supabase SQL editor:
insert into public.admin_memberships (email, display_name, role)
values ('you@example.com', 'Your Name', 'owner')
on conflict (email) do update set
display_name = excluded.display_name,
role = 'owner',
status = 'active';After that owner signs in, they can add more organizer emails from the console.
Configure Google in Supabase Dashboard → Authentication → Sign In / Providers and Google Cloud Console before the first hosted organizer sign-in.
Required setup:
- In Google Cloud, create a Web OAuth client.
- Add your app origins to Authorized JavaScript origins, for example
https://events-management.pages.devandhttp://localhost:5173. - Add the Supabase-hosted callback URI shown on the Google provider page to Authorized redirect URIs.
- Paste the Google client id and client secret into the Supabase Google provider settings.
- Keep Supabase Site URL pointed at the deployed app origin so post-auth redirects return to the organizer surface.
Organizer access still depends on admin_memberships. A successful Google login does not grant organizer permissions unless the verified email is active in the allowlist.
For local development, keep Google OAuth pinned to http://localhost:5173. The login screen blocks Google sign-in on other local ports or 127.0.0.1 so Supabase does not fall back to the deployed Site URL.
If Supabase admin auth is not enabled, /organizer-console/login falls back to the local shared password:
ADMIN_PASSWORD=devcon-admin
ADMIN_SESSION_SECRET=replace-this-locallyThis fallback is for local development only. Hosted environments should configure APP_DATA_SOURCE=supabase, VITE_SUPABASE_URL, VITE_SUPABASE_ANON_KEY, and SUPABASE_SERVICE_ROLE_KEY so Google-based organizer auth is used.
- Admin cookies are
HttpOnly, path-scoped to/, and expire after 12 hours. - Cookies use
SameSite=Laxby default. Split-origin deployments that configurePUBLIC_FRONTEND_ORIGINuseSameSite=None; Secure. - State-changing admin requests reject unexpected
Originheaders. - Organizer management requires
ownerrole. - Audit log review requires
ownerrole. - Disabling a membership blocks future session validation for that email.
- The Supabase service-role key is used only on the server and must never use a
VITE_prefix.