### OAuth2 purpose A way for the `user` to tell `google` to give an access to `myapp` app
### Flow: | When | What | How | |-------|--------------------------|--------------------------------------------------| | 1 | Get Code | ` Front ⇢ Google ⇢ Back `| | 2 | Exchange Code with Token | ` Back ⇢ Google ⇢ Back ⇢ Front `| | 4 | Use Token | ` Front ⇢ Back ⇢ Google ⇢ Back ⇢ Front `|

### Details: 1. Get Authorization Code 1. Frontend **Navigate** to Google URL with a callback url 2. Google **Redirect** to Backend's callback url with the authorization code 2. Exchange Code with Token 1. Backend **POST** the `code` to Google 2. Google **Response** to Backend with an `access_token` and a `refresh token` 3. Backend **Redirect** to Frontend with the `access_token` in a `cookie` 3. Use Token 1. Frontend **GET** profile data from Backend using the `cookie` 2. Backend **GET** profile data from Google using the `access_token` 3. Google **Response** to Backend with profile data 4. Backend **Response** to Frontend with profile data


# 1. Get Code 1. Frontend **Navigate** to Google `https://accounts.google.com/o/oauth2` with a callback url 2. Google **Redirect** to Backend callback url `https://myapp/api/auth/callback` with authorization code ### 1. Front **GET** to Google ```sh GET https://accounts.google.com/o/oauth2/v2/auth? response_type=code& # This indicates you're using the "authorization code" flow. client_id=ABC34JHS9D& # Your Google API client ID created on the google API console. redirect_uri=https://myapp/api/auth/callback& # The URI Google will redirect to after the user consents. scope=email%20profile& # The permissions you're requesting (e.g., email, profile). state=xyz123 # A random string to protect against CSRF attacks. ``` ### 2. Google **302** to Back ```bash HTTP/1.1 302 Found Location: https://myapp/api/auth/callback?code=4/0AX4XfWgyVyz-uT8k7WiyLg0Q&state=xyz123 Content-Type: text/html; charset=UTF-8 Content-Length: 0 ``` *Security: the state string should be validated upon receiving the response from Google, as it ensures that the response corresponds to the request.*


# 2. Exchange Code with Token 1. Backend **POST** the `code` to Google `https://oauth2.googleapis.com/token` 2. Google **Response** to Backend with an `access_token` and a `refresh token` 3. Backend **Redirect** to Frontend with the `access_token` in a `cookie` ### 1. Backend **POST** the code to Google ```sh POST https://oauth2.googleapis.com/token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code& code=AAAABCX4XfWgyVyziyLg0QHHHHH& client_id=ABC34JHS9D& client_secret=PASS1234& redirect_uri=https://myapp/callback ``` ### 2. Google **Response** to Backend ```js { "access_token": "ya29.a0AfH6SMC8Op6zkVX-VoA", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "1//04d5lrjAqU4kS3vGdMvckw", "scope": "email profile" } ``` ### 3. Backend **Redirect** to Frontend success page This redirect will place a **cookie** in the browser that contains the **access token** ```bash HTTP/1.1 302 Found Location: http://localhost:3000/dashboard Set-Cookie: access_token=ya29.a0AfH6SMC8Op6zkVX-VoA; HttpOnly; Secure; Max-Age=3600; Path=/; ```

Backend code



Implements the endpoint /auth/google/callback

1. Recieves authorization code from Google


2. POST send the authorization code to https://oauth2.googleapis.com/token


3. POST response the access & refresh tokens


4. Redirect to Fronend success page with a cookie contains the access token




```js app.get('/callback', async (req, res) => { try { // // 1. Get the authorization code from Google's redirect // const { code } = req.query; // // 2. POST the authorization code to Google // const tokenResponse = await fetch('https://oauth2.googleapis.com/token', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ code, client_id: process.env.GOOGLE_CLIENT_ID, client_secret: process.env.GOOGLE_CLIENT_SECRET, redirect_uri: 'https://myapp/callback', grant_type: 'authorization_code', }), }); // // 3. Get Response tokens from Google // const { access_token, refresh_token } = await tokenResponse.json(); // // 4. Redirect to Fronend success page with a cookie contains the access token // res.cookie('access', access_token, { httpOnly: true, // Cannot be accessed by client-side JavaScript secure: true, // Only sent over HTTPS sameSite: 'strict', // CSRF protection maxAge: 24 * 60 * 60 * 1000, // 24 hours }); res.redirect('https://myapp/dashboard'); } catch (error) { res.redirect('https://myapp/login'); } }); ``` The `redirect_uri` in the token exchange `POST` request needs to match EXACTLY the same `redirect_uri` that was used in the initial authorization request to Google. It's part of OAuth2 security verification.



# 3. Use Token ### 1. Frontend **GET** profile data from Backend ```bash curl -X GET https://myapp/api/auth/profile \ -H "Cookie: access_token=ya29.a0AfH6SMC8Op6zkVX-VoA" \ -H "Accept: application/json" ``` ### 2. Backend **GET** profile data from Google ```bash curl -X GET "https://www.googleapis.com/oauth2/v3/userinfo" \ -H "Authorization: Bearer {access_token}" \ -H "Accept: application/json" ``` ### 3. Google **response** to Back with profile data ``` { "sub": "1234567890", "name": "John Doe", "given_name": "John", "family_name": "Doe", "profile": "https://plus.google.com/1234567890", "picture": "https://lh3.googleusercontent.com/a-/AOh14GgIXXl5JXzW0c1Szbl-e1Jch1vhl5rHhH65vlK6J5g5PqkGjj1O0p3t8bgVEOykQ6ykFSQ=s96", "email": "john.doe@example.com", "email_verified": true, "locale": "en" } ``` ### 4. Backend **response** to Front with profile data ``` { "sub": "1234567890", "name": "John Doe", "picture": "https://lh3.googleusercontent.com/8bgVEOykQ6ykFSQ=s96", "email": "john.doe@example.com", } ``` ### Frontend Code ```js import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; function DashboardPage() { const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(true); const [dashboardData, setDashboardData] = useState(null); useEffect(() => { async function loadDashboard() { try { // // Single request that both verifies auth and gets data // const response = await fetch('https://myapp/api/dashboard-data', { credentials: 'include' // Sends the auth cookie }); if (!response.ok) { // // If auth is invalid, the backend will return 401 // if (response.status === 401) { navigate('/login'); return; } throw new Error('Failed to load dashboard'); } const data = await response.json(); setDashboardData(data); setIsLoading(false); } catch (err) { navigate('/login'); } } loadDashboard(); }, [navigate]); if (isLoading) { return
Loading...
; } return (

Welcome to Dashboard

); } ```