diff --git a/OAuth2-Backend-Approach.md b/OAuth2-Backend-Approach.md index ea82a2a..f5d1e07 100644 --- a/OAuth2-Backend-Approach.md +++ b/OAuth2-Backend-Approach.md @@ -134,8 +134,51 @@ app.get('/auth/google/callback', async (req, res) => { }); ``` + + + + + + + # 3. Use Token +1. Front **GET** profile data from Back `https://xorismesiti.gr/api/auth/profile` using the `cookie` +2. Back **GET** profile data from Google `https://www.googleapis.com/oauth2/v3/userinfo` using the `access_token` from Front `cookie` +3. Google **response** to Back with profile data +4. Back **response** to Front with profile data + + +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. Back **response** to Front with profile data + +``` +{ + "sub": "1234567890", + "name": "John Doe", + "picture": "https://lh3.googleusercontent.com/8bgVEOykQ6ykFSQ=s96", + "email": "john.doe@example.com", +} +``` + + ```js import React, { useState, useEffect } from 'react'; import axios from 'axios'; @@ -202,30 +245,11 @@ export default App; ------------------------- - - - -# 4. [Frontend] Use the Token - -1. The frontend receives the tokens from the Backend response, -2. Store them in the localStorage of the browser -3. Make authenticated requests directly to Google API - -
-

Frontend HTTP GET Request to Backend

- ```bash GET https://xorismesiti.gr/api/user-profile Authorization: Bearer access-token-from-backend ``` -
- -**↴** Step 5 takes place here: The Backend request the user data from Google using the Tokens - -
-

Backedn HTTP Response to Frontend

- ```json { "sub": "1234567890", @@ -240,127 +264,11 @@ Authorization: Bearer access-token-from-backend } ``` -
- -
-

Frontend Code

- -```js -// Store tokens and expiration time after receiving them from the backend -const storeTokens = (access_token, refresh_token, expires_in) => { - const expirationTime = Date.now() + expires_in * 1000; // expires_in is in seconds, so multiply by 1000 to get ms - localStorage.setItem('access_token', access_token); - localStorage.setItem('refresh_token', refresh_token); - localStorage.setItem('token_expiration', expirationTime); -}; - -// Function to check if the access token has expired -const isTokenExpired = () => { - const expirationTime = localStorage.getItem('token_expiration'); - return Date.now() > expirationTime; -}; - -// Function to refresh the access token using the refresh token -const refreshAccessToken = async () => { - const refresh_token = localStorage.getItem('refresh_token'); - try { - const response = await fetch('/api/auth/refresh-token', { - method: 'POST', - body: JSON.stringify({ refresh_token }), - headers: { - 'Content-Type': 'application/json', - }, - }); - - const data = await response.json(); - if (data.access_token) { - // Store the new access token and expiration time - storeTokens(data.access_token, data.refresh_token, data.expires_in); - return data.access_token; - } - } catch (error) { - console.error('Error refreshing token:', error); - return null; // Handle error appropriately (e.g., logout user or prompt to log in again) - } -}; - -// Function to get the access token, either from localStorage or by refreshing it if expired -const getAccessToken = async () => { - if (isTokenExpired()) { - console.log('Access token expired. Refreshing...'); - const newAccessToken = await refreshAccessToken(); - return newAccessToken; - } else { - return localStorage.getItem('access_token'); - } -}; - -// Example usage: Get the access token and use it for an authenticated API request -const fetchUserProfile = async () => { - const access_token = await getAccessToken(); - - if (!access_token) { - console.error('No valid access token found. User might need to log in.'); - return; - } - - // Now you can use the access token to make an authenticated API request - try { - const response = await fetch('/api/user-profile', { - headers: { - 'Authorization': `Bearer ${access_token}`, - }, - }); - - const userData = await response.json(); - console.log(userData); - } catch (error) { - console.error('Error fetching user profile:', error); - } -}; - -// Example call to fetch user profile -fetchUserProfile(); -``` - -
- -


- - - - - - - - - -# 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 GET Request

- ```sh 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

- ```json { "sub": "1234567890", @@ -377,179 +285,10 @@ Authorization: Bearer ya29.a0AfH6SMC8Op6zXZkHi2XITkDoOVzYXt3hTY6sny54UlWlxrnKlX5 -
- -
-

Backend GET 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 - -// Function to validate the access token by making a request to the Google user info endpoint -const validateAccessToken = async (accessToken) => { - try { - const response = await axios.get('https://www.googleapis.com/oauth2/v3/userinfo', { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }); - - // If this succeeds, the token is valid - return response.data; // Return the user data - } catch (error) { - // If the request fails (token is invalid or expired), throw an error - throw new Error('Invalid or expired access token'); - } -}; - -// Example route for fetching the user profile -app.get('/api/user-profile', async (req, res) => { - const accessToken = req.headers['authorization']?.split(' ')[1]; // Extract token from Authorization header - - if (!accessToken) { - return res.status(400).json({ error: 'No access token provided' }); - } - - try { - // Validate the access token by fetching user info from Google - const userData = await validateAccessToken(accessToken); - - // Send the user profile data to the frontend - res.json(userData); - } catch (error) { - // Handle invalid or expired token - console.error('Error validating access token:', error); - res.status(401).json({ error: 'Invalid or expired access token' }); - } -}); - -// Start the server -app.listen(3000, () => { - console.log('Server running on http://localhost:3000'); -}); -``` - -
- -
-

Front GET Code

- -```js -// 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 => { - if (response.status === 401) { - // Handle token expiration or invalid token - console.error('Access token expired or invalid. Please log in again.'); - // Optionally, redirect to login page or refresh token - } else { - return response.json(); - } - }) - .then(data => { - // Handle user data - console.log(data); - }) - .catch(error => { - console.error('Error fetching user profile:', error); - }); -``` -
- -


- - - - - - - -# 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 POST Request

- -```bash -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/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. - -
- -
-

HTTP Response

- ```json { "access_token": "ya29.a0AfH6SMC8Op6zXZkHi2XITkDoOVzYXt3hTY6sny54UlWlxrnKlX5Xv78is7BEHekVX-VoA", "token_type": "Bearer", "expires_in": 3600 } -``` - -
- -
-

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' }); - } -}); -``` - -
- - - +``` \ No newline at end of file