Skip to content

Commit b9c3235

Browse files
Added an OAuth flow example
1 parent 441b81f commit b9c3235

File tree

5 files changed

+153
-1
lines changed

5 files changed

+153
-1
lines changed

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
tests/
22
testapp/
33
jstestapp/
4+
examples/
45
.idea/
56
node_modules/
67
.github/

.prettierignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ testapp/
22
dist/
33
.idea/
44
node_modules/
5-
docs/
5+
docs/
6+
examples/

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ This library is a wrapper around the [Modrinth API](https://docs.modrinth.com/),
3939
- [Get an access token from an authorization code](#get-an-access-token-from-an-authorization-code)
4040
- [Miscellanous](#miscellanous)
4141
- [Get statistics](#get-statistics)
42+
- [Examples](#examples)
4243
- [Coverage](#coverage)
4344
- [License](#license)
4445

@@ -323,6 +324,10 @@ const stats = await modrinth.getStatistics();
323324

324325
[> Typedoc](https://typerinth.js.org/classes/Modrinth.html#getstatistics)
325326

327+
## Examples
328+
329+
- [OAuth flow example](https://github.com/KartoffelChipss/Typerinth/tree/main/examples/oauth) - A simple example on how to implement a secure OAuth 2.0 login flow using Typerinth
330+
326331
## Coverage
327332

328333
You can find a list of the endpoints covered by this library in the [coverage.md](https://github.com/KartoffelChipss/Typerinth/blob/main/coverage.md) file.

examples/oauth/README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Modrinth OAuth Example with Typerinth and Express
2+
3+
This project demonstrates how to implement a secure OAuth 2.0 login flow using [Modrinth's OAuth API](https://docs.modrinth.com/guide/oauth/) via the [`Typerinth`](https://www.npmjs.com/package/typerinth) library, Express, and express-session.
4+
5+
---
6+
7+
## 🚀 What This Does
8+
9+
- Redirects users to Modrinth's authorization page
10+
- Validates the OAuth `state` to prevent CSRF
11+
- Exchanges an authorization code for an access token
12+
- Uses the token to retrieve the authenticated user's Modrinth account info
13+
14+
---
15+
16+
## 🧰 Technologies Used
17+
18+
- [Typerinth](https://www.npmjs.com/package/typerinth)
19+
- [Express](https://expressjs.com/)
20+
- [express-session](https://www.npmjs.com/package/express-session)
21+
- [dotenv](https://www.npmjs.com/package/dotenv)
22+
- TypeScript
23+
24+
---
25+
26+
## 📦 Setup
27+
28+
### 1. Install the dependencies
29+
30+
Install the dependencies using a package manager of your choice.
31+
32+
### 2. Configure environment variables
33+
34+
Create a .env file:
35+
36+
```bash
37+
CLIENT_ID=your-modrinth-client-id
38+
CLIENT_SECRET=your-modrinth-client-secret
39+
REDIRECT_URI=http://localhost:3000/callback
40+
SESSION_SECRET=super-secret-key
41+
```
42+
43+
## ⚠️ Disclaimer
44+
45+
This is a **simplified example** intended for educational and demonstration purposes. It does not include advanced features like token refresh, error recovery, persistent user sessions, or HTTPS enforcement.
46+
47+
**Use at your own risk.**
48+
49+
## 📄 License
50+
51+
You can find the full license [here](https://github.com/KartoffelChipss/Typerinth/blob/main/LICENSE)

examples/oauth/server.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import express from 'express';
2+
import session from 'express-session';
3+
import { Modrinth, AuthScope } from 'typerinth';
4+
import dotenv from 'dotenv';
5+
import crypto from 'crypto';
6+
7+
dotenv.config();
8+
9+
const app = express();
10+
const PORT = process.env.PORT || 3000;
11+
12+
const { CLIENT_ID, CLIENT_SECRET, REDIRECT_URI, SESSION_SECRET } = process.env;
13+
14+
if (!CLIENT_ID || !CLIENT_SECRET || !REDIRECT_URI || !SESSION_SECRET) {
15+
throw new Error('Missing environment variables.');
16+
}
17+
18+
const USER_AGENT = 'MyApp/1.0'; // Replace with your app's user agent
19+
20+
const modrinth = new Modrinth({
21+
userAgent: USER_AGENT,
22+
});
23+
24+
// Session setup
25+
app.use(
26+
session({
27+
secret: SESSION_SECRET,
28+
resave: false,
29+
saveUninitialized: true,
30+
cookie: { secure: false }, // use `cookie: { secure: true, sameSite: 'lax' }` in production behind HTTPS
31+
})
32+
);
33+
34+
declare module 'express-session' {
35+
interface SessionData {
36+
oauthState?: string;
37+
}
38+
}
39+
40+
// Login Route
41+
app.get('/login', (req, res) => {
42+
const state = crypto.randomUUID(); // Generate unique state
43+
req.session.oauthState = state;
44+
45+
const authUrl = modrinth.generateAuthorizationUrl(
46+
CLIENT_ID,
47+
REDIRECT_URI,
48+
[AuthScope.UserRead, AuthScope.PayoutsRead],
49+
state
50+
);
51+
52+
res.redirect(authUrl);
53+
});
54+
55+
// Callback Route
56+
app.get('/callback', async (req, res) => {
57+
const code = req.query.code as string;
58+
const state = req.query.state as string;
59+
const storedState = req.session.oauthState;
60+
61+
if (!state || state !== storedState) {
62+
res.status(400).send('Invalid or missing state parameter.');
63+
return;
64+
}
65+
66+
if (!code) {
67+
res.status(400).send('Missing code parameter.');
68+
return;
69+
}
70+
71+
try {
72+
const token = await modrinth.getToken(
73+
code,
74+
CLIENT_ID,
75+
REDIRECT_URI,
76+
CLIENT_SECRET
77+
);
78+
79+
// Use the token to make API requests
80+
const user = await modrinth.getAuthUser(token.access_token);
81+
res.json({
82+
message: 'Successfully authenticated with Modrinth!',
83+
user,
84+
});
85+
} catch (error) {
86+
console.error('OAuth callback error:', error);
87+
res.status(500).send('Failed to authenticate.');
88+
}
89+
});
90+
91+
// Start server
92+
app.listen(PORT, () => {
93+
console.log(`Server is running at http://localhost:${PORT}`);
94+
});

0 commit comments

Comments
 (0)