11import React , { useCallback , useEffect , useState } from 'react' ;
22import './Auth.css' ;
33import { useAuth } from '../../contexts/AuthContext' ;
4+ import { auth } from '../../config/firebase' ;
45
56const CLIENT_ID = '87955960620-dv9h8pfv4a97mno598dcc3m1nlt0h6u4.apps.googleusercontent.com' ;
67
@@ -21,20 +22,77 @@ declare global {
2122
2223const Auth : React . FC = ( ) => {
2324 const [ isGoogleLoaded , setIsGoogleLoaded ] = useState ( false ) ;
24- const { login, loginWithApple} = useAuth ( ) ;
25+ const { user, login, loginWithApple} = useAuth ( ) ;
26+ const [ isGenerating , setIsGenerating ] = useState ( false ) ;
27+ const [ isRedirecting , setIsRedirecting ] = useState ( false ) ;
28+
29+ // Get redirect URL from URL params (for CLI integration)
30+ const urlParams = new URLSearchParams ( window . location . search ) ;
31+ const redirectUrl = urlParams . get ( 'redirect_url' ) ;
32+
33+ const generateTokenAndRedirect = async ( ) => {
34+ if ( ! redirectUrl || ! user ) return ;
35+
36+ setIsGenerating ( true ) ;
37+ try {
38+ // Get the current user's ID token
39+ const idToken = await auth . currentUser ?. getIdToken ( ) ;
40+ if ( ! idToken ) {
41+ throw new Error ( 'Failed to get authentication token' ) ;
42+ }
43+
44+ // Call the salamander-service to generate custom token
45+ const response = await fetch ( 'https://api.salamander.space/v1/generate-custom-token' , {
46+ method : 'POST' ,
47+ headers : {
48+ 'Content-Type' : 'application/json' ,
49+ } ,
50+ body : JSON . stringify ( {
51+ firebaseIdToken : idToken
52+ } ) ,
53+ } ) ;
54+
55+ if ( ! response . ok ) {
56+ const errorData = await response . json ( ) ;
57+ throw new Error ( errorData . error || 'Failed to generate custom token' ) ;
58+ }
59+
60+ const result = await response . json ( ) ;
61+
62+ setIsRedirecting ( true ) ;
63+ // Redirect to the specified URL with the token
64+ window . location . href = `${ redirectUrl } ?token=${ encodeURIComponent ( result . customToken ) } ` ;
65+ } catch ( err : any ) {
66+ console . error ( 'Error generating custom token:' , err ) ;
67+ alert ( err . message || 'Failed to generate authentication token' ) ;
68+ } finally {
69+ setIsGenerating ( false ) ;
70+ }
71+ } ;
72+
73+ useEffect ( ( ) => {
74+ // If user is signed in and we have a redirect URL, generate token and redirect
75+ if ( user && redirectUrl && ! isGenerating && ! isRedirecting ) {
76+ generateTokenAndRedirect ( ) ;
77+ }
78+ // eslint-disable-next-line react-hooks/exhaustive-deps
79+ } , [ user , redirectUrl , isGenerating , isRedirecting ] ) ;
2580
2681 const handleCredentialResponse = useCallback ( async ( response : any ) => {
2782 console . log ( 'Encoded JWT ID token: ' + response . credential ) ;
2883
2984 try {
3085 await login ( response . credential ) ;
31- // Redirect to account page after successful login
32- window . location . hash = 'account' ;
86+ // If we have a redirect URL, the useEffect will handle the redirect
87+ if ( ! redirectUrl ) {
88+ // Redirect to account page after successful login
89+ window . location . hash = 'account' ;
90+ }
3391 } catch ( error ) {
3492 console . error ( 'Failed to process login:' , error ) ;
3593 alert ( error instanceof Error ? error . message : 'Sign-in failed. Please try again.' ) ;
3694 }
37- } , [ login ] ) ;
95+ } , [ login , redirectUrl ] ) ;
3896
3997 useEffect ( ( ) => {
4098 const initializeGoogle = ( ) => {
@@ -71,14 +129,52 @@ const Auth: React.FC = () => {
71129 const handleAppleAuth = async ( ) => {
72130 try {
73131 await loginWithApple ( ) ;
74- // Redirect to account page after successful login
75- window . location . hash = 'account' ;
132+ // If we have a redirect URL, the useEffect will handle the redirect
133+ if ( ! redirectUrl ) {
134+ // Redirect to account page after successful login
135+ window . location . hash = 'account' ;
136+ }
76137 } catch ( error ) {
77138 console . error ( 'Apple sign-in failed:' , error ) ;
78139 alert ( error instanceof Error ? error . message : 'Apple sign-in failed. Please try again.' ) ;
79140 }
80141 } ;
81142
143+ // Show loading state if generating token or redirecting
144+ if ( isGenerating || isRedirecting ) {
145+ return (
146+ < div className = "auth-container" >
147+ < div className = "auth-card" >
148+ < div className = "auth-header" >
149+ < img src = "images/logo_salamander.png" alt = "Salamander" className = "auth-logo" />
150+ < h1 className = "auth-title" > Salamander</ h1 >
151+ < p className = "auth-tagline" > Never be AFK</ p >
152+ </ div >
153+
154+ < div className = "auth-content" style = { { textAlign : 'center' } } >
155+ < div style = { {
156+ width : '32px' ,
157+ height : '32px' ,
158+ border : '3px solid #374151' ,
159+ borderTop : '3px solid #ff6b35' ,
160+ borderRadius : '50%' ,
161+ animation : 'spin 1s linear infinite' ,
162+ margin : '0 auto 16px'
163+ } } > </ div >
164+ < p style = { { color : '#d1d5db' , fontSize : '14px' } } >
165+ { isRedirecting ? 'Redirecting to CLI...' : 'Generating authentication token...' }
166+ </ p >
167+ { isRedirecting && (
168+ < p style = { { color : '#9ca3af' , fontSize : '12px' , marginTop : '16px' , fontStyle : 'italic' } } >
169+ If the redirect doesn't work, you can close this window and return to your terminal.
170+ </ p >
171+ ) }
172+ </ div >
173+ </ div >
174+ </ div >
175+ ) ;
176+ }
177+
82178 return (
83179 < div className = "auth-container" >
84180 < div className = "auth-card" >
0 commit comments