### OAuth2 purpose
A way for the `user` to tell `google` to give an access token to `xorismesiti.gr` app
### OAuth2 Flow:
1. **User clicks** "Login with Google" on your platform `xorismesiti.gr`
2. **Authorization Request**: Redirect to Google's authorization endpoint `accounts.google.com/o/oauth2`, requesting the user's data
3. **User Login and Consent**: User login to Google and grants permissions.
4. **Authorization Code Response**: Google redirects back to your platform `xorismesiti.gr/callback` with an authorization `code`.
5. **Access Token Request**: Exchange the authorization `code` for an `access_token`.
6. **Access Protected Resources**: Use the `access_token` to fetch the user's Google profile and email from `googleapis.com/oauth2`
7. **Token Refresh** (Optional): If the `access_token` expires, use the `refresh token` to get a new `access_token`.
### Summary of Tasks Split Across Frontend and Backend:
***Frontend**
1. Redirect the user to Google's OAuth authorization endpoint.
2. Capture the authorization code after Google redirects back to the frontend.
3. Send the authorization code to the backend for token exchange.
**Backend**
1. Handle the token exchange (exchange the authorization code for an access token and refresh token).
2. Optionally fetch user profile data from Google (or other resources) using the access token.
3. Store the tokens securely (in session or a database).
4. Provide a way to refresh the access token if it expires.
5. By splitting the OAuth flow this way, the sensitive details (like the client secret and token exchange) remain secure on the backend, while the frontend handles user interaction.
# 1. [Frontend] Request Authorization code
### Redirect the user to Google's OAuth Authorization Endpoint
1. Action: The frontend provides a "Login with Google" button.
2. When the user clicks it, the frontend constructs a URL to Google's OAuth 2.0 authorization endpoint and redirects the user there.
3. After this redirection, the user will log in to Google and grant permissions (if they haven’t already).
4. Google will redirect the user back to your specified redirect_uri with an authorization code.
```sh
GET https://accounts.google.com/o/oauth2/v2/auth?
response_type=code&
client_id=YOUR_GOOGLE_CLIENT_ID&
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.
Example Frontent Code
```js
// Redirect to Google's OAuth 2.0 endpoint when user clicks login
const loginWithGoogle = () => {
const clientId = 'YOUR_GOOGLE_CLIENT_ID'; // 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. [Frontend] Receive Authorization Code
### And send it to the Backend
1. Once the user grants permission,
2. Google will redirect the user to the `redirect_uri` you specified in the previous step (`https://xorismesiti.gr/api/auth/callback`)
The frontend must not directly exchange the `code` for an `access_token`. Instead, it sends the `code` to the backend via an API request.
Example 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;
```
# 3. Backend (Node.js): Handle Token Exchange
1. The backend receives the authorization `code` from the frontend,
2. The backend makes a `POST` request to Google token endpoint, to exchange the authorization `code` for the `access_token` and optionally a `refresh token`
3. The backend ensures to never expose the client_secret to the frontend. This step should always be handled on the backend.
4. The backend will exchange the `code` for an `access_token` and `refresh_token`, which are sent back to the frontend or stored securely for subsequent API calls.
- `HTTP` Method: POST
- `URL`: https://oauth2.googleapis.com/token
- `Headers`:
- `Content`-Type: application/x-www-form-urlencoded
- `Body` Parameters:
- `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).
```sh
POST https://oauth2.googleapis.com/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=4/0AX4XfWgNmGZVbV7Kdr8Q9yVyzIYBnbbBdLfX39ZaE8m0w8zT8jKRLl7w-uT8k7WiyLg0Q&
redirect_uri=https://xorismesiti.gr/callback&
client_id=YOUR_GOOGLE_CLIENT_ID&
client_secret=YOUR_GOOGLE_CLIENT_SECRET
```
Example 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');
});
```
# 5. [Backend] Fetch User Data (Optional)
If you want to fetch the user profile data (e.g., from Google), your backend can use the `access_token` to request it from Google’s user info endpoint.
Example Backend Code:
```js
app.get('/api/user-profile', async (req, res) => {
const accessToken = req.headers['authorization'].split(' ')[1]; // Extract token from Authorization header
try {
const response = await axios.get('https://www.googleapis.com/oauth2/v3/userinfo', {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
// Send the user profile data to the frontend
res.json(response.data);
} catch (error) {
console.error('Error fetching user profile:', error);
res.status(500).json({ error: 'Failed to fetch user profile' });
}
});
```
# 6. [Backend] Token Expiry and Refresh (Optional)
Example Backend Code:
```js
app.post('/api/auth/refresh-token', async (req, res) => {
const { refresh_token } = req.body;
try {
const response = await axios.post('https://oauth2.googleapis.com/token', null, {
params: {
refresh_token,
client_id: clientId,
client_secret: clientSecret,
grant_type: 'refresh_token',
},
});
res.json(response.data); // Return new access token and refresh token
} catch (error) {
console.error('Error refreshing access token:', error);
res.status(500).json({ error: 'Failed to refresh access token' });
}
});
```
```sh
GET https://xorismesiti.gr/callback?
code=4/0AX4XfWgNmGZVbV7Kdr8Q9yVyzIYBnbbBdLfX39ZaE8m0w8zT8jKRLl7w-uT8k7WiyLg0Q&
state=xyz123
```
- `HTTP` Method: GET
- `URL`: https://xorismesiti.gr/callback
- `Parameters`:
- `code`: The authorization code sent by Google.
- `state`: The same state value sent in the original request (for CSRF protection).
# 4. Access Token Request (Exchange Authorization Code for Token)
Now your platform can use exchange the authorization code for an access token and refresh token.
# 5. Access Token Response (Google Returns Tokens)
1. Google validates the request
2. and returns a response with the access token (which can be used to access the user's Google resources)
3. and optionally, a refresh token (which can be used to refresh the access token when it expires).
```json
{
"access_token": "ya29.a0AfH6SMC8Op6zXZkHi2XITkDoOVzYXt3hTY6sny54UlWlxrnKlX5Xv78is7BEHekVX-VoA",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "1//04d5XHqmn6Hdy3wTf5OYDP1SyBa74zEFURjddQ2A1cFw78PY13pQyWhlD2A6XhDQtKlrjAqU4kS3vGdMvckw",
"scope": "email profile"
}
```
- HTTP Method: 200 OK
- Response Body:
- access_token: The access token used for accessing the user's resources (e.g., profile, email).
- token_type: Usually Bearer, indicating the type of token.
- expires_in: The lifetime of the access token in seconds (e.g., 3600 seconds = 1 hour).
- refresh_token: (Optional) The refresh token used to obtain a new access token when the current one expires.
- scope: The scope of access granted (e.g., email, profile).
# 6. Access Protected Resources (Fetching User Profile Data)
With the access token obtained in the previous step,
your platform can now use it to fetch the user's Google profile and email information.
The token is included in the Authorization header of the request.
- URL: https://www.googleapis.com/oauth2/v3/userinfo
- HTTP Method: GET
- Headers:
- Authorization: Bearer {access_token}: The access token obtained in step 5.
**Request:**
```sh
GET https://www.googleapis.com/oauth2/v3/userinfo
Authorization: Bearer ya29.a0AfH6SMC8Op6zXZkHi2XITkDoOVzYXt3hTY6sny54UlWlxrnKlX5Xv78is7BEHekVX-VoA
```
**Response**
```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"
}
```
# 7. Refreshing the Access Token (If Necessary)
If the access token expires,
your platform can use the refresh token (if provided) to obtain a new access token without requiring the user to log in again.
- `URL`: https://oauth2.googleapis.com/token
- `HTTP` Method: POST
- `Headers`:
- `Content`-Type: application/x-www-form-urlencoded
- `Body` Parameters:
- `grant_type`=refresh_token: This indicates the refresh token flow.
- `refresh_token`: The refresh token obtained in step 5.
- `client_id`: Your Google API client ID.
- `client_secret`: Your Google API client secret.
**Request**
```sh
POST https://oauth2.googleapis.com/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&
refresh_token=1//04d5XHqmn6Hdy3wTf5OYDP1SyBa74zEFURjddQ2A1cFw78PY13pQyWhlD2A6XhDQtKlrjAqU4kS3vGdMvckw&
client_id=YOUR_GOOGLE_CLIENT_ID&
client_secret=YOUR_GOOGLE_CLIENT_SECRET
```
**Response**
```json
{
"access_token": "ya29.a0AfH6SMC8Op6zXZkHi2XITkDoOVzYXt3hTY6sny54UlWlxrnKlX5Xv78is7BEHekVX-VoA",
"token_type": "Bearer",
"expires_in": 3600
}
```