@@ -22,6 +22,12 @@ export const AuthProvider = ({ children }) => {
2222 const [ userData , setUserData ] = useState ( null ) ;
2323 const [ loading , setLoading ] = useState ( true ) ;
2424 const [ isOnboarding , setIsOnboarding ] = useState ( false ) ;
25+ // GitHub OAuth access token kept in React state only -- never written to
26+ // sessionStorage or localStorage. Storing it in Web Storage exposes it to
27+ // any JavaScript running on the page (XSS). Keeping it in memory means it
28+ // is lost on page refresh, but the token is only needed once after login
29+ // to call fetchGitHubStats, so this trade-off is acceptable.
30+ const [ ghAccessToken , setGhAccessToken ] = useState ( null ) ;
2531
2632 // Listen to Auth State Changed
2733 useEffect ( ( ) => {
@@ -92,8 +98,8 @@ export const AuthProvider = ({ children }) => {
9298 const githubId = additionalInfo ?. profile ?. id || null ;
9399 const avatar = additionalInfo ?. profile ?. avatar_url || authUser . photoURL || "" ;
94100
95- // Store GitHub accessToken in sessionStorage securely (non-persistent across tabs for security)
96- sessionStorage . setItem ( `gh_token_ ${ authUser . uid } ` , accessToken ) ;
101+ // Keep the token in React state only -- do not write to Web Storage.
102+ setGhAccessToken ( accessToken ) ;
97103
98104 const userDocRef = doc ( db , "users" , authUser . uid ) ;
99105 const docSnap = await getDoc ( userDocRef ) ;
@@ -146,23 +152,25 @@ export const AuthProvider = ({ children }) => {
146152 const logout = async ( ) => {
147153 setLoading ( true ) ;
148154 try {
149- if ( user ) {
150- sessionStorage . removeItem ( `gh_token_${ user . uid } ` ) ;
151- }
152155 await signOutUser ( ) ;
153156 setUser ( null ) ;
154157 setUserData ( null ) ;
155158 setIsOnboarding ( false ) ;
159+ setGhAccessToken ( null ) ;
156160 } catch ( error ) {
157161 console . error ( "Logout failure:" , error ) ;
158162 } finally {
159163 setLoading ( false ) ;
160164 }
161165 } ;
162166
163- // Securely fetches GitHub stats on the client once using the transient OAuth token
167+ // Fetches GitHub stats once after login using the in-memory OAuth token.
168+ // The token is no longer read from sessionStorage -- it comes from the
169+ // ghAccessToken state variable which is populated on login and cleared on
170+ // logout. This prevents any JavaScript on the page from reading the token
171+ // via sessionStorage.getItem().
164172 const fetchGitHubStats = async ( uid , username ) => {
165- const token = sessionStorage . getItem ( `gh_token_ ${ uid } ` ) ;
173+ const token = ghAccessToken ;
166174 const headers = token ? { Authorization : `token ${ token } ` } : { } ;
167175
168176 try {
0 commit comments