### OAuth2 purpose A way for the `user` to tell `google` to give an access token to `xorismesiti.gr` app


### OAuth2 Frontend/Backend Flow: 1. Frontend **Redirect** the user to Google's OAuth authorization endpoint `accounts.google.com/o/oauth2` 2. Google **Redirect** the user back to `xorismesiti.gr/callback` including the authorization `code` 3. Frontend **Send** the authorization `code` to the Backend 4. Backend **exchange** the authorization `code` for an `access_token` and `refresh token` 2. **fetch** user profile data from from `googleapis.com/oauth2` using the `access_token` 3. **Store** the `tokens` securely in session (front) or a database (back) 4. **Refresh** the `access_token` if it expires


# 1. Get Authorization code ### Frontend ⇢ Google ⇢ Frontend
1. A button "Login with Google" redirects the user to the Google's authorization endpoint `accounts.google.com/o/oauth2/v2/auth` 2. After the redirection, the user will log in to Google and grant permissions (if they haven’t already). 3. Google will redirect the user back to your redirect_uri `https://xorismesiti.gr/callback` with an authorization code `?code=ABC123` *Security: the state string should be validated upon receiving the response from Google, as it ensures that the response corresponds to the request.*

HTTP GET Request from Frontend to Google

```sh GET https://accounts.google.com/o/oauth2/v2/auth? response_type=code& client_id=ABC34JHS9D& redirect_uri=https://xorismesiti.gr/callback& scope=email%20profile& state=xyz123 ``` - `response_type=code`: This indicates you're using the "authorization code" flow. - `client_id`: Your Google API client ID. Created by the developers of the `xorismesiti.gr` on the google API console. - `redirect_uri`: The URI Google will redirect to after the user consents. - `scope`: The permissions you're requesting (e.g., email, profile). - `state`: A random string to protect against CSRF attacks.

HTTP GET Response from Google to Frontend

```bash HTTP/1.1 302 Found Location: https://xorismesiti.gr/api/auth/callback?code=4/0AX4XfWgyVyz-uT8k7WiyLg0Q&state=xyz123 Content-Type: text/html; charset=UTF-8 Content-Length: 0 ```

Frontend Code

```js // Redirect to Google's OAuth 2.0 endpoint when user clicks login const loginWithGoogle = () => { const clientId = 'ABC34JHS9D'; // Replace with your actual Google Client ID const redirectUri = 'https://xorismesiti.gr/api/auth/callback'; // Backend URL where Google will send the user after login const scope = 'email profile'; // Scopes you're requesting (email, profile, etc.) const state = 'random-state-value'; // For CSRF protection const googleAuthUrl = `https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&state=${state}`; window.location.href = googleAuthUrl; // Redirect user to Google }; ```



# 2. Exchange Authorization Code with Tokens ### Frontend ⇢ Backend ⇢ Google ⇢ Backend ⇢ Frontend
### 2.1 Frontend Now that the frontend has the Authorization `code` on the callback url, it can send it to the backend

HTTP POST Request from Frontend to Backend

```bash POST https://xorismesiti.gr/api/auth/exchange-token Content-Type: application/json { "code": "AAAABCX4XfWgyVyziyLg0QHHHHH" } ```

Frontend Code

```js // On the backend callback URL, the frontend receives the authorization code import { useEffect } from 'react'; import { useRouter } from 'next/router'; const Callback = () => { const router = useRouter(); useEffect(() => { const { code, state } = router.query; // Send the authorization code to the backend for token exchange fetch('/api/auth/exchange-token', { method: 'POST', body: JSON.stringify({ code }), headers: { 'Content-Type': 'application/json', }, }) .then(response => response.json()) .then(data => { // Handle success (store token, update UI, etc.) console.log(data); // Typically, you'll store the access token here or manage the user session. router.push('/dashboard'); // Redirect the user to their dashboard or home page. }) .catch(error => { console.error('Error exchanging token:', error); }); }, []); return
Loading...
; }; export default Callback; ```

### 2.2 Backend 1. The Backend **receives** the authorization `code` form the Frontend POST at `xorismesiti.gr/api/auth/exchange-token` 2. The Backend **POST** the Authorization `code` to Google API 3. The Google **response** to Backend POST with the tokens 4. The Backend **response** to Frontend POST with the the tokens *Security: The backend never expose the client_secret to the frontend. This step should always be handled on the backend.*

HTTP POST Request from Backend to Google

```sh POST https://oauth2.googleapis.com/token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code& code=AAAABCX4XfWgyVyziyLg0QHHHHH& redirect_uri=https://xorismesiti.gr/callback& client_id=ABC34JHS9D& client_secret=PASS1234 ``` - `HTTP Method`: POST - `URL`: https://oauth2.googleapis.com/token - Headers - `Content-Type`: application/x-www-form-urlencoded - Body - `grant_type`=authorization_code: This specifies the grant type. - `code`: The authorization code you received in the previous step. - `redirect_uri`: The same redirect URI used in the authorization request. - `client_id`: Your Google API client ID. - `client_secret`: Your Google API client secret (which should be kept secure).

HTTP POST Response from Google to Backend

```json { "access_token": "ACCESSTOKEN.6zXZkHi2XITkDoOV.ACCESSTOKEN", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "1//04d5XHqmn6Hdy3wTf5OYDP1SyBa74zEFURjddQ2A1cFw78PY13pQyWhlD2A6XhDQtKlrjAqU4kS3vGdMvckw", "scope": "email profile" } ```

Backend Code:

```js const express = require('express'); const axios = require('axios'); const app = express(); app.use(express.json()); // To parse JSON bodies const clientId = 'YOUR_GOOGLE_CLIENT_ID'; // Google Client ID const clientSecret = 'YOUR_GOOGLE_CLIENT_SECRET'; // Google Client Secret const redirectUri = 'https://xorismesiti.gr/api/auth/callback'; // Must match the one used in frontend // Handle token exchange app.post('/api/auth/exchange-token', async (req, res) => { const { code } = req.body; try { const response = await axios.post('https://oauth2.googleapis.com/token', null, { params: { code, client_id: clientId, client_secret: clientSecret, redirect_uri: redirectUri, grant_type: 'authorization_code', }, }); const { access_token, refresh_token, expires_in } = response.data; // Optionally, store the tokens in a secure location (e.g., session, database) // For now, send them back to the frontend (not recommended for production) res.json({ access_token, refresh_token, expires_in }); } catch (error) { console.error('Error exchanging authorization code for token:', error); res.status(500).json({ error: 'Failed to exchange authorization code for access token' }); } }); // Start the server app.listen(3000, () => { console.log('Server running on http://localhost:3000'); }); ```

### 2.3 Frontend The frontend gets the tokens from the backend response, and saves them in a cookie marked as `HTTP-only` and `Secure` Now its ready to use the tokens to get the user data from Google

HTTP Response from Backend to Frontend

```json { "access_token": "ya29.a0AfH6SMC8Op6zXZkHi2XITkDoOVzYXt3hTY6sny54UlWlxrnKlX5Xv78is7BEHekVX-VoA", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "1//04d5XHqmn6Hdy3wTf5OYDP1SyBa74zEFURjddQ2A1cFw78PY13pQyWhlD2A6XhDQtKlrjAqU4kS3vGdMvckw", "scope": "email profile" } ```

Frontend Code:

```js // Set an HTTP-only, Secure cookie on the backend res.cookie('access_token', accessToken, { httpOnly: true, secure: true, maxAge: 3600000 // 1 hour expiry }); ```



# 4. Use the Token ### Frontend ⇢ Google ⇢ Frontend
1. The frontend receives the tokens from the Backend response 2. Store them in a secure cookie 3. Make authenticated requests directly to Google API

HTTP GET Request from Frontend to Google

```bash GET https://www.googleapis.com/oauth2/v3/userinfo Authorization: Bearer ACCESSTOKEN6zXZkHi2XITkDoOVACCESSTOKEN ```

HTTP GET Response from Google to Frontend

```json { "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" } ```

Frontend Code

```js const fetchUserData = async (accessToken) => { try { const response = await fetch('https://www.googleapis.com/oauth2/v3/userinfo', { method: 'GET', headers: { 'Authorization': `Bearer ${accessToken}`, }, }); if (!response.ok) { throw new Error('Failed to fetch user data from Google'); } // Parse the response to get the user data const userData = await response.json(); console.log('User Data:', userData); // Example user data: // { // "sub": "1234567890123456789012345678901234567890", // "name": "John Doe", // "given_name": "John", // "family_name": "Doe", // "email": "john.doe@example.com", // "picture": "https://example.com/profile.jpg" // } } catch (error) { console.error('Error fetching user data:', error); } }; ```



# 5. Refresh the Token ### Frontend ⇢ Backend ⇢ Google ⇢ Backend ⇢ Frontend
If the access token is expired, the frontend will receive a response error from Google when attempting to fetch user data

HTTP GET Request from Frontend to Google (with expires token)

```bash GET https://www.googleapis.com/oauth2/v3/userinfo Authorization: Bearer ACCESSTOKEN6zXZkHi2XITkDoOVACCESSTOKEN ```

HTTP GET Response from Google to Frontend (for expired token)

```json { "error": "invalid_token", "error_description": "The access token expired" } ```

HTTP POST Request from Frontend to Backend (with refresh token)

```bash POST /api/refresh-token HTTP/1.1 Host: your-backend-domain.com Content-Type: application/json Authorization: Bearer (optional, depends on the backend) User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Accept: application/json Origin: https://your-frontend-domain.com Connection: keep-alive Content-Length: 57 { "refresh_token": "REFRESHTOKEN6zXZkHi2XITkDoOVREFRESHTOKEN" } ```

HTTP POST Response from Backend to Frontend (with new access token)

```json HTTP/1.1 200 OK Content-Type: application/json Content-Length: 123 Cache-Control: no-store Pragma: no-cache { "access_token": "new-access-token-here", "expires_in": 3600, "token_type": "bearer" } ```

Frontend Code

```js const refreshAccessToken = async (refreshToken) => { try { const response = await fetch('/api/refresh-token', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ refresh_token: refreshToken }), }); if (!response.ok) { throw new Error('Failed to refresh access token'); } const data = await response.json(); return data.access_token; // New access token } catch (error) { console.error('Error refreshing access token:', error); } }; ```

Frontend Code

```js const response = await fetch('https://oauth2.googleapis.com/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ client_id: 'YOUR_GOOGLE_CLIENT_ID', client_secret: 'YOUR_GOOGLE_CLIENT_SECRET', refresh_token: refreshToken, grant_type: 'refresh_token', }), }); const data = await response.json(); const newAccessToken = data.access_token; // New access token ```