Update OAuth2-Backend-Approach.md
This commit is contained in:
parent
dda4d0bfe2
commit
39b1c4ed52
@ -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
|
||||
|
||||
@ -48,12 +47,12 @@ A way for the `user` to tell `google` to give an access to `xorismesiti.gr` app
|
||||
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.
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<details>
|
||||
<summary><h3>Frontend Code</h3></summary>
|
||||
|
||||
```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
|
||||
};
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<br><br><br>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 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`
|
||||
|
||||
<details>
|
||||
<summary><h3>Frontend HTTP POST Request to Backend</h3></summary>
|
||||
|
||||
```bash
|
||||
POST https://xorismesiti.gr/api/auth/exchange-token
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"code": "AAAABCX4XfWgyVyziyLg0QHHHHH"
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Step 3 takes place here, Backend exchanges the Code with the Tokens
|
||||
|
||||
<details>
|
||||
<summary><h3>Backend HTTP Response to Frontend</h3></summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"access_token": "ya29.a0AfH6SMC8Op6zXZkHi2XITkDoOVzYXt3hTY6sny54UlWlxrnKlX5Xv78is7BEHekVX-VoA",
|
||||
"token_type": "Bearer",
|
||||
"expires_in": 3600,
|
||||
"refresh_token": "1//04d5XHqmn6Hdy3wTf5OYDP1SyBa74zEFURjddQ2A1cFw78PY13pQyWhlD2A6XhDQtKlrjAqU4kS3vGdMvckw",
|
||||
"scope": "email profile"
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><h3>Frontend Code</h3></summary>
|
||||
|
||||
```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 <div>Loading...</div>;
|
||||
};
|
||||
|
||||
export default Callback;
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<br><br><br>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 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.*
|
||||
|
||||
<details>
|
||||
<summary><h3>Backend HTTP POST Request to Google</h3></summary>
|
||||
The Backend **POST** to Google
|
||||
|
||||
```sh
|
||||
POST https://oauth2.googleapis.com/token
|
||||
@ -215,21 +80,7 @@ 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).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><h3>Google HTTP Response Backend</h3></summary>
|
||||
The **response** from Google
|
||||
|
||||
```json
|
||||
{
|
||||
@ -241,62 +92,59 @@ client_secret=PASS1234
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><h3>Backend Code:</h3></summary>
|
||||
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');
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<br><br><br>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-------------------------
|
||||
|
||||
|
||||
|
||||
# 4. [Frontend] Use the Token
|
||||
|
||||
1. The frontend receives the tokens from the Backend response,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user