@drmhse/authos-node 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +269 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# @drmhse/authos-node
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@drmhse/authos-node)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
Node.js server adapter for [AuthOS](https://authos.dev) - the multi-tenant authentication platform. Provides JWT verification, webhook signature validation, and Express middleware.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @drmhse/authos-node
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
For Express middleware:
|
|
15
|
+
```bash
|
|
16
|
+
npm install @drmhse/authos-node express
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
### JWT Token Verification
|
|
22
|
+
|
|
23
|
+
Verify JWT tokens issued by AuthOS:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { createTokenVerifier } from '@drmhse/authos-node';
|
|
27
|
+
|
|
28
|
+
const verifier = createTokenVerifier({
|
|
29
|
+
baseURL: 'https://sso.example.com'
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Verify a token
|
|
33
|
+
const verified = await verifier.verifyToken(token);
|
|
34
|
+
|
|
35
|
+
console.log(verified.claims);
|
|
36
|
+
// {
|
|
37
|
+
// sub: 'user_123',
|
|
38
|
+
// email: 'user@example.com',
|
|
39
|
+
// is_platform_owner: false,
|
|
40
|
+
// org: 'acme-corp',
|
|
41
|
+
// permissions: ['users:read', 'users:write']
|
|
42
|
+
// }
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### TypeScript Integration
|
|
46
|
+
|
|
47
|
+
To get type support for `req.auth` in Express, you can extend the Request interface or use our utility type:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { Request } from 'express';
|
|
51
|
+
import { TokenClaims } from '@drmhse/authos-node';
|
|
52
|
+
|
|
53
|
+
// Extend Express Request
|
|
54
|
+
declare global {
|
|
55
|
+
namespace Express {
|
|
56
|
+
interface Request {
|
|
57
|
+
auth?: {
|
|
58
|
+
claims: TokenClaims;
|
|
59
|
+
token: string;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Express Middleware
|
|
67
|
+
|
|
68
|
+
Protect your Express routes with AuthOS authentication:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import { createAuthMiddleware } from '@drmhse/authos-node/express';
|
|
72
|
+
import express from 'express';
|
|
73
|
+
|
|
74
|
+
const app = express();
|
|
75
|
+
|
|
76
|
+
const { requireAuth, requirePermission } = createAuthMiddleware({
|
|
77
|
+
baseURL: process.env.AUTHOS_URL!
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Public route
|
|
81
|
+
app.get('/', (req, res) => {
|
|
82
|
+
res.json({ message: 'Hello world' });
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Protected route - requires valid JWT
|
|
86
|
+
app.get('/profile', requireAuth(), (req, res) => {
|
|
87
|
+
// req.auth contains verified token info
|
|
88
|
+
res.json({ user: req.auth?.claims });
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Protected route - requires specific permission
|
|
92
|
+
app.delete('/users/:id',
|
|
93
|
+
requireAuth(),
|
|
94
|
+
requirePermission('users:delete'),
|
|
95
|
+
(req, res) => {
|
|
96
|
+
res.json({ message: 'User deleted' });
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
app.listen(3000);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The middleware looks for a Bearer token in the `Authorization` header:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Middleware Reference
|
|
110
|
+
|
|
111
|
+
### requireAuth(options?)
|
|
112
|
+
|
|
113
|
+
Requires a valid JWT token. Adds `req.auth` with verified token info.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
app.get('/protected', requireAuth(), (req, res) => {
|
|
117
|
+
const userId = req.auth?.claims.sub;
|
|
118
|
+
res.json({ userId });
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Options:**
|
|
123
|
+
| Option | Type | Description |
|
|
124
|
+
|--------|------|-------------|
|
|
125
|
+
| `getToken` | `(req) => string` | Custom token extractor |
|
|
126
|
+
|
|
127
|
+
### requirePermission(permission, options?)
|
|
128
|
+
|
|
129
|
+
Requires the user to have a specific permission.
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
app.post('/admin/users',
|
|
133
|
+
requireAuth(),
|
|
134
|
+
requirePermission('users:create'),
|
|
135
|
+
(req, res) => { ... }
|
|
136
|
+
);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Options:**
|
|
140
|
+
| Option | Type | Default | Description |
|
|
141
|
+
|--------|------|---------|-------------|
|
|
142
|
+
| `message` | `string` | "Insufficient permissions" | Custom error message |
|
|
143
|
+
|
|
144
|
+
### requireAnyPermission(permissions, options?)
|
|
145
|
+
|
|
146
|
+
Requires the user to have at least one of the specified permissions.
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
app.get('/reports',
|
|
150
|
+
requireAuth(),
|
|
151
|
+
requireAnyPermission(['reports:read', 'reports:admin']),
|
|
152
|
+
(req, res) => { ... }
|
|
153
|
+
);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### requireAllPermissions(permissions, options?)
|
|
157
|
+
|
|
158
|
+
Requires the user to have all of the specified permissions.
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
app.post('/admin/settings',
|
|
162
|
+
requireAuth(),
|
|
163
|
+
requireAllPermissions(['admin:access', 'settings:write']),
|
|
164
|
+
(req, res) => { ... }
|
|
165
|
+
);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### requirePlatformOwner(options?)
|
|
169
|
+
|
|
170
|
+
Requires the user to be a platform owner.
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
app.get('/platform/settings',
|
|
174
|
+
requireAuth(),
|
|
175
|
+
requirePlatformOwner(),
|
|
176
|
+
(req, res) => { ... }
|
|
177
|
+
);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### requireOrganization(slug, options?)
|
|
181
|
+
|
|
182
|
+
Requires the user to belong to a specific organization.
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
app.get('/org/:slug/data',
|
|
186
|
+
requireAuth(),
|
|
187
|
+
requireOrganization((req) => req.params.slug),
|
|
188
|
+
(req, res) => { ... }
|
|
189
|
+
);
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Webhook Verification
|
|
193
|
+
|
|
194
|
+
Verify webhooks from AuthOS:
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
import { verifyWebhookSignature } from '@drmhse/authos-node';
|
|
198
|
+
|
|
199
|
+
app.post('/webhooks/authos', (req, res) => {
|
|
200
|
+
const signature = req.headers['x-authos-signature'];
|
|
201
|
+
const payload = JSON.stringify(req.body);
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
const isValid = verifyWebhookSignature(payload, signature, {
|
|
205
|
+
secret: process.env.WEBHOOK_SECRET!
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
if (!isValid) {
|
|
209
|
+
return res.status(401).json({ error: 'Invalid signature' });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Process webhook
|
|
213
|
+
res.json({ received: true });
|
|
214
|
+
} catch (err) {
|
|
215
|
+
res.status(400).json({ error: 'Webhook verification failed' });
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Creating Webhook Signatures
|
|
221
|
+
|
|
222
|
+
If you need to verify webhooks from your own services:
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
import { createWebhookSignature } from '@drmhse/authos-node';
|
|
226
|
+
|
|
227
|
+
const payload = JSON.stringify({ event: 'user.created' });
|
|
228
|
+
const signature = createWebhookSignature(payload, 'your_secret');
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## API Reference
|
|
232
|
+
|
|
233
|
+
### createTokenVerifier(options)
|
|
234
|
+
|
|
235
|
+
Creates a JWT token verifier that fetches JWKS from AuthOS.
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
import { createTokenVerifier, clearJWKSCache } from '@drmhse/authos-node';
|
|
239
|
+
|
|
240
|
+
const verifier = createTokenVerifier({
|
|
241
|
+
baseURL: 'https://sso.example.com',
|
|
242
|
+
// Optional: cache time in seconds
|
|
243
|
+
cacheTimeSeconds: 300
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const verified = await verifier.verifyToken(token);
|
|
247
|
+
|
|
248
|
+
// Clear cache to force JWKS refresh
|
|
249
|
+
clearJWKSCache();
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Returns:**
|
|
253
|
+
- `verifyToken(token)` - Verifies a JWT and returns claims
|
|
254
|
+
- Claims include: `sub`, `email`, `is_platform_owner`, `org`, `permissions`
|
|
255
|
+
|
|
256
|
+
## Error Codes
|
|
257
|
+
|
|
258
|
+
| Code | Description |
|
|
259
|
+
|------|-------------|
|
|
260
|
+
| `MISSING_TOKEN` | No Bearer token provided |
|
|
261
|
+
| `INVALID_TOKEN` | Token is malformed or expired |
|
|
262
|
+
| `NOT_AUTHENTICATED` | No auth info on request |
|
|
263
|
+
| `PERMISSION_DENIED` | User lacks required permission |
|
|
264
|
+
| `NOT_PLATFORM_OWNER` | User is not a platform owner |
|
|
265
|
+
| `WRONG_ORGANIZATION` | User is not in required organization |
|
|
266
|
+
|
|
267
|
+
## License
|
|
268
|
+
|
|
269
|
+
MIT © DRM HSE
|
package/package.json
CHANGED