1import{j as e}from"./index-w8uym0Jr.js";import"./vendor-react-B5kyjNin.js";import"./vendor-auth-3IJ4rw9Z.js";import"./vendor-ui-zVkK_Prl.js";import"./vendor-payment-Cx_eeV9O.js";import"./vendor-framer-CGrNjTbd.js";function n(s){const r={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...s.components};return e.jsxs(e.Fragment,{children:[e.jsx(r.h1,{children:"Authentication"}),` 2`,e.jsx(r.p,{children:"This document outlines the authentication system used in ClassAct, which is built on Supabase Authentication."}),` 3`,e.jsx(r.h2,{children:"Overview"}),` 4`,e.jsx(r.p,{children:"ClassAct implements a comprehensive authentication system with multiple user roles and access levels. The authentication is handled through Supabase Auth, which provides secure JWT-based authentication."}),` 5`,e.jsx(r.h2,{children:"Authentication Context"}),` 6`,e.jsxs(r.p,{children:["The central piece of the authentication system is the ",e.jsx(r.code,{children:"AuthContext"})," which provides authentication state and methods throughout the application:"]}),` 7`,e.jsx(r.pre,{children:e.jsx(r.code,{className:"language-typescript",children:`{\`/**\r 8 * @fileoverview Authentication context for ClassAct application\r 9 * \r 10 * Features:\r 11 * - User authentication state management\r 12 * - Login, logout, and registration functions\r 13 * - Role-based access control\r 14 * \r 15 * @requires react\r 16 * @requires @supabase/supabase-js\r 17 * @requires ../utils/logger\r 18 */}\r 19\r 20import { createContext, useState, useEffect, useContext } from 'react';\r 21import { supabase } from '../services/supabaseClient';\r 22import { createLogger } from '../utils/logger';\r 23\r 24const logger = createLogger('AuthContext', true);\r 25\r 26export const AuthContext = createContext(null);\r 27\r 28export const useAuth = () => useContext(AuthContext);\r 29\r 30export const AuthProvider = ({ children }) => {\r 31 const [user, setUser] = useState(null);\r 32 const [isAuth, setIsAuth] = useState(false);\r 33 const [loading, setLoading] = useState(true);\r 34\r 35 // Implementation of auth functions...\r 36};\`} 37`})}),` 38`,e.jsx(r.h2,{children:"User Roles"}),` 39`,e.jsx(r.p,{children:"ClassAct has three levels of user permissions:"}),` 40`,e.jsx(r.pre,{children:e.jsx(r.code,{className:"language-typescript",children:`enum UserRole {\r 41 USER = 0,\r 42 ADMIN = 1\r 43} 44`})}),` 45`,e.jsx(r.p,{children:"Additionally, there is a special super admin role that is handled via environment variable rather than a role value for enhanced security."}),` 46`,e.jsx(r.h2,{children:"Authentication Flow"}),` 47`,e.jsxs("div",{className:"flow-diagram",children:[e.jsx("div",{className:"flow-title",children:"User Registration Process"}),e.jsxs("div",{className:"flow-steps",children:[e.jsxs("div",{className:"flow-step",children:[e.jsx("div",{className:"step-number",children:"1"}),e.jsxs("div",{className:"step-content",children:[e.jsx("div",{className:"step-title",children:"Registration Form"}),e.jsx("div",{className:"step-description",children:"User submits registration form with email and password"})]})]}),e.jsxs("div",{className:"flow-step",children:[e.jsx("div",{className:"step-number",children:"2"}),e.jsxs("div",{className:"step-content",children:[e.jsx("div",{className:"step-title",children:"Account Creation"}),e.jsx("div",{className:"step-description",children:"Account is created in Supabase Auth"})]})]}),e.jsxs("div",{className:"flow-step",children:[e.jsx("div",{className:"step-number",children:"3"}),e.jsxs("div",{className:"step-content",children:[e.jsx("div",{className:"step-title",children:"Profile Storage"}),e.jsx("div",{className:"step-description",children:"Profile data is stored in Supabase database"})]})]}),e.jsxs("div",{className:"flow-step",children:[e.jsx("div",{className:"step-number",children:"4"}),e.jsxs("div",{className:"step-content",children:[e.jsx("div",{className:"step-title",children:"Email Confirmation"}),e.jsx("div",{className:"step-description",children:"User receives a confirmation email"})]})]}),e.jsxs("div",{className:"flow-step",children:[e.jsx("div",{className:"step-number",children:"5"}),e.jsxs("div",{className:"step-content",children:[e.jsx("div",{className:"step-title",children:"Verification"}),e.jsx("div",{className:"step-description",children:"User verifies email by clicking link"})]})]})]})]}),` 48`,e.jsx(r.h2,{children:"Login Flow"}),` 49`,e.jsxs("div",{className:"flow-diagram",children:[e.jsx("div",{className:"flow-title",children:"User Login Process"}),e.jsxs("div",{className:"flow-steps",children:[e.jsxs("div",{className:"flow-step",children:[e.jsx("div",{className:"step-number",children:"1"}),e.jsxs("div",{className:"step-content",children:[e.jsx("div",{className:"step-title",children:"Login Form"}),e.jsx("div",{className:"step-description",children:"User submits login credentials"})]})]}),e.jsxs("div",{className:"flow-step",children:[e.jsx("div",{className:"step-number",children:"2"}),e.jsxs("div",{className:"step-content",children:[e.jsx("div",{className:"step-title",children:"Credential Validation"}),e.jsx("div",{className:"step-description",children:"Supabase validates credentials"})]})]}),e.jsxs("div",{className:"flow-step",children:[e.jsx("div",{className:"step-number",children:"3"}),e.jsxs("div",{className:"step-content",children:[e.jsx("div",{className:"step-title",children:"Token Storage"}),e.jsx("div",{className:"step-description",children:"JWT token is stored in browser"})]})]}),e.jsxs("div",{className:"flow-step",children:[e.jsx("div",{className:"step-number",children:"4"}),e.jsxs("div",{className:"step-content",children:[e.jsx("div",{className:"step-title",children:"Session Establishment"}),e.jsx("div",{className:"step-description",children:"User session is established"})]})]}),e.jsxs("div",{className:"flow-step",children:[e.jsx("div",{className:"step-number",children:"5"}),e.jsxs("div",{className:"step-content",children:[e.jsx("div",{className:"step-title",children:"Context Update"}),e.jsx("div",{className:"step-description",children:"AuthContext is updated with user data"})]})]})]})]}),` 50`,e.jsx(r.h2,{children:"Implementation Details"}),` 51`,e.jsx(r.h3,{children:"Authentication Methods"}),` 52`,e.jsx(r.pre,{children:e.jsx(r.code,{className:"language-tsx",children:`// Login function\r 53const login = async (email: string, password: string) => {\r 54 try {\r
55 setLoading(true);\r 56 const { data, error } = await supabase.auth.signInWithPassword({\r 57 email,\r 58 password,\r 59 });\r 60\r 61 if (error) {\r 62 logger.error('Login error:', error);\r 63 throw error;\r 64 }\r 65\r 66 await getUserProfile(data.user.id);\r 67 return data;\r 68 } catch (error) {\r 69 logger.error('Unexpected login error:', error);\r 70 throw error;\r 71 } finally {\r 72 setLoading(false);\r 73 }\r 74};\r 75\r 76// Register function\r 77const register = async (email, password, userData) => {\r 78 try {\r 79 setLoading(true);\r 80 const { data, error } = await supabase.auth.signUp({\r 81 email,\r 82 password,\r 83 options: {\r 84 data: userData\r 85 }\r 86 });\r 87\r 88 if (error) {\r 89 logger.error('Registration error:', error);\r 90 throw error;\r 91 }\r 92\r 93 return data;\r 94 } catch (error) {\r 95 logger.error('Unexpected registration error:', error);\r 96 throw error;\r 97 } finally {\r 98 setLoading(false);\r 99 }\r 100};\r 101\r 102// Logout function\r 103const logout = async () => {\r 104 try {\r 105 setLoading(true);\r 106 await supabase.auth.signOut();\r 107 setUser(null);\r 108 setIsAuth(false);\r 109 } catch (error) {\r 110 logger.error('Logout error:', error);\r 111 throw error;\r 112 } finally {\r 113 setLoading(false);\r 114 }\r 115}; 116`})}),` 117`,e.jsx(r.h3,{children:"Session Persistence"}),` 118`,e.jsx(r.p,{children:"The AuthContext includes a useEffect hook that listens for authentication state changes:"}),` 119`,e.jsx(r.pre,{children:e.jsx(r.code,{className:"language-typescript",children:`useEffect(() => {\r 120 const getSession = async () => {\r 121 try {\r 122 const { data } = await supabase.auth.getSession();\r 123 \r 124 if (data?.session) {\r 125 const { user: authUser } = data.session;\r 126 setIsAuth(true);\r 127 await getUserProfile(authUser.id);\r 128 } else {\r 129 setIsAuth(false);\r 130 setUser(null);\r 131 }\r 132 } catch (error) {\r 133 logger.error('Session retrieval error:', error);\r 134 setIsAuth(false);\r 135 setUser(null);\r 136 } finally {\r 137 setLoading(false);\r 138 }\r 139 };\r 140\r 141 getSession();\r 142\r 143 const { data: authListener } = supabase.auth.onAuthStateChange(\r 144 async (event, session) => {\r 145 logger.log('Auth state changed:', event);\r 146 \r 147 if (event === 'SIGNED_IN' && session) {\r 148 setIsAuth(true);\r 149 await getUserProfile(session.user.id);\r 150 } else if (event === 'SIGNED_OUT') {\r 151 setIsAuth(false);\r 152 setUser(null);\r 153 }\r 154 }\r 155 );\r 156\r 157 return () => {\r 158 authListener?.subscription?.unsubscribe();\r 159 };\r 160}, []); 161`})}),` 162`,e.jsx(r.h2,{children:"Password Reset"}),` 163`,e.jsx(r.p,{children:"The authentication system includes password reset functionality:"}),` 164`,e.jsx(r.pre,{children:e.jsx(r.code,{className:"language-tsx",children:`const resetPassword = async (email) => {\r 165 try {\r 166 setLoading(true);\r 167 const { error } = await supabase.auth.resetPasswordForEmail(email, {\r 168 redirectTo: \`\${window.location.origin}/reset-password\`,\r 169 });\r 170\r 171 if (error) {\r 172 logger.error('Password reset error:', error);\r 173 throw error;\r 174 }\r 175\r 176 return { success: true };\r 177 } catch (error) {\r 178 logger.error('Unexpected password reset error:', error);\r 179 throw error;\r 180 } finally {\r 181 setLoading(false);\r 182 }\r 183};\r 184\r 185const updatePassword = async (newPassword) => {\r 186 try {\r 187 setLoading(true);\r 188 const { error } = await supabase.auth.updateUser({\r 189 password: newPassword,\r 190 });\r 191\r 192 if (error) {\r 193 logger.error('Update password error:', error);\r 194 throw error;\r 195 }\r 196\r 197 return { success: true };\r 198 } catch (error) {\r 199 logger.error('Unexpected update password error:', error);\r 200 throw error;\r 201 } finally {\r 202 setLoading(false);\r 203 }\r 204}; 205`})}),` 206`,e.jsx(r.h2,{children:"Role-Based Access Control"}),` 207`,e.jsx(r.p,{children:"Role checking is implemented in the following ways:"}),` 208`,e.jsxs(r.ol,{children:[` 209`,e.jsxs(r.li,{children:[e.jsx(r.strong,{children:"Route Guards"}),": See ",e.jsx(r.a,{href:"/documentation/route-guards",children:"Route Guards Documentation"})]}),` 210`,e.jsxs(r.li,{children:[e.jsx(r.strong,{children:"UI Conditionals"}),": Components render differently based on user role"]}),`
211`,e.jsxs(r.li,{children:[e.jsx(r.strong,{children:"API Access Control"}),": Supabase Row Level Security (RLS) enforces access at the database level"]}),` 212`]}),` 213`,e.jsx(r.p,{children:"Example of conditional rendering based on role:"}),` 214`,e.jsx(r.pre,{children:e.jsx(r.code,{className:"language-tsx",children:`{user.role === UserRole.ADMIN && (\r 215 <button \r 216 className={styles.adminAction}\r 217 onClick={handleAdminAction}\r 218 >\r 219 Admin Action\r 220 </button>\r 221)} 222`})}),` 223`,e.jsx(r.h2,{children:"Security Considerations"}),` 224`,e.jsxs(r.ul,{children:[` 225`,e.jsx(r.li,{children:"JWT tokens are automatically handled by Supabase"}),` 226`,e.jsx(r.li,{children:"Passwords are securely hashed and never exposed in the client"}),` 227`,e.jsx(r.li,{children:"Auth state is persisted in local storage with encryption"}),` 228`,e.jsx(r.li,{children:"All sensitive operations require authentication"}),` 229`,e.jsx(r.li,{children:"Admin operations are protected by role checks and RLS"}),` 230`]}),` 231`,e.jsx(r.h2,{children:"Best Practices"}),` 232`,e.jsxs(r.ol,{children:[` 233`,e.jsxs(r.li,{children:["Always use the ",e.jsx(r.code,{children:"useAuth"})," hook to access authentication state and methods"]}),` 234`,e.jsx(r.li,{children:"Wrap protected routes with the appropriate guard components"}),` 235`,e.jsx(r.li,{children:"Implement proper error handling for auth operations"}),` 236`,e.jsx(r.li,{children:"Use RLS policies in Supabase for backend data protection"}),` 237`,e.jsx(r.li,{children:"Always log authentication events for audit purposes"}),` 238`]})]})}function d(s={}){const{wrapper:r}=s.components||{};return r?e.jsx(r,{...s,children:e.jsx(n,{...s})}):n(s)}export{d as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.