-
Notifications
You must be signed in to change notification settings - Fork 0
Authentication System
The UiASub website includes an authentication system for internal tools, using Discord OAuth via Supabase for secure, role-based access control.
Internal tools like equipment management and storage tracking require authentication. The system leverages:
- Discord OAuth 2.0 for identity verification
- Supabase for backend authentication and database
- Supabase Edge Functions for serverless role verification
- localStorage for session persistence
User → Login Page → Discord OAuth → Supabase Auth
↓
Supabase Edge Function
↓
Discord API (Role Check)
↓
Return Token + Status
↓
Client stores token in localStorage
Trigger: User clicks "Logg Inn" / "Log In" in the header
Action: Redirects to pages/login.html
Page: pages/login.html
Process:
- User clicks the Discord login button
- Supabase redirects to Discord OAuth
- User authorizes the application
- Discord redirects back to the site
Script: js/login.js
Process:
// 1. Extract access token from URL
const token = supabase.auth.getSession()
// 2. Send token to Edge Function for role verification
const response = await fetch(edgeFunctionURL, {
headers: { Authorization: `Bearer ${token}` }
})
// 3. Edge Function checks Discord role
const hasRequiredRole = await checkDiscordRole(userId)
// 4. Return verification result
return { verified: hasRequiredRole, user: userData }If verified:
- Token stored in
localStorage.setItem('sb-auth-token', token) - User redirected to requested internal page
- Session persists across page loads
If not verified:
- Error message displayed
- User remains on login page
- No token stored
On every page load (js/header.js):
// Check if token exists
const token = localStorage.getItem('sb-auth-token')
if (token) {
// Verify token is still valid (cached for 5 minutes)
const isValid = await verifyToken(token)
if (isValid) {
// Show "Logged In" state
} else {
// Clear invalid token and show "Log In"
localStorage.removeItem('sb-auth-token')
}
}| File | Purpose |
|---|---|
pages/login.html |
Login page with Discord OAuth button |
js/login.js |
Handles OAuth flow and token verification |
js/header.js |
Checks auth status and updates UI |
js/equipment.js |
Authenticated equipment management |
js/storage.js |
Authenticated storage management |
| Resource | Purpose |
|---|---|
Edge Function: discord-role-sync
|
Verifies Discord roles server-side |
Database: equipment table |
Stores equipment data |
Database: storage table |
Stores storage data |
| Auth: Discord OAuth provider | Handles user authentication |
The Edge Function checks for specific Discord roles:
| Role Name | Access Level |
|---|---|
Member |
Read-only access to internal tools |
Leadership |
Full CRUD access to internal tools |
Note: Role names are configurable in the Edge Function.
Server-side role verification to prevent client-side bypass.
https://[project-id].supabase.co/functions/v1/discord-role-sync
fetch(endpoint, {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
}){
"verified": true,
"user": {
"id": "discord-user-id",
"username": "username#0000",
"roles": ["Member", "Leadership"]
}
}Features:
- View equipment inventory
- Add new equipment
- Assign equipment to members
- Return equipment
- Delete equipment records
Access Control:
- Read: All authenticated members
- Write: Leadership only
Features:
- View storage locations
- Add storage items
- Update item status
- Remove items
Access Control:
- Read: All authenticated members
- Write: Leadership only
-
Token Storage:
localStorage(secure, but not HTTP-only) - Token Validation: Verified on every protected page load
- Timeout: Tokens expire after session timeout
- Cache: Auth status cached for 5 minutes to reduce API calls
- Role Verification: All operations verified via Edge Function
- JWT Validation: Supabase validates JWT tokens
- Discord API: Roles fetched directly from Discord (cannot be spoofed)
- Row-Level Security: Supabase RLS policies enforce permissions
// User logs in successfully
localStorage.setItem('sb-auth-token', token)
localStorage.setItem('sb-user-data', JSON.stringify(userData))// Clear session
localStorage.removeItem('sb-auth-token')
localStorage.removeItem('sb-user-data')
supabase.auth.signOut()- Tokens expire after 1 hour (Supabase default)
- Refresh token used to renew session automatically
- Invalid tokens cleared automatically
The header (header.html + js/header.js) dynamically updates based on auth status:
<a href="/pages/login.html">Logg Inn</a><span>Logged In</span>
<a href="#" onclick="logout()">Logg Ut</a>Cause: User doesn't have required Discord role
Solution:
- Verify Discord server membership
- Check role assignment in Discord
- Contact admin to assign correct role
Cause: Session exceeded timeout period
Solution: Log in again (tokens auto-refresh if refresh token is valid)
Checklist:
- Logged in via Discord
- Token exists in localStorage
- Required Discord role assigned
- Edge Function is running
- Not using private/incognito mode (clears localStorage)
Debug:
// Check stored token
console.log(localStorage.getItem('sb-auth-token'))
// Test Edge Function manually
fetch('https://[project-id].supabase.co/functions/v1/discord-role-sync', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.then(console.log)- Supabase project created
- Discord OAuth app configured
- Edge Function deployed
-
Configure Discord OAuth in Supabase:
- Dashboard → Authentication → Providers → Discord
- Add Discord Client ID and Secret
- Set redirect URL:
https://[project-id].supabase.co/auth/v1/callback
-
Deploy Edge Function:
supabase functions deploy discord-role-sync
-
Set Environment Variables:
supabase secrets set DISCORD_BOT_TOKEN=your_bot_token supabase secrets set DISCORD_GUILD_ID=your_server_id
-
Update Client Configuration:
// js/login.js const SUPABASE_URL = 'https://[project-id].supabase.co' const SUPABASE_ANON_KEY = 'your_anon_key'