diff --git a/OAuth2-Backend-Approach.md b/OAuth2-Backend-Approach.md index e8687f1..2c7b36e 100644 --- a/OAuth2-Backend-Approach.md +++ b/OAuth2-Backend-Approach.md @@ -24,10 +24,9 @@ A way for the `user` to tell `google` to give an access to `xorismesiti.gr` app 2. Exchange Code with Token - 1. Front **POST** the `code` to the Back `https://xorismesiti.gr/api/auth/exchange-token` - 2. Back **POST** the `code` to Google `https://oauth2.googleapis.com/token` - 3. Google **response** to Back with an `access_token` and a `refresh token` - 4. Back **response** to Front with the `access_token` in a `cookie` + 1. Back **POST** the `code` to Google `https://oauth2.googleapis.com/token` + 2. Google **response** to Back with an `access_token` and a `refresh token` + 3. Back **response** to Front with the `access_token` in a `cookie` 3. Use Token @@ -46,14 +45,14 @@ A way for the `user` to tell `google` to give an access to `xorismesiti.gr` app ```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://xorismesiti.gr/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. + 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://xorismesiti.gr/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. ``` -### Google **302** to Front +### Google **302** to Back ```bash HTTP/1.1 302 Found @@ -64,145 +63,11 @@ 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 +# 2. Exchange Code with Token +### 1. Back **POST** the `code` to Google - - - - - - - - - - - - - - - - - - -
-

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. [Frontend] Receive Authorization Code - -Now that the frontend has the Authorization `code` on th callback url https://xorismesiti.gr/api/auth/callback`?code=AAAABCX4XfWgyVyziyLg0QHHHHH` it can send it to the backend with POST to `xorismesiti.gr/api/auth/exchange-token`, in order the backend to exchange the `code` for an `access_token` and optionally an `refresh_token` - -
-

Frontend HTTP POST Request to Backend

- -```bash -POST https://xorismesiti.gr/api/auth/exchange-token -Content-Type: application/json - -{ - "code": "AAAABCX4XfWgyVyziyLg0QHHHHH" -} -``` - -
- -#### Step 3 takes place here, Backend exchanges the Code with the Tokens - -
-

Backend HTTP Response to Frontend

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

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] Exchange Code with Token - -1. The backend **receives** the authorization `code` form frontend (Frontend POST at `xorismesiti.gr/api/auth/exchange-token`) -2. The backend **POST** Authorization `code` to Google API -3. The Google API respond to backend POST with the tokens `access_token` and `refresh_token` -4. The backend **respond** to frontend with the tokens (respond to frontend POST at `xorismesiti.gr/api/auth/exchange-token`) - -*Security: The backend never expose the client_secret to the frontend. This step should always be handled on the backend.* - -
-

Backend HTTP POST Request to Google

+The Backend **POST** to Google ```sh POST https://oauth2.googleapis.com/token @@ -215,88 +80,71 @@ 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). - -
- -
-

Google HTTP Response Backend

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

Backend Code:

+The 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; +// Backend callback URL: http://localhost:3000/auth/google/callback +app.get('/auth/google/callback', async (req, res) => { + const { code } = req.query; // Extract the code from the query string try { + // Exchange the authorization code for access token and refresh token const response = await axios.post('https://oauth2.googleapis.com/token', null, { params: { code, client_id: clientId, client_secret: clientSecret, - redirect_uri: redirectUri, + redirect_uri: redirectUri, // Same as in Step 1 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' }); - } -}); + // Store tokens in session or cookies + res.cookie('access_token', access_token, { httpOnly: true, secure: true, maxAge: expires_in * 1000 }); + res.cookie('refresh_token', refresh_token, { httpOnly: true, secure: true }); -// Start the server -app.listen(3000, () => { - console.log('Server running on http://localhost:3000'); + // Respond to frontend or redirect as needed + res.send('Authentication successful! You can now use the app.'); + } catch (error) { + console.error('Error exchanging code for tokens:', error); + res.status(500).send('Error during authentication'); + } }); ``` -
- -


+ + + + + + + + + + +------------------------- + + + # 4. [Frontend] Use the Token 1. The frontend receives the tokens from the Backend response,