@@ -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