@oathmesh/sdk 1.0.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 +238 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.d.ts +39 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +58 -0
- package/dist/middleware.js.map +1 -0
- package/dist/next.d.ts +117 -0
- package/dist/next.d.ts.map +1 -0
- package/dist/next.js +183 -0
- package/dist/next.js.map +1 -0
- package/dist/types.d.ts +174 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +98 -0
- package/dist/types.js.map +1 -0
- package/dist/verify.d.ts +31 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +176 -0
- package/dist/verify.js.map +1 -0
- package/package.json +81 -0
package/README.md
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# @oathmesh/sdk
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="../../assets/logo.png" width="80" alt="OathMesh Logo">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<b>OathMesh token verification for Node.js</b> — TypeScript-first, Express & Next.js.
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/@oathmesh/oathmesh">
|
|
13
|
+
<img src="https://img.shields.io/npm/v/@oathmesh/oathmesh.svg" alt="npm version">
|
|
14
|
+
</a>
|
|
15
|
+
<a href="https://www.npmjs.com/package/@oathmesh/oathmesh">
|
|
16
|
+
<img src="https://img.shields.io/npm/dm/@oathmesh/oathmesh" alt="npm downloads">
|
|
17
|
+
</a>
|
|
18
|
+
<a href="https://github.com/oathmesh/oathmesh/actions/workflows/ci.yml">
|
|
19
|
+
<img src="https://github.com/oathmesh/oathmesh/actions/workflows/ci.yml/badge.svg" alt="CI Status">
|
|
20
|
+
</a>
|
|
21
|
+
<a href="https://github.com/oathmesh/oathmesh/blob/main/LICENSE">
|
|
22
|
+
<img src="https://img.shields.io/github/license/oathmesh/oathmesh" alt="License">
|
|
23
|
+
</a>
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install @oathmesh/oathmesh
|
|
32
|
+
# or
|
|
33
|
+
yarn add @oathmesh/oathmesh
|
|
34
|
+
# or
|
|
35
|
+
pnpm add @oathmesh/oathmesh
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Requirements
|
|
39
|
+
|
|
40
|
+
- Node.js 18+
|
|
41
|
+
- Express 4+ (optional, middleware works with any framework)
|
|
42
|
+
- Next.js 13+ (optional, for Next.js integrations)
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { verifyToken } from '@oathmesh/oathmesh';
|
|
50
|
+
|
|
51
|
+
app.use(verifyToken({
|
|
52
|
+
audience: 'https://inventory.internal',
|
|
53
|
+
trustedIssuers: ['https://issuer.oathmesh.dev'],
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
app.get('/inventory', (req, res) => {
|
|
57
|
+
const caller = req.oathmeshContext!;
|
|
58
|
+
res.json({ subject: caller.principal.subject });
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Framework Support
|
|
65
|
+
|
|
66
|
+
| Framework | Integration | Export |
|
|
67
|
+
|-----------|-------------|--------|
|
|
68
|
+
| **Express** | Middleware | `@oathmesh/oathmesh` |
|
|
69
|
+
| **Next.js App** | Route handler | `@oathmesh/oathmesh/next` |
|
|
70
|
+
| **Next.js Pages** | API wrapper | `@oathmesh/oathmesh/next` |
|
|
71
|
+
| **Next.js Edge** | Edge middleware | `@oathmesh/oathmesh/next` |
|
|
72
|
+
| **Any** | Core verifier | `@oathmesh/oathmesh` |
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Express.js
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import express from 'express';
|
|
80
|
+
import { verifyToken } from '@oathmesh/oathmesh';
|
|
81
|
+
|
|
82
|
+
const app = express();
|
|
83
|
+
|
|
84
|
+
app.use(verifyToken({
|
|
85
|
+
audience: 'https://inventory.internal',
|
|
86
|
+
trustedIssuers: ['https://issuer.oathmesh.dev'],
|
|
87
|
+
}));
|
|
88
|
+
|
|
89
|
+
app.get('/inventory', (req, res) => {
|
|
90
|
+
const caller = req.oathmeshContext!;
|
|
91
|
+
res.json({ subject: caller.principal.subject, action: caller.action });
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Next.js — App Router (Route Handlers)
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// app/api/inventory/route.ts
|
|
101
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
102
|
+
import { withOathMesh } from '@oathmesh/oathmesh/next';
|
|
103
|
+
|
|
104
|
+
const oathmesh = withOathMesh({
|
|
105
|
+
audience: 'https://inventory.internal',
|
|
106
|
+
trustedIssuers: ['https://issuer.oathmesh.dev'],
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
export async function GET(request: NextRequest) {
|
|
110
|
+
const { caller, error } = await oathmesh(request);
|
|
111
|
+
if (error) return error; // 401 with structured error body
|
|
112
|
+
|
|
113
|
+
return NextResponse.json({
|
|
114
|
+
subject: caller.principal.subject,
|
|
115
|
+
action: caller.action,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Next.js — Pages Router (API Routes)
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// pages/api/inventory.ts
|
|
124
|
+
import { withOathMeshApi } from '@oathmesh/oathmesh/next';
|
|
125
|
+
|
|
126
|
+
export default withOathMeshApi(
|
|
127
|
+
{
|
|
128
|
+
audience: 'https://inventory.internal',
|
|
129
|
+
trustedIssuers: ['https://issuer.oathmesh.dev'],
|
|
130
|
+
},
|
|
131
|
+
(req, res) => {
|
|
132
|
+
const caller = (req as any).oathmeshContext;
|
|
133
|
+
res.json({ subject: caller.principal.subject });
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Next.js — Edge Middleware
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// middleware.ts (project root)
|
|
142
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
143
|
+
import { createEdgeVerifier } from '@oathmesh/oathmesh/next';
|
|
144
|
+
|
|
145
|
+
const verify = createEdgeVerifier({
|
|
146
|
+
audience: 'https://inventory.internal',
|
|
147
|
+
trustedIssuers: ['https://issuer.oathmesh.dev'],
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
export async function middleware(request: NextRequest) {
|
|
151
|
+
const denied = await verify(request);
|
|
152
|
+
if (denied) return denied; // 401 — stops the request
|
|
153
|
+
|
|
154
|
+
return NextResponse.next(); // Continue to the route handler
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export const config = {
|
|
158
|
+
matcher: '/api/:path*',
|
|
159
|
+
};
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Core Verifier (Framework-agnostic)
|
|
165
|
+
|
|
166
|
+
Use `verifyOathToken` directly in any runtime — Hono, Fastify, or raw Node:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
import { verifyOathToken, extractToken, OathMeshError } from '@oathmesh/oathmesh';
|
|
170
|
+
|
|
171
|
+
const token = extractToken(headers.authorization);
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
const caller = await verifyOathToken(token!, {
|
|
175
|
+
audience: 'https://inventory.internal',
|
|
176
|
+
trustedIssuers: ['https://issuer.oathmesh.dev'],
|
|
177
|
+
});
|
|
178
|
+
console.log(caller.principal.subject);
|
|
179
|
+
} catch (err) {
|
|
180
|
+
if (err instanceof OathMeshError) {
|
|
181
|
+
console.error(err.code, err.fix);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Lifecycle Hooks
|
|
189
|
+
|
|
190
|
+
Monitor verification events with `onVerified` and `onDenied`:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
verifyToken({
|
|
194
|
+
audience: 'https://inventory.internal',
|
|
195
|
+
trustedIssuers: ['https://issuer.oathmesh.dev'],
|
|
196
|
+
onVerified: (caller) => {
|
|
197
|
+
metrics.increment('oathmesh.allow', { sub: caller.principal.subject });
|
|
198
|
+
},
|
|
199
|
+
onDenied: (err) => {
|
|
200
|
+
logger.warn('oathmesh denied', { code: err.code, message: err.message });
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Error Responses
|
|
208
|
+
|
|
209
|
+
All verification failures return HTTP 401 with a stable JSON shape:
|
|
210
|
+
|
|
211
|
+
```json
|
|
212
|
+
{
|
|
213
|
+
"error": "audience_mismatch",
|
|
214
|
+
"message": "token audience does not match",
|
|
215
|
+
"fix": "mint with --aud https://inventory.internal"
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
| Code | Trigger |
|
|
220
|
+
|---|---|
|
|
221
|
+
| `claim_missing:token` | Missing/invalid Authorization header |
|
|
222
|
+
| `algorithm_not_allowed` | `typ` ≠ `om+jwt` or `alg` = `none`/unsupported |
|
|
223
|
+
| `issuer_untrusted` | Issuer not in trusted list |
|
|
224
|
+
| `signature_invalid` | JWKS signature verification failed |
|
|
225
|
+
| `token_expired` | Past expiry + 10s clock skew |
|
|
226
|
+
| `audience_mismatch` | `aud` doesn't match configured audience |
|
|
227
|
+
| `claim_missing:{sub,act,jti}` | Missing required claim |
|
|
228
|
+
| `verification_failed` | Catch-all for malformed tokens |
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Development
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
npm install
|
|
236
|
+
npm test # vitest — 12 tests
|
|
237
|
+
npm run build # tsc → dist/
|
|
238
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @oathmesh/sdk — OathMesh verification SDK for Node.js / TypeScript
|
|
3
|
+
*
|
|
4
|
+
* Entry points:
|
|
5
|
+
* - `@oathmesh/sdk` → Express middleware + core verifier
|
|
6
|
+
* - `@oathmesh/sdk/next` → Next.js App Router, Pages Router, Edge Middleware
|
|
7
|
+
*
|
|
8
|
+
* @example Express
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { verifyToken } from '@oathmesh/sdk';
|
|
11
|
+
*
|
|
12
|
+
* app.use(verifyToken({
|
|
13
|
+
* audience: 'https://inventory.internal',
|
|
14
|
+
* trustedIssuers: ['https://issuer.oathmesh.dev'],
|
|
15
|
+
* }));
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @example Next.js App Router
|
|
19
|
+
* ```typescript
|
|
20
|
+
* import { withOathMesh } from '@oathmesh/sdk/next';
|
|
21
|
+
*
|
|
22
|
+
* const oathmesh = withOathMesh({
|
|
23
|
+
* audience: 'https://inventory.internal',
|
|
24
|
+
* trustedIssuers: ['https://issuer.oathmesh.dev'],
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* export async function GET(request: NextRequest) {
|
|
28
|
+
* const { caller, error } = await oathmesh(request);
|
|
29
|
+
* if (error) return error;
|
|
30
|
+
* return NextResponse.json({ subject: caller.principal.subject });
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export { verifyToken } from './middleware';
|
|
35
|
+
export { verifyOathToken, extractToken } from './verify';
|
|
36
|
+
export { OathMeshError, type VerifiedCallerContext, type VerifierConfig, type Principal, type Source, type ErrorCode, type OathMeshErrorBody, } from './types';
|
|
37
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGzD,OAAO,EACL,aAAa,EACb,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,MAAM,EACX,KAAK,SAAS,EACd,KAAK,iBAAiB,GACvB,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @oathmesh/sdk — OathMesh verification SDK for Node.js / TypeScript
|
|
4
|
+
*
|
|
5
|
+
* Entry points:
|
|
6
|
+
* - `@oathmesh/sdk` → Express middleware + core verifier
|
|
7
|
+
* - `@oathmesh/sdk/next` → Next.js App Router, Pages Router, Edge Middleware
|
|
8
|
+
*
|
|
9
|
+
* @example Express
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { verifyToken } from '@oathmesh/sdk';
|
|
12
|
+
*
|
|
13
|
+
* app.use(verifyToken({
|
|
14
|
+
* audience: 'https://inventory.internal',
|
|
15
|
+
* trustedIssuers: ['https://issuer.oathmesh.dev'],
|
|
16
|
+
* }));
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @example Next.js App Router
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { withOathMesh } from '@oathmesh/sdk/next';
|
|
22
|
+
*
|
|
23
|
+
* const oathmesh = withOathMesh({
|
|
24
|
+
* audience: 'https://inventory.internal',
|
|
25
|
+
* trustedIssuers: ['https://issuer.oathmesh.dev'],
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* export async function GET(request: NextRequest) {
|
|
29
|
+
* const { caller, error } = await oathmesh(request);
|
|
30
|
+
* if (error) return error;
|
|
31
|
+
* return NextResponse.json({ subject: caller.principal.subject });
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.OathMeshError = exports.extractToken = exports.verifyOathToken = exports.verifyToken = void 0;
|
|
37
|
+
// Express middleware
|
|
38
|
+
var middleware_1 = require("./middleware");
|
|
39
|
+
Object.defineProperty(exports, "verifyToken", { enumerable: true, get: function () { return middleware_1.verifyToken; } });
|
|
40
|
+
// Core verifier (framework-agnostic)
|
|
41
|
+
var verify_1 = require("./verify");
|
|
42
|
+
Object.defineProperty(exports, "verifyOathToken", { enumerable: true, get: function () { return verify_1.verifyOathToken; } });
|
|
43
|
+
Object.defineProperty(exports, "extractToken", { enumerable: true, get: function () { return verify_1.extractToken; } });
|
|
44
|
+
// Types
|
|
45
|
+
var types_1 = require("./types");
|
|
46
|
+
Object.defineProperty(exports, "OathMeshError", { enumerable: true, get: function () { return types_1.OathMeshError; } });
|
|
47
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;;;AAEH,qBAAqB;AACrB,2CAA2C;AAAlC,yGAAA,WAAW,OAAA;AAEpB,qCAAqC;AACrC,mCAAyD;AAAhD,yGAAA,eAAe,OAAA;AAAE,sGAAA,YAAY,OAAA;AAEtC,QAAQ;AACR,iCAQiB;AAPf,sGAAA,aAAa,OAAA"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OathMesh Express middleware adapter.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the core verifier for Express.js / Connect-compatible frameworks.
|
|
5
|
+
*/
|
|
6
|
+
import type { Request, Response, NextFunction } from 'express';
|
|
7
|
+
import { type VerifierConfig, type VerifiedCallerContext } from './types';
|
|
8
|
+
declare global {
|
|
9
|
+
namespace Express {
|
|
10
|
+
interface Request {
|
|
11
|
+
oathmeshContext?: VerifiedCallerContext;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Creates an Express middleware that verifies OathMesh tokens.
|
|
17
|
+
*
|
|
18
|
+
* On success, populates `req.oathmeshContext` with the verified caller identity.
|
|
19
|
+
* On failure, responds with 401 and a structured error body.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* import express from 'express';
|
|
24
|
+
* import { verifyToken } from '@oathmesh/sdk';
|
|
25
|
+
*
|
|
26
|
+
* const app = express();
|
|
27
|
+
* app.use(verifyToken({
|
|
28
|
+
* audience: 'https://inventory.internal',
|
|
29
|
+
* trustedIssuers: ['https://issuer.oathmesh.dev'],
|
|
30
|
+
* }));
|
|
31
|
+
*
|
|
32
|
+
* app.get('/inventory', (req, res) => {
|
|
33
|
+
* const caller = req.oathmeshContext!;
|
|
34
|
+
* res.json({ subject: caller.principal.subject });
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare function verifyToken(config: VerifierConfig): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
39
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAiB,KAAK,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAIzF,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,OAAO,CAAC;QAChB,UAAU,OAAO;YACf,eAAe,CAAC,EAAE,qBAAqB,CAAC;SACzC;KACF;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,cAAc,IAClC,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC,CA2B9E"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OathMesh Express middleware adapter.
|
|
4
|
+
*
|
|
5
|
+
* Wraps the core verifier for Express.js / Connect-compatible frameworks.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.verifyToken = verifyToken;
|
|
9
|
+
const types_1 = require("./types");
|
|
10
|
+
const verify_1 = require("./verify");
|
|
11
|
+
/**
|
|
12
|
+
* Creates an Express middleware that verifies OathMesh tokens.
|
|
13
|
+
*
|
|
14
|
+
* On success, populates `req.oathmeshContext` with the verified caller identity.
|
|
15
|
+
* On failure, responds with 401 and a structured error body.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import express from 'express';
|
|
20
|
+
* import { verifyToken } from '@oathmesh/sdk';
|
|
21
|
+
*
|
|
22
|
+
* const app = express();
|
|
23
|
+
* app.use(verifyToken({
|
|
24
|
+
* audience: 'https://inventory.internal',
|
|
25
|
+
* trustedIssuers: ['https://issuer.oathmesh.dev'],
|
|
26
|
+
* }));
|
|
27
|
+
*
|
|
28
|
+
* app.get('/inventory', (req, res) => {
|
|
29
|
+
* const caller = req.oathmeshContext!;
|
|
30
|
+
* res.json({ subject: caller.principal.subject });
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
function verifyToken(config) {
|
|
35
|
+
return async (req, res, next) => {
|
|
36
|
+
const token = (0, verify_1.extractToken)(req.headers.authorization);
|
|
37
|
+
if (!token) {
|
|
38
|
+
const err = new types_1.OathMeshError('claim_missing:token', 'missing or invalid Authorization header', "provide a token in the format 'Authorization: OathMesh <token>'");
|
|
39
|
+
await config.onDenied?.(err, req.headers);
|
|
40
|
+
res.status(401).json(err.toJSON());
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const context = await (0, verify_1.verifyOathToken)(token, config);
|
|
45
|
+
req.oathmeshContext = context;
|
|
46
|
+
await config.onVerified?.(context, req.headers);
|
|
47
|
+
next();
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
const oathErr = err instanceof types_1.OathMeshError
|
|
51
|
+
? err
|
|
52
|
+
: new types_1.OathMeshError('verification_failed', err.message, 'check token format');
|
|
53
|
+
await config.onDenied?.(oathErr, req.headers);
|
|
54
|
+
res.status(401).json(oathErr.toJSON());
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AAsCH,kCA4BC;AA/DD,mCAAyF;AACzF,qCAAyD;AAWzD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,SAAgB,WAAW,CAAC,MAAsB;IAChD,OAAO,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAiB,EAAE;QAC9E,MAAM,KAAK,GAAG,IAAA,qBAAY,EAAC,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAEtD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,GAAG,GAAG,IAAI,qBAAa,CAC3B,qBAAqB,EACrB,yCAAyC,EACzC,iEAAiE,CAClE,CAAC;YACF,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,OAA6C,CAAC,CAAC;YAChF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YACnC,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAA,wBAAe,EAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YACrD,GAAG,CAAC,eAAe,GAAG,OAAO,CAAC;YAC9B,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,OAA6C,CAAC,CAAC;YACtF,IAAI,EAAE,CAAC;QACT,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,qBAAa;gBAC1C,CAAC,CAAC,GAAG;gBACL,CAAC,CAAC,IAAI,qBAAa,CAAC,qBAAqB,EAAG,GAAa,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;YAC3F,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,OAA6C,CAAC,CAAC;YACpF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/next.d.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OathMesh Next.js adapters.
|
|
3
|
+
*
|
|
4
|
+
* Provides verification for:
|
|
5
|
+
* - App Router Route Handlers (GET, POST, etc.)
|
|
6
|
+
* - Pages Router API Routes
|
|
7
|
+
* - Next.js Edge Middleware
|
|
8
|
+
*
|
|
9
|
+
* All adapters use the same core verifier — no framework-specific crypto.
|
|
10
|
+
*/
|
|
11
|
+
import { type VerifierConfig, type VerifiedCallerContext } from './types';
|
|
12
|
+
/**
|
|
13
|
+
* Verify an OathMesh token inside a Next.js App Router Route Handler.
|
|
14
|
+
*
|
|
15
|
+
* Returns the verified caller context or throws an OathMeshError.
|
|
16
|
+
* Use with `NextRequest` in route handlers.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* // app/api/inventory/route.ts
|
|
21
|
+
* import { NextRequest, NextResponse } from 'next/server';
|
|
22
|
+
* import { withOathMesh } from '@oathmesh/sdk/next';
|
|
23
|
+
*
|
|
24
|
+
* const oathmesh = withOathMesh({
|
|
25
|
+
* audience: 'https://inventory.internal',
|
|
26
|
+
* trustedIssuers: ['https://issuer.oathmesh.dev'],
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* export async function GET(request: NextRequest) {
|
|
30
|
+
* const { caller, error } = await oathmesh(request);
|
|
31
|
+
* if (error) return error;
|
|
32
|
+
*
|
|
33
|
+
* return NextResponse.json({
|
|
34
|
+
* subject: caller.principal.subject,
|
|
35
|
+
* action: caller.action,
|
|
36
|
+
* });
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function withOathMesh(config: VerifierConfig): (request: Request) => Promise<{
|
|
41
|
+
caller: VerifiedCallerContext;
|
|
42
|
+
error: null;
|
|
43
|
+
} | {
|
|
44
|
+
caller: null;
|
|
45
|
+
error: Response;
|
|
46
|
+
}>;
|
|
47
|
+
/** Minimal Next.js Pages Router request type (avoids hard next dependency). */
|
|
48
|
+
interface NextApiRequest {
|
|
49
|
+
headers: Record<string, string | string[] | undefined>;
|
|
50
|
+
body?: unknown;
|
|
51
|
+
query?: Record<string, string | string[] | undefined>;
|
|
52
|
+
method?: string;
|
|
53
|
+
}
|
|
54
|
+
/** Minimal Next.js Pages Router response type. */
|
|
55
|
+
interface NextApiResponse {
|
|
56
|
+
status(code: number): NextApiResponse;
|
|
57
|
+
json(body: unknown): void;
|
|
58
|
+
}
|
|
59
|
+
/** Next.js Pages Router API handler type. */
|
|
60
|
+
type NextApiHandler = (req: NextApiRequest, res: NextApiResponse) => void | Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Wrap a Next.js Pages Router API handler with OathMesh verification.
|
|
63
|
+
*
|
|
64
|
+
* The verified caller context is injected into `req.oathmeshContext`.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* // pages/api/inventory.ts
|
|
69
|
+
* import { withOathMeshApi } from '@oathmesh/sdk/next';
|
|
70
|
+
*
|
|
71
|
+
* export default withOathMeshApi(
|
|
72
|
+
* {
|
|
73
|
+
* audience: 'https://inventory.internal',
|
|
74
|
+
* trustedIssuers: ['https://issuer.oathmesh.dev'],
|
|
75
|
+
* },
|
|
76
|
+
* (req, res) => {
|
|
77
|
+
* const caller = (req as any).oathmeshContext;
|
|
78
|
+
* res.json({ subject: caller.principal.subject });
|
|
79
|
+
* }
|
|
80
|
+
* );
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export declare function withOathMeshApi(config: VerifierConfig, handler: NextApiHandler): NextApiHandler;
|
|
84
|
+
/**
|
|
85
|
+
* Create a Next.js Edge Middleware verifier.
|
|
86
|
+
*
|
|
87
|
+
* Returns a function you call inside your `middleware.ts`. If verification
|
|
88
|
+
* fails it returns a `Response` you should return immediately. If it passes,
|
|
89
|
+
* it returns `null` and you should call `NextResponse.next()`.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* // middleware.ts (project root)
|
|
94
|
+
* import { NextRequest, NextResponse } from 'next/server';
|
|
95
|
+
* import { createEdgeVerifier } from '@oathmesh/sdk/next';
|
|
96
|
+
*
|
|
97
|
+
* const verify = createEdgeVerifier({
|
|
98
|
+
* audience: 'https://inventory.internal',
|
|
99
|
+
* trustedIssuers: ['https://issuer.oathmesh.dev'],
|
|
100
|
+
* });
|
|
101
|
+
*
|
|
102
|
+
* export async function middleware(request: NextRequest) {
|
|
103
|
+
* const denied = await verify(request);
|
|
104
|
+
* if (denied) return denied;
|
|
105
|
+
*
|
|
106
|
+
* // Verification passed — forward with injected headers
|
|
107
|
+
* return NextResponse.next();
|
|
108
|
+
* }
|
|
109
|
+
*
|
|
110
|
+
* export const config = {
|
|
111
|
+
* matcher: '/api/:path*',
|
|
112
|
+
* };
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
export declare function createEdgeVerifier(config: VerifierConfig): (request: Request) => Promise<Response | null>;
|
|
116
|
+
export {};
|
|
117
|
+
//# sourceMappingURL=next.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../src/next.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAiB,KAAK,cAAc,EAAE,KAAK,qBAAqB,EAA0B,MAAM,SAAS,CAAC;AAKjH;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,IAE/C,SAAS,OAAO,KACf,OAAO,CACN;IAAE,MAAM,EAAE,qBAAqB,CAAC;IAAC,KAAK,EAAE,IAAI,CAAA;CAAE,GAC9C;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,CACpC,CAgCF;AAID,+EAA+E;AAC/E,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,kDAAkD;AAClD,UAAU,eAAe;IACvB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAAC;IACtC,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;CAC3B;AAED,6CAA6C;AAC7C,KAAK,cAAc,GAAG,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1F;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,cAAc,GACtB,cAAc,CAgChB;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,IACzC,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA4B1D"}
|