PageSourceSearch

https://class-act.netlify.app/assets/006_authentication-0Uort_jH.js

js class-act.netlify.app collected 2026-10-03 10:35:00 UTC 12,323 bytes, 238 lines download raw bytes

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.