Spaces:
Sleeping
Sleeping
| # Google Sign-In Client Integration Guide | |
| Quick guide for frontend developers to integrate Google Sign-In with the APIGateway. | |
| ## Setup | |
| ### 1. Get Your Google Client ID | |
| Use the same `GOOGLE_CLIENT_ID` that's configured on the backend. | |
| ### 2. Add Google Identity Services Script | |
| ```html | |
| <script src="https://accounts.google.com/gsi/client" async defer></script> | |
| ``` | |
| --- | |
| ## Option A: One-Tap / Button Sign-In (Recommended) | |
| ```html | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <script src="https://accounts.google.com/gsi/client" async defer></script> | |
| </head> | |
| <body> | |
| <!-- Google One Tap prompt --> | |
| <div id="g_id_onload" | |
| data-client_id="YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com" | |
| data-callback="handleGoogleSignIn" | |
| data-auto_prompt="false"> | |
| </div> | |
| <!-- Sign In Button --> | |
| <div class="g_id_signin" | |
| data-type="standard" | |
| data-size="large" | |
| data-theme="outline" | |
| data-text="sign_in_with" | |
| data-shape="rectangular" | |
| data-logo_alignment="left"> | |
| </div> | |
| <script> | |
| const API_BASE = 'https://your-api-gateway.com'; // Change this | |
| async function handleGoogleSignIn(response) { | |
| try { | |
| // Send Google token to your backend | |
| const res = await fetch(`${API_BASE}/auth/google`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| id_token: response.credential, | |
| temp_user_id: localStorage.getItem('temp_user_id') // optional | |
| }) | |
| }); | |
| const data = await res.json(); | |
| if (data.success) { | |
| // Store the access token | |
| localStorage.setItem('access_token', data.access_token); | |
| localStorage.setItem('user', JSON.stringify({ | |
| user_id: data.user_id, | |
| email: data.email, | |
| name: data.name, | |
| credits: data.credits | |
| })); | |
| console.log('Signed in!', data.is_new_user ? 'New user' : 'Existing user'); | |
| // Redirect or update UI | |
| window.location.reload(); | |
| } else { | |
| alert('Sign in failed: ' + (data.detail || 'Unknown error')); | |
| } | |
| } catch (error) { | |
| console.error('Sign in error:', error); | |
| alert('Sign in failed. Please try again.'); | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> | |
| ``` | |
| --- | |
| ## Option B: Programmatic Sign-In | |
| ```javascript | |
| const API_BASE = 'https://your-api-gateway.com'; | |
| // Initialize Google Sign-In | |
| function initGoogleSignIn(clientId) { | |
| google.accounts.id.initialize({ | |
| client_id: clientId, | |
| callback: handleGoogleSignIn | |
| }); | |
| // Render button in a container | |
| google.accounts.id.renderButton( | |
| document.getElementById('google-signin-btn'), | |
| { theme: 'outline', size: 'large', text: 'signin_with' } | |
| ); | |
| } | |
| // Handle the response | |
| async function handleGoogleSignIn(response) { | |
| const res = await fetch(`${API_BASE}/auth/google`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ id_token: response.credential }) | |
| }); | |
| const data = await res.json(); | |
| if (data.success) { | |
| localStorage.setItem('access_token', data.access_token); | |
| } | |
| } | |
| // Call on page load | |
| window.onload = () => initGoogleSignIn('YOUR_CLIENT_ID.apps.googleusercontent.com'); | |
| ``` | |
| --- | |
| ## Making API Calls | |
| After sign-in, use the access token for all API calls: | |
| ```javascript | |
| const API_BASE = 'https://your-api-gateway.com'; | |
| async function apiCall(endpoint, options = {}) { | |
| const token = localStorage.getItem('access_token'); | |
| if (!token) { | |
| throw new Error('Not signed in'); | |
| } | |
| const response = await fetch(`${API_BASE}${endpoint}`, { | |
| ...options, | |
| headers: { | |
| 'Authorization': `Bearer ${token}`, | |
| 'Content-Type': 'application/json', | |
| ...options.headers | |
| } | |
| }); | |
| if (response.status === 401) { | |
| // Token expired - need to sign in again | |
| localStorage.removeItem('access_token'); | |
| window.location.href = '/login'; | |
| return; | |
| } | |
| return response.json(); | |
| } | |
| // Examples | |
| async function getCurrentUser() { | |
| return apiCall('/auth/me'); | |
| } | |
| async function generateText(prompt) { | |
| return apiCall('/gemini/generate-text', { | |
| method: 'POST', | |
| body: JSON.stringify({ prompt }) | |
| }); | |
| } | |
| async function checkJobStatus(jobId) { | |
| return apiCall(`/gemini/job/${jobId}`); | |
| } | |
| ``` | |
| --- | |
| ## API Endpoints Reference | |
| | Endpoint | Method | Auth Required | Description | | |
| |----------|--------|---------------|-------------| | |
| | `/auth/google` | POST | β | Sign in with Google token | | |
| | `/auth/me` | GET | β | Get current user info | | |
| | `/auth/refresh` | POST | β | Refresh access token | | |
| | `/gemini/generate-text` | POST | β | Generate text (costs 1 credit) | | |
| | `/gemini/generate-video` | POST | β | Generate video (costs 1 credit) | | |
| | `/gemini/job/{job_id}` | GET | β | Check job status | | |
| --- | |
| ## Sign Out | |
| ```javascript | |
| function signOut() { | |
| localStorage.removeItem('access_token'); | |
| localStorage.removeItem('user'); | |
| // Optionally call logout endpoint for audit | |
| fetch(`${API_BASE}/auth/logout`, { | |
| method: 'POST', | |
| headers: { 'Authorization': `Bearer ${token}` } | |
| }); | |
| // Revoke Google session | |
| google.accounts.id.disableAutoSelect(); | |
| window.location.href = '/'; | |
| } | |
| ``` | |
| --- | |
| ## Error Handling | |
| | Status | Meaning | Action | | |
| |--------|---------|--------| | |
| | 401 | Token expired/invalid | Redirect to sign-in | | |
| | 402 | Insufficient credits | Show "buy credits" prompt | | |
| | 429 | Rate limited | Wait and retry | | |
| ```javascript | |
| async function apiCallWithErrorHandling(endpoint, options) { | |
| const response = await apiCall(endpoint, options); | |
| if (response.status === 402) { | |
| alert('You have run out of credits!'); | |
| return null; | |
| } | |
| return response; | |
| } | |
| ``` | |
| --- | |
| ## React Example | |
| ```jsx | |
| import { useEffect, useState } from 'react'; | |
| function App() { | |
| const [user, setUser] = useState(null); | |
| useEffect(() => { | |
| // Check if already signed in | |
| const token = localStorage.getItem('access_token'); | |
| if (token) { | |
| fetch('/auth/me', { | |
| headers: { 'Authorization': `Bearer ${token}` } | |
| }) | |
| .then(r => r.json()) | |
| .then(setUser) | |
| .catch(() => localStorage.removeItem('access_token')); | |
| } | |
| // Initialize Google Sign-In | |
| window.handleGoogleSignIn = async (response) => { | |
| const res = await fetch('/auth/google', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ id_token: response.credential }) | |
| }); | |
| const data = await res.json(); | |
| if (data.success) { | |
| localStorage.setItem('access_token', data.access_token); | |
| setUser(data); | |
| } | |
| }; | |
| }, []); | |
| if (!user) { | |
| return ( | |
| <div> | |
| <div id="g_id_onload" | |
| data-client_id="YOUR_CLIENT_ID" | |
| data-callback="handleGoogleSignIn" /> | |
| <div className="g_id_signin" data-type="standard" /> | |
| </div> | |
| ); | |
| } | |
| return <div>Welcome, {user.name}! Credits: {user.credits}</div>; | |
| } | |
| ``` | |