Skip to content

Commit 85244ca

Browse files
committed
docs: updated social auth docs that was ambiguios before
1 parent 868a994 commit 85244ca

1 file changed

Lines changed: 215 additions & 85 deletions

File tree

‎docs/authentication.md‎

Lines changed: 215 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -128,129 +128,259 @@ await fetch('https://api.ub.bitbros.in/api/userAuth/public/dev_pulse', {
128128
> [!NOTE]
129129
> This endpoint never returns sensitive fields like `password` or `email`.
130130
131-
## 7. Social Auth
131+
## 7. Social Auth (GitHub / Google)
132132

133-
Supported providers:
134-
- GitHub
135-
- Google
133+
Social auth lets your users sign in using their GitHub or Google accounts instead of email/password.
136134

137-
Social auth is configured from the dashboard:
138-
1. Set your project `Site URL` in Project Settings.
139-
2. Open `Auth -> Social Auth`.
140-
3. Copy the read-only callback URL shown for the provider.
141-
4. Register that callback URL in the provider console.
142-
5. Save the provider `Client ID` and `Client Secret` in urBackend and enable the provider.
135+
### Overview: How It Works
143136

144-
### Start the provider login
145-
146-
**Endpoint**: `GET /api/userAuth/social/:provider/start`
147-
148-
Required headers:
149-
- `x-api-key: pk_live_*` or `x-api-key: sk_live_*`
137+
```
138+
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
139+
│ Your App │────▶│ urBackend │────▶│ Provider │────▶│ urBackend │
140+
│ (Frontend) │ │ /start │ │ (GitHub/ │ │ /callback │
141+
│ │ │ │ │ Google) │ │ │
142+
└─────────────┘ └─────────────┘ └─────────────┘ └──────┬──────┘
143+
│
144+
┌─────────────┐ ┌─────────────┐ │
145+
│ Your App │◀────│ urBackend │◀───────────┘
146+
│ /auth/ │ │ redirects │
147+
│ callback │ │ with tokens│
148+
└─────────────┘ └─────────────┘
149+
```
150150

151-
Example:
151+
---
152+
153+
### Step 0: Dashboard Setup (One-time)
154+
155+
Before your frontend can use social auth:
156+
157+
1. **Set Site URL** → Go to `Project Settings` → Add your frontend URL (e.g., `https://myapp.com`)
158+
2. **Configure Provider** → Go to `Auth` → `Social Auth` → Select GitHub or Google
159+
3. **Copy Callback URL** → urBackend shows a read-only callback URL like:
160+
```
161+
https://api.ub.bitbros.in/api/userAuth/social/github/callback
162+
```
163+
4. **Register in Provider Console**:
164+
- GitHub: `Settings → Developer settings → OAuth Apps → New OAuth App`
165+
- Google: `Google Cloud Console → APIs & Services → Credentials → Create OAuth Client`
166+
- Paste the callback URL from Step 3
167+
5. **Save Credentials** → Copy `Client ID` and `Client Secret` from provider → Paste in urBackend dashboard → Enable
168+
169+
---
170+
171+
### Step 1: Create a Callback Page in Your Frontend
172+
173+
> ⚠️ **You MUST create this page!** urBackend will redirect users here after OAuth.
174+
175+
Create a page at `/auth/callback` in your frontend app. This page will:
176+
1. Extract tokens from the URL
177+
2. Exchange `rtCode` for refresh token
178+
3. Store tokens and redirect user to dashboard
179+
180+
**React Example (`/auth/callback`):**
181+
182+
```jsx
183+
// pages/AuthCallback.jsx
184+
import { useEffect } from 'react';
185+
import { useNavigate } from 'react-router-dom';
186+
187+
function AuthCallback() {
188+
const navigate = useNavigate();
189+
190+
useEffect(() => {
191+
async function handleCallback() {
192+
// Step 2a: Check for errors
193+
const queryParams = new URLSearchParams(window.location.search);
194+
const error = queryParams.get('error');
195+
196+
if (error) {
197+
alert('Login failed: ' + error);
198+
navigate('/login');
199+
return;
200+
}
201+
202+
// Step 2b: Extract tokens from URL
203+
const hashParams = new URLSearchParams(window.location.hash.slice(1));
204+
const accessToken = hashParams.get('token');
205+
const rtCode = queryParams.get('rtCode');
206+
207+
if (!accessToken || !rtCode) {
208+
alert('Missing tokens');
209+
navigate('/login');
210+
return;
211+
}
212+
213+
// Step 3: Exchange rtCode for refresh token
214+
const response = await fetch('https://api.ub.bitbros.in/api/userAuth/social/exchange', {
215+
method: 'POST',
216+
headers: {
217+
'Content-Type': 'application/json',
218+
'x-api-key': 'pk_live_YOUR_KEY'
219+
},
220+
body: JSON.stringify({ token: accessToken, rtCode })
221+
});
222+
223+
const data = await response.json();
224+
225+
if (!data.success) {
226+
alert('Token exchange failed');
227+
navigate('/login');
228+
return;
229+
}
230+
231+
// Step 4: Store tokens and redirect
232+
localStorage.setItem('accessToken', accessToken);
233+
localStorage.setItem('refreshToken', data.data.refreshToken);
234+
235+
// Optional: store extra info
236+
localStorage.setItem('provider', queryParams.get('provider'));
237+
localStorage.setItem('userId', queryParams.get('userId'));
238+
239+
navigate('/dashboard');
240+
}
241+
242+
handleCallback();
243+
}, [navigate]);
244+
245+
return <div>Completing login...</div>;
246+
}
152247

153-
```javascript
154-
window.location.href = 'https://api.ub.bitbros.in/api/userAuth/social/github/start';
248+
export default AuthCallback;
155249
```
156250

157-
This endpoint redirects the browser to the provider and uses the project `Site URL` to send users back to:
251+
---
158252

159-
```text
160-
<Site URL>/auth/callback
161-
```
253+
### Step 2: Add Login Button in Your Frontend
254+
255+
When user clicks "Login with GitHub" or "Login with Google", redirect them to urBackend's start endpoint:
162256

163-
### What the callback URL contains
257+
```jsx
258+
// components/LoginButtons.jsx
259+
const API_KEY = 'pk_live_YOUR_KEY';
260+
const API_URL = 'https://api.ub.bitbros.in';
164261

165-
After successful provider login, urBackend redirects to your frontend callback route with:
166-
- `token` in the URL fragment
167-
- `rtCode` in the query string
168-
- `provider`, `projectId`, `userId`, `isNewUser`, and `linkedByEmail` in the query string
262+
function LoginButtons() {
263+
const handleGitHubLogin = () => {
264+
// Redirect browser to urBackend, which redirects to GitHub
265+
window.location.href = `${API_URL}/api/userAuth/social/github/start?x-api-key=${API_KEY}`;
266+
};
169267

170-
Example redirect:
268+
const handleGoogleLogin = () => {
269+
window.location.href = `${API_URL}/api/userAuth/social/google/start?x-api-key=${API_KEY}`;
270+
};
171271

172-
```text
173-
https://your-app.example/auth/callback?rtCode=abc123&provider=github&projectId=proj_1&userId=user_1&isNewUser=false&linkedByEmail=true#token=eyJ...
272+
return (
273+
<div>
274+
<button onClick={handleGitHubLogin}>Login with GitHub</button>
275+
<button onClick={handleGoogleLogin}>Login with Google</button>
276+
</div>
277+
);
278+
}
174279
```
175280

176-
`token` is intentionally placed in the fragment so it is not exposed through normal query-string logging or referrer leakage.
281+
> **Note:** You can also pass `x-api-key` as a header, but since this is a redirect (not fetch), query param is easier.
177282
178-
### Exchange the one-time `rtCode`
283+
---
179284

180-
**Endpoint**: `POST /api/userAuth/social/exchange`
285+
### Complete Flow Summary
181286

182-
Required headers:
287+
| Step | What Happens | Who Does It |
288+
|------|--------------|-------------|
289+
| 1 | User clicks "Login with GitHub" | Your frontend |
290+
| 2 | Browser redirects to `/api/userAuth/social/github/start` | Your frontend |
291+
| 3 | urBackend redirects to GitHub login page | urBackend |
292+
| 4 | User logs in on GitHub | User |
293+
| 5 | GitHub redirects to urBackend callback | GitHub |
294+
| 6 | urBackend creates/links user, generates tokens | urBackend |
295+
| 7 | urBackend redirects to `<your-site>/auth/callback?rtCode=...#token=...` | urBackend |
296+
| 8 | Your callback page extracts `token` and `rtCode` | Your frontend |
297+
| 9 | Your callback page calls `/social/exchange` with both | Your frontend |
298+
| 10 | urBackend returns `refreshToken` | urBackend |
299+
| 11 | Your app stores tokens, redirects to dashboard | Your frontend |
183300

184-
```javascript
185-
{
186-
'Content-Type': 'application/json',
187-
'x-api-key': 'YOUR_KEY'
188-
}
189-
```
301+
---
190302

191-
Required JSON body:
303+
### API Reference
192304

193-
```javascript
194-
{
195-
token: 'ACCESS_TOKEN_FROM_HASH',
196-
rtCode: 'ONE_TIME_CODE_FROM_QUERY'
197-
}
198-
```
305+
#### Start OAuth Flow
199306

200-
Example frontend callback handler:
307+
**Endpoint:** `GET /api/userAuth/social/:provider/start`
201308

202-
```javascript
203-
const hashParams = new URLSearchParams(window.location.hash.slice(1));
204-
const queryParams = new URLSearchParams(window.location.search);
309+
| Param | Value |
310+
|-------|-------|
311+
| `:provider` | `github` or `google` |
312+
| `x-api-key` | Your publishable key (query param or header) |
205313

206-
const token = hashParams.get('token');
207-
const rtCode = queryParams.get('rtCode');
314+
**Response:** Browser redirect to provider login page
208315

209-
if (!token || !rtCode) {
210-
throw new Error('Missing auth callback tokens');
211-
}
316+
---
212317

213-
const response = await fetch('https://api.ub.bitbros.in/api/userAuth/social/exchange', {
214-
method: 'POST',
215-
headers: {
216-
'Content-Type': 'application/json'
217-
},
218-
body: JSON.stringify({ token, rtCode })
219-
});
318+
#### Exchange rtCode for Refresh Token
319+
320+
**Endpoint:** `POST /api/userAuth/social/exchange`
220321

221-
const payload = await response.json();
322+
**Headers:**
323+
```
324+
Content-Type: application/json
325+
x-api-key: pk_live_YOUR_KEY
222326
```
223327

224-
Successful response:
328+
**Body:**
329+
```json
330+
{
331+
"token": "eyJ... (access token from URL fragment)",
332+
"rtCode": "abc123 (one-time code from query string)"
333+
}
334+
```
225335

226-
```javascript
336+
**Success Response:**
337+
```json
227338
{
228-
success: true,
229-
data: {
230-
refreshToken: 'REFRESH_TOKEN_VALUE'
339+
"success": true,
340+
"data": {
341+
"refreshToken": "REFRESH_TOKEN_VALUE"
231342
},
232-
message: 'Refresh token exchanged successfully'
343+
"message": "Refresh token exchanged successfully"
233344
}
234345
```
235346

236-
Expected client behavior after success:
237-
1. Keep using the original `token` as the access token.
238-
2. Store `data.refreshToken` for session continuation.
239-
3. Optionally store `provider`, `projectId`, and `userId` from the callback query string.
240-
4. Redirect the user into the authenticated part of your app.
347+
**Error Responses:**
348+
```json
349+
{ "success": false, "message": "rtCode and token are required" }
350+
{ "success": false, "message": "Invalid or expired refresh token exchange code" }
351+
```
241352

242-
Common error responses:
353+
---
243354

244-
```javascript
245-
{ success: false, message: 'rtCode and token are required' }
246-
{ success: false, message: 'Invalid or expired refresh token exchange code' }
247-
{ success: false, message: 'Invalid refresh token exchange payload' }
355+
### What Gets Passed to Your Callback URL
356+
357+
After successful login, urBackend redirects to:
358+
359+
```
360+
https://your-site.com/auth/callback?rtCode=abc&provider=github&projectId=p1&userId=u1&isNewUser=false&linkedByEmail=true#token=eyJ...
248361
```
249362

250-
Notes:
251-
- `rtCode` is short-lived and one-time use.
252-
- Existing users are linked by verified email when possible.
253-
- New social-auth users still receive a generated hashed password internally to satisfy the required `users` schema contract.
363+
| Param | Location | Description |
364+
|-------|----------|-------------|
365+
| `token` | URL fragment (`#`) | Access token (JWT) - use for API calls |
366+
| `rtCode` | Query string | One-time code to exchange for refresh token |
367+
| `provider` | Query string | `github` or `google` |
368+
| `projectId` | Query string | Your urBackend project ID |
369+
| `userId` | Query string | The user's ID in your database |
370+
| `isNewUser` | Query string | `true` if account was just created |
371+
| `linkedByEmail` | Query string | `true` if existing account was linked |
372+
| `error` | Query string | Error message (only on failure) |
373+
374+
> **Why is `token` in the fragment?** Fragments (`#...`) are never sent to servers in HTTP requests, preventing token leakage through referrer headers or server logs.
375+
376+
---
377+
378+
### Important Notes
379+
380+
1. **rtCode expires in 60 seconds** - Exchange it immediately
381+
2. **rtCode is one-time use** - Cannot be reused
382+
3. **Account linking** - If a user with the same verified email exists, accounts are automatically linked
383+
4. **New users** - A hashed password is auto-generated internally (users can still set a real password later)
254384

255385
## Security Note
256386

0 commit comments

Comments
 (0)