13 KiB
13 KiB
OAuth2 purpose
A way for the user to tell google to give an access token to xorismesiti.gr app
OAuth2 Standar Flow:
- User clicks "Login with Google" on your platform
xorismesiti.gr - Authorization Request: Redirect to Google's authorization endpoint
accounts.google.com/o/oauth2 - User Login and Consent: User login to Google and grants permissions.
- Authorization Code Response: Google redirects back to your app
xorismesiti.gr/callbackwith an authorizationcode. - Access Token Request: App exchanges the authorization
codefor anaccess_token. - Access Protected Resources: App uses the
access_tokento fetch the user's Google profile and email fromgoogleapis.com/oauth2 - Token Refresh (Optional): If the
access_tokenexpires, app uses therefresh tokento get a newaccess_token.
OAuth2 Frontend/`Backend Flow:
Frontend
- Redirect the user to Google's OAuth authorization endpoint.
- Get the authorization
codeafter Google redirects back to the frontend. - Send the authorization
codeto the backend fortokenexchange.
Backend
- exchange the authorization
codefor anaccess_tokenandrefresh token - fetch user profile data from Google (or other resources) using the
access_token - Store the
tokenssecurely (in session or a database). - Refresh the
access_tokenif it expires.
1. [Frontend] Request Authorization code
- The use clicks a "Login with Google" button with a URL to Google's OAuth 2.0 authorization endpoint and redirects the user there.
- After this redirection, the user will log in to Google and grant permissions (if they haven’t already).
- Google will redirect the user back to your specified redirect_uri with an authorization code.
HTTP Request
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 thexorismesiti.gron 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.
Frontent Code
// 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
- User grants permission
- Google redirect the user to the
redirect_uriyou specified in the previous request (https://xorismesiti.gr/api/auth/callback) - The frontend must not directly exchange the
codefor anaccess_token. Instead, it sends thecodeto the backend via an API request.
HTTP Request
POST https://xorismesiti.gr/api/auth/exchange-token
Content-Type: application/json
{
"code": "authorization-code-from-google"
}
Frontend Code
// 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 <div>Loading...</div>;
};
export default Callback;
3. [Backend] Exchange Code with Token
- The backend receives the authorization
codefrom the frontend, - The backend makes a
POSTrequest to Google token endpoint, to exchange the authorizationcodefor theaccess_tokenand optionally arefresh token - The backend ensures to never expose the client_secret to the frontend. This step should always be handled on the backend.
- The backend will exchange the
codefor anaccess_tokenandrefresh_token, which are sent back to the frontend or stored securely for subsequent API calls.
HTTP Request
HTTPMethod: POSTURL: https://oauth2.googleapis.com/tokenHeaders:Content-Type: application/x-www-form-urlencoded
BodyParameters: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).
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
HTTP Response
{
"access_token": "ya29.a0AfH6SMC8Op6zXZkHi2XITkDoOVzYXt3hTY6sny54UlWlxrnKlX5Xv78is7BEHekVX-VoA",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "1//04d5XHqmn6Hdy3wTf5OYDP1SyBa74zEFURjddQ2A1cFw78PY13pQyWhlD2A6XhDQtKlrjAqU4kS3vGdMvckw",
"scope": "email profile"
}
Example Backend Code:
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');
});
4. [Frontend] Use the Access Token
Once the backend exchanges the code for the access_token,
the frontend can use it to make authenticated requests to the backend or Google APIs
HTTP Request
GET https://xorismesiti.gr/api/user-profile
Authorization: Bearer access-token-from-backend
HTTP Response
{
"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"
}
Example Frontend Code:
// After receiving the token, store it in the frontend (e.g., localStorage or context)
localStorage.setItem('access_token', response.access_token);
// Use it to make authenticated API requests to the backend
fetch('/api/user-profile', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('access_token')}`,
},
})
.then(response => response.json())
.then(data => {
// Handle user data
})
.catch(error => {
console.error('Error fetching user profile:', error);
});
5. [Backend] Fetch User 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.
HTTP Request
GET https://www.googleapis.com/oauth2/v3/userinfo
Authorization: Bearer ya29.a0AfH6SMC8Op6zXZkHi2XITkDoOVzYXt3hTY6sny54UlWlxrnKlX5Xv78is7BEHekVX-VoA
- URL: https://www.googleapis.com/oauth2/v3/userinfo
- HTTP Method: GET
- Headers:
- Authorization: Bearer {access_token}: The access token obtained in step 5.
HTTP Response
{
"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"
}
Example Backend Code:
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)
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.
HTTP Request
POST https://oauth2.googleapis.com/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&
refresh_token=refresh-token-from-backend&
client_id=YOUR_GOOGLE_CLIENT_ID&
client_secret=YOUR_GOOGLE_CLIENT_SECRET
URL: https://oauth2.googleapis.com/tokenHTTPMethod: POSTHeaders:Content-Type: application/x-www-form-urlencodedBodyParameters: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.
HTTP Response
{
"access_token": "ya29.a0AfH6SMC8Op6zXZkHi2XITkDoOVzYXt3hTY6sny54UlWlxrnKlX5Xv78is7BEHekVX-VoA",
"token_type": "Bearer",
"expires_in": 3600
}
Example Backend Code:
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' });
}
});