From 4457e53ff1a0a4e24a9a56e7a0ada3e10790f229 Mon Sep 17 00:00:00 2001 From: Ste Vaidis Date: Tue, 7 Jan 2025 16:38:45 +0200 Subject: [PATCH] Update OAuth2-Backend-Approach.md --- OAuth2-Backend-Approach.md | 90 +++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/OAuth2-Backend-Approach.md b/OAuth2-Backend-Approach.md index f342b00..c5d173f 100644 --- a/OAuth2-Backend-Approach.md +++ b/OAuth2-Backend-Approach.md @@ -1,6 +1,6 @@ ### OAuth2 purpose -A way for the `user` to tell `google` to give an access to `xorismesiti.gr` app +A way for the `user` to tell `google` to give an access to `myapp` app
@@ -12,25 +12,25 @@ A way for the `user` to tell `google` to give an access to `xorismesiti.gr` app | 2 | Exchange Code with Token | ` Back ⇢ Google ⇢ Back ⇢ Front `| | 4 | Use Token | ` Front ⇢ Back ⇢ Google ⇢ Back ⇢ Front `| -
+

### Details: -1. Get Code +1. Get Authorization Code - 1. Frontend **GET** to Google `https://accounts.google.com/o/oauth2` with callback url - 2. Google **302** to Backend `https://xorismesiti.gr/api/auth/callback` with authorization code + 1. Frontend **Navigate** to Google URL with a callback url + 2. Google **Redirect** to Backend's callback url with the authorization code 2. Exchange Code with Token 1. Backend **POST** the `code` to Google `https://oauth2.googleapis.com/token` 2. Google **response** to Backend with an `access_token` and a `refresh token` - 3. Backend **response** to Frontend with the `access_token` in a `cookie` + 3. Backend **redirect** to Frontend `https://myapp/auth/success` with the `access_token` in a `cookie` 3. Use Token - 1. Frontend **GET** profile data from Backend `https://xorismesiti.gr/api/auth/profile` using the `cookie` + 1. Frontend **GET** profile data from Backend `https://myapp/api/auth/profile` using the `cookie` 2. Backend **GET** profile data from Google `https://www.googleapis.com/oauth2/v3/userinfo` using the `access_token` from Frontend `cookie` 3. Google **response** to Backend with profile data 4. Backend **response** to Frontend with profile data @@ -42,24 +42,24 @@ A way for the `user` to tell `google` to give an access to `xorismesiti.gr` app # 1. Get Code 1. Frontend **GET** to Google `https://accounts.google.com/o/oauth2` with callback url -2. Google **302** to Backend `https://xorismesiti.gr/api/auth/callback` with authorization code +2. Google **302** to Backend `https://myapp/api/auth/callback` with authorization code ### 1. Front **GET** to Google ```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/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. + 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://myapp/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. ``` ### 2. Google **302** to Back ```bash HTTP/1.1 302 Found -Location: https://xorismesiti.gr/api/auth/callback?code=4/0AX4XfWgyVyz-uT8k7WiyLg0Q&state=xyz123 +Location: https://myapp/api/auth/callback?code=4/0AX4XfWgyVyz-uT8k7WiyLg0Q&state=xyz123 Content-Type: text/html; charset=UTF-8 Content-Length: 0 ``` @@ -86,7 +86,7 @@ grant_type=authorization_code& code=AAAABCX4XfWgyVyziyLg0QHHHHH& client_id=ABC34JHS9D& client_secret=PASS1234& -redirect_uri=https://xorismesiti.gr/callback +redirect_uri=https://myapp/callback ``` ### 2. Google **response** to Backend @@ -129,33 +129,43 @@ redirect_uri=https://xorismesiti.gr/callback

```js -// 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 - +app.get('/callback', async (req, res) => { 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, // Same as in Step 1 - grant_type: 'authorization_code', + // 1. Get the authorization code from Google's redirect + const { code } = req.query; + + // 2. Exchange the code for tokens + const tokenResponse = await fetch('https://oauth2.googleapis.com/token', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', }, + body: JSON.stringify({ + code, + client_id: process.env.GOOGLE_CLIENT_ID, + client_secret: process.env.GOOGLE_CLIENT_SECRET, + redirect_uri: 'https://myapp/callback', + grant_type: 'authorization_code', + }), }); - - const { access_token, refresh_token, expires_in } = response.data; - - // 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 }); - - // Respond to frontend or redirect as needed - res.send('Authentication successful! You can now use the app.'); + + const { access_token, refresh_token } = await tokenResponse.json(); + + // 3. Set the HTTP-only cookie with the access token + res.cookie('access', access_token, { + httpOnly: true, // Cannot be accessed by client-side JavaScript + secure: true, // Only sent over HTTPS + sameSite: 'strict', // CSRF protection + maxAge: 24 * 60 * 60 * 1000, // 24 hours + }); + + // 4. Store refresh token securely in database (recommended) + // await db.storeRefreshToken(user_id, refresh_token); + + // 5. Redirect back to frontend + res.redirect('https://myapp/authentication-success'); } catch (error) { - console.error('Error exchanging code for tokens:', error); - res.status(500).send('Error during authentication'); + res.redirect('https://myapp/authentication-error'); } }); ``` @@ -173,7 +183,7 @@ app.get('/auth/google/callback', async (req, res) => { ### 1. Frontend **GET** profile data from Backend ```bash -curl -X GET https://xorismesiti.gr/api/auth/profile \ +curl -X GET https://myapp/api/auth/profile \ -H "Cookie: access_token=ya29.a0AfH6SMC8Op6zXZkHi2XITkDoOVzYXt3hTY6sny54UlWlxrnKlX5Xv78is7BEHekVX-VoA" \ -H "Accept: application/json" ``` @@ -282,7 +292,7 @@ export default App; ------------------------- ```bash -GET https://xorismesiti.gr/api/user-profile +GET https://myapp/api/user-profile Authorization: Bearer access-token-from-backend ```