Skip to content

Commit 2ea8179

Browse files
authored
[codex] follow up oauth quickstart fixes (#93)
* docs: follow up oauth quickstart fixes * docs: address follow-up review comments
1 parent 89fc2e2 commit 2ea8179

3 files changed

Lines changed: 50 additions & 14 deletions

File tree

‎docs/tutorials/oidc/getting-started-with-oauth2.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,10 @@ sequenceDiagram
219219

220220
## 🧩 Step 1: Request OAuth 2.0 Client Credentials
221221

222-
1. Submit an [Application](/request-access) to obtain your `client_id` and confirm whether Quran Foundation provisioned the client as public or confidential.
222+
1. Submit an [Application](/request-access) to obtain your OAuth client credentials: you will always receive a `client_id`, and confidential clients will also receive a `client_secret` for server-side use only.
223223
2. Provide one or more exact `redirect_uri` values. These must match exactly at runtime.
224224

225-
> Most Request Access clients should let the frontend or native app start the login flow and generate PKCE, then use a backend for token exchange and refresh. Never embed `client_secret` in a browser or mobile app.
225+
> Most Request Access clients should let the frontend or native app start the login flow and generate PKCE, then use a backend for token exchange and refresh. For confidential clients, keep the `client_secret` on the server only and never embed it in a browser or mobile app.
226226
227227
:::important Client Type Is Set During Provisioning
228228
Hydra/Ory decides whether your client is public or confidential when Quran Foundation provisions the OAuth client, not when you write the integration code.

‎docs/tutorials/oidc/mobile-apps/react-native.mdx‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,15 @@ const CLIENT_ID = "YOUR_CLIENT_ID"; // From your OAuth application
4040
const REDIRECT_URI = "exp://192.168.x.x:8081"; // Your Expo dev URL or custom scheme
4141
const BACKEND_BASE_URL = "https://your-backend.example.com";
4242
const USE_PRELIVE = true; // set to false for production
43+
const authBaseUrl = USE_PRELIVE
44+
? "https://prelive-oauth2.quran.foundation"
45+
: "https://oauth2.quran.foundation";
4346

44-
// OAuth2 authorization endpoint
47+
// OAuth2 endpoints
4548
const discovery = {
46-
authorizationEndpoint: USE_PRELIVE
47-
? "https://prelive-oauth2.quran.foundation/oauth2/auth"
48-
: "https://oauth2.quran.foundation/oauth2/auth",
49+
authorizationEndpoint: `${authBaseUrl}/oauth2/auth`,
50+
tokenEndpoint: `${authBaseUrl}/oauth2/token`,
51+
revocationEndpoint: `${authBaseUrl}/oauth2/revoke`,
4952
};
5053

5154
export default function App() {
@@ -108,6 +111,8 @@ export default function App() {
108111
const logout = async () => {
109112
await fetch(`${BACKEND_BASE_URL}/api/auth/qf/logout`, {
110113
method: "POST",
114+
headers: { "Content-Type": "application/json" },
115+
body: JSON.stringify({ refreshToken: authSession?.refreshToken }),
111116
}).catch(() => {});
112117
setAuthSession(null);
113118
};

‎docs/tutorials/oidc/user-apis-quickstart.mdx‎

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ Get user authentication working in **under 5 minutes**. This guide shows you the
1919

2020
## 📋 Prerequisites
2121

22-
1. **Get OAuth2 credentials and confirm how Quran Foundation provisioned the client**: [Request Access →](/request-access)
23-
2. **Register your redirect URI** (e.g., `http://localhost:3000/callback` or `yourapp://callback`)
22+
1. **Get OAuth2 credentials**: [Request Access →](/request-access)
23+
2. **Register your redirect URI**, for example `http://localhost:3000/callback` or `yourapp://callback`
2424

2525
:::important Recommended Integration Shape
2626
- Frontend and native apps can still use Quran Foundation's hosted login screen and PKCE.
@@ -59,12 +59,15 @@ const CLIENT_ID = "YOUR_CLIENT_ID";
5959
const REDIRECT_URI = "yourapp://callback";
6060
const BACKEND_BASE_URL = "https://your-backend.example.com";
6161
const USE_PRELIVE = true; // set to false for production
62+
const authBaseUrl = USE_PRELIVE
63+
? "https://prelive-oauth2.quran.foundation"
64+
: "https://oauth2.quran.foundation";
6265

63-
// OAuth2 authorization endpoint
66+
// OAuth2 endpoints
6467
const discovery = {
65-
authorizationEndpoint: USE_PRELIVE
66-
? "https://prelive-oauth2.quran.foundation/oauth2/auth"
67-
: "https://oauth2.quran.foundation/oauth2/auth",
68+
authorizationEndpoint: `${authBaseUrl}/oauth2/auth`,
69+
tokenEndpoint: `${authBaseUrl}/oauth2/token`,
70+
revocationEndpoint: `${authBaseUrl}/oauth2/revoke`,
6871
};
6972

7073
export default function App() {
@@ -119,6 +122,8 @@ export default function App() {
119122
const logout = async () => {
120123
await fetch(`${BACKEND_BASE_URL}/api/auth/qf/logout`, {
121124
method: "POST",
125+
headers: { "Content-Type": "application/json" },
126+
body: JSON.stringify({ refreshToken: session?.refreshToken }),
122127
}).catch(() => {});
123128

124129
setUser(null);
@@ -148,7 +153,7 @@ export default function App() {
148153
}
149154
```
150155
151-
If Quran Foundation provisioned your client as public with `token_endpoint_auth_method=none`, you can swap the backend call above for a direct `exchangeCodeAsync(...)` call in the app.
156+
If Quran Foundation provisioned your client as public with `token_endpoint_auth_method=none`, you can swap the backend call above for a direct `exchangeCodeAsync(...)` call in the app using the same `discovery.tokenEndpoint`.
152157
153158
👉 **[Full React Native Example Repo](https://github.com/quran/oauth2-react-native-client-example)** shows the direct public-client variant
154159
@@ -306,12 +311,38 @@ app.post("/api/auth/qf/refresh", async (req, res) => {
306311
}
307312
});
308313

314+
app.post("/api/auth/qf/logout", async (req, res) => {
315+
const { refreshToken } = req.body;
316+
317+
if (!refreshToken) {
318+
return res.status(204).send();
319+
}
320+
321+
try {
322+
const params = new URLSearchParams();
323+
params.append("token", refreshToken);
324+
params.append("token_type_hint", "refresh_token");
325+
326+
await axios.post(`${authBaseUrl}/oauth2/revoke`, params.toString(), {
327+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
328+
auth: {
329+
username: process.env.CLIENT_ID,
330+
password: process.env.CLIENT_SECRET,
331+
},
332+
});
333+
} catch (error) {
334+
// Treat logout as best effort so the app can still clear local state.
335+
}
336+
337+
res.status(204).send();
338+
});
339+
309340
app.listen(3000, () => {
310341
console.log("QF backend exchange service running on http://localhost:3000");
311342
});
312343
```
313344
314-
If you already use backend sessions, keep the `refresh_token` on the server and return only the short-lived access token and decoded user profile to the app.
345+
If you already use backend sessions, keep the `refresh_token` on the server and return only the short-lived access token and decoded user profile to the app. In that setup, `/api/auth/qf/logout` should revoke and clear the server-stored refresh token instead of expecting it from the app.
315346
316347
---
317348

0 commit comments

Comments
 (0)