@tekcify/auth-backend 2.0.3 → 2.1.0
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 +147 -12
- package/dist/application-management.js +2 -2
- package/dist/express/index.d.ts +2 -0
- package/dist/express/index.d.ts.map +1 -1
- package/dist/express/index.js +3 -1
- package/dist/express/jwks-middleware.d.ts +17 -0
- package/dist/express/jwks-middleware.d.ts.map +1 -0
- package/dist/express/jwks-middleware.js +44 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/jwks-verifier.d.ts +19 -0
- package/dist/jwks-verifier.d.ts.map +1 -0
- package/dist/jwks-verifier.js +72 -0
- package/dist/nestjs/guard.d.ts.map +1 -1
- package/dist/nestjs/guard.js +44 -83
- package/dist/nestjs/index.d.ts +2 -0
- package/dist/nestjs/index.d.ts.map +1 -1
- package/dist/nestjs/index.js +3 -1
- package/dist/nestjs/jwks-guard.d.ts +14 -0
- package/dist/nestjs/jwks-guard.d.ts.map +1 -0
- package/dist/nestjs/jwks-guard.js +66 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/user-profile.d.ts.map +1 -1
- package/dist/user-profile.js +13 -19
- package/dist/userinfo.d.ts +20 -1
- package/dist/userinfo.d.ts.map +1 -1
- package/dist/userinfo.js +31 -5
- package/dist/verify.d.ts +1 -1
- package/dist/verify.js +11 -41
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -16,7 +16,9 @@ yarn add @tekcify/auth-backend
|
|
|
16
16
|
|
|
17
17
|
- ✅ **NestJS Support** - Guards and decorators for NestJS applications
|
|
18
18
|
- ✅ **Express Support** - Middleware for Express applications
|
|
19
|
-
- ✅ **Token Verification** - JWT token validation with
|
|
19
|
+
- ✅ **Token Verification** - JWT token validation with RS256/JWKS (recommended) or HS256
|
|
20
|
+
- ✅ **JWKS Support** - Automatic public key fetching and caching
|
|
21
|
+
- ✅ **Auto Key Rotation** - Works seamlessly with rotating keys
|
|
20
22
|
- ✅ **User Info Fetching** - Helper to get user information from auth server
|
|
21
23
|
- ✅ **TypeScript Support** - Full type definitions included
|
|
22
24
|
|
|
@@ -24,7 +26,17 @@ yarn add @tekcify/auth-backend
|
|
|
24
26
|
|
|
25
27
|
### Prerequisites
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
**Option 1: JWKS (Recommended - More Secure)**
|
|
30
|
+
|
|
31
|
+
No secrets needed! Just the auth server URL:
|
|
32
|
+
|
|
33
|
+
```env
|
|
34
|
+
AUTH_SERVER_URL=http://localhost:7001
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Option 2: Shared Secret (Legacy)**
|
|
38
|
+
|
|
39
|
+
You need the JWT access secret from your Tekcify Auth server:
|
|
28
40
|
|
|
29
41
|
```env
|
|
30
42
|
JWT_ACCESS_SECRET=your-jwt-access-secret-here
|
|
@@ -32,6 +44,36 @@ JWT_ACCESS_SECRET=your-jwt-access-secret-here
|
|
|
32
44
|
|
|
33
45
|
**Note:** The auth server URL is centralized in `@tekcify/auth-core-client` package as `AUTH_SERVER_URL` (default: `http://localhost:7001`, override with `AUTH_SERVER_URL` env). Functions that communicate with the auth server use this constant automatically.
|
|
34
46
|
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 🔐 JWKS vs Shared Secret
|
|
50
|
+
|
|
51
|
+
### JWKS (RS256) - Recommended ✅
|
|
52
|
+
|
|
53
|
+
**Benefits:**
|
|
54
|
+
- No shared secrets to distribute
|
|
55
|
+
- Private key never leaves auth server
|
|
56
|
+
- Automatic key rotation support
|
|
57
|
+
- More secure for distributed systems
|
|
58
|
+
|
|
59
|
+
**Use when:**
|
|
60
|
+
- You have multiple products
|
|
61
|
+
- Security is a top priority
|
|
62
|
+
- You want automatic key rotation
|
|
63
|
+
|
|
64
|
+
### Shared Secret (HS256) - Legacy
|
|
65
|
+
|
|
66
|
+
**Benefits:**
|
|
67
|
+
- Simpler setup
|
|
68
|
+
- No network calls for verification
|
|
69
|
+
|
|
70
|
+
**Use when:**
|
|
71
|
+
- Single product setup
|
|
72
|
+
- Quick prototyping
|
|
73
|
+
- Migrating from old system
|
|
74
|
+
|
|
75
|
+
**⚠️ Migration:** If you're migrating from HS256 to RS256, see [MIGRATION-RS256.md](../../MIGRATION-RS256.md)
|
|
76
|
+
|
|
35
77
|
## Auth Server Endpoints Used
|
|
36
78
|
|
|
37
79
|
- Base prefix: `/api`
|
|
@@ -40,15 +82,47 @@ JWT_ACCESS_SECRET=your-jwt-access-secret-here
|
|
|
40
82
|
|
|
41
83
|
## NestJS Integration
|
|
42
84
|
|
|
43
|
-
###
|
|
85
|
+
### Option A: JWKS (Recommended)
|
|
86
|
+
|
|
87
|
+
#### Step 1: Install Dependencies
|
|
44
88
|
|
|
45
89
|
```bash
|
|
46
90
|
pnpm add @tekcify/auth-backend @nestjs/common @nestjs/core
|
|
47
91
|
```
|
|
48
92
|
|
|
49
|
-
|
|
93
|
+
#### Step 2: Create and Configure the Guard
|
|
94
|
+
|
|
95
|
+
Create a guard provider in your module using JWKS:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { Module } from '@nestjs/common';
|
|
99
|
+
import { APP_GUARD } from '@nestjs/core';
|
|
100
|
+
import { JwksAuthGuard } from '@tekcify/auth-backend/nestjs';
|
|
101
|
+
|
|
102
|
+
@Module({
|
|
103
|
+
providers: [
|
|
104
|
+
{
|
|
105
|
+
provide: APP_GUARD,
|
|
106
|
+
useFactory: () => {
|
|
107
|
+
return new JwksAuthGuard({
|
|
108
|
+
jwksUri: `${process.env.AUTH_SERVER_URL}/api/.well-known/jwks.json`,
|
|
109
|
+
cacheTime: 3600000, // Cache keys for 1 hour
|
|
110
|
+
issuer: 'tekcify-auth',
|
|
111
|
+
audience: 'tekcify-api',
|
|
112
|
+
getUserInfo: async (userId: string) => {
|
|
113
|
+
// Optional: Fetch user info from your database
|
|
114
|
+
const user = await userRepository.findById(userId);
|
|
115
|
+
return user ? { email: user.email } : null;
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
})
|
|
122
|
+
export class AppModule {}
|
|
123
|
+
```
|
|
50
124
|
|
|
51
|
-
|
|
125
|
+
### Option B: Shared Secret (Legacy)
|
|
52
126
|
|
|
53
127
|
```typescript
|
|
54
128
|
import { Module } from '@nestjs/common';
|
|
@@ -65,8 +139,6 @@ import { JwtAuthGuard } from '@tekcify/auth-backend/nestjs';
|
|
|
65
139
|
issuer: 'tekcify-auth',
|
|
66
140
|
audience: 'tekcify-api',
|
|
67
141
|
getUserInfo: async (userId: string) => {
|
|
68
|
-
// Optional: Fetch user info from your database
|
|
69
|
-
// This is called only if getUserInfo is provided
|
|
70
142
|
const user = await userRepository.findById(userId);
|
|
71
143
|
return user ? { email: user.email } : null;
|
|
72
144
|
},
|
|
@@ -153,22 +225,25 @@ getCurrentUser(@CurrentUser() user: UserPayload) {
|
|
|
153
225
|
|
|
154
226
|
## Express Integration
|
|
155
227
|
|
|
156
|
-
###
|
|
228
|
+
### Option A: JWKS (Recommended)
|
|
229
|
+
|
|
230
|
+
#### Step 1: Install Dependencies
|
|
157
231
|
|
|
158
232
|
```bash
|
|
159
233
|
pnpm add @tekcify/auth-backend express
|
|
160
234
|
```
|
|
161
235
|
|
|
162
|
-
|
|
236
|
+
#### Step 2: Create Auth Middleware
|
|
163
237
|
|
|
164
238
|
```typescript
|
|
165
239
|
import express from 'express';
|
|
166
|
-
import {
|
|
240
|
+
import { createJwksAuthMiddleware } from '@tekcify/auth-backend/express';
|
|
167
241
|
|
|
168
242
|
const app = express();
|
|
169
243
|
|
|
170
|
-
const authMiddleware =
|
|
171
|
-
|
|
244
|
+
const authMiddleware = createJwksAuthMiddleware({
|
|
245
|
+
jwksUri: `${process.env.AUTH_SERVER_URL}/api/.well-known/jwks.json`,
|
|
246
|
+
cacheTime: 3600000, // Cache keys for 1 hour
|
|
172
247
|
issuer: 'tekcify-auth',
|
|
173
248
|
audience: 'tekcify-api',
|
|
174
249
|
getUserInfo: async (userId: string) => {
|
|
@@ -193,6 +268,36 @@ app.get('/api/profile', (req, res) => {
|
|
|
193
268
|
});
|
|
194
269
|
```
|
|
195
270
|
|
|
271
|
+
### Option B: Shared Secret (Legacy)
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
import express from 'express';
|
|
275
|
+
import { createAuthMiddleware } from '@tekcify/auth-backend/express';
|
|
276
|
+
|
|
277
|
+
const app = express();
|
|
278
|
+
|
|
279
|
+
const authMiddleware = createAuthMiddleware({
|
|
280
|
+
secret: process.env.JWT_ACCESS_SECRET!,
|
|
281
|
+
issuer: 'tekcify-auth',
|
|
282
|
+
audience: 'tekcify-api',
|
|
283
|
+
getUserInfo: async (userId: string) => {
|
|
284
|
+
const user = await userRepository.findById(userId);
|
|
285
|
+
return user ? { email: user.email } : null;
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
app.use(express.json());
|
|
290
|
+
app.use('/api', authMiddleware);
|
|
291
|
+
|
|
292
|
+
app.get('/api/profile', (req, res) => {
|
|
293
|
+
res.json({
|
|
294
|
+
userId: req.user!.userId,
|
|
295
|
+
email: req.user!.email,
|
|
296
|
+
scopes: req.user!.scopes,
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
```
|
|
300
|
+
|
|
196
301
|
### Step 3: Route-Level Middleware
|
|
197
302
|
|
|
198
303
|
You can also apply middleware to specific routes:
|
|
@@ -230,6 +335,36 @@ app.get('/api/user', authMiddleware, (req: Request, res: Response) => {
|
|
|
230
335
|
|
|
231
336
|
For cases where you need to verify tokens directly (e.g., in background jobs, WebSocket connections):
|
|
232
337
|
|
|
338
|
+
### Option A: JWKS Verification (Recommended)
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
import { createJwksVerifier } from '@tekcify/auth-backend';
|
|
342
|
+
|
|
343
|
+
const verifier = createJwksVerifier({
|
|
344
|
+
jwksUri: `${process.env.AUTH_SERVER_URL}/api/.well-known/jwks.json`,
|
|
345
|
+
cacheTime: 3600000, // Cache keys for 1 hour
|
|
346
|
+
issuer: 'tekcify-auth',
|
|
347
|
+
audience: 'tekcify-api',
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
const token = req.headers.authorization?.replace('Bearer ', '');
|
|
351
|
+
|
|
352
|
+
if (!token) {
|
|
353
|
+
throw new Error('No token provided');
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const result = await verifier.verify(token);
|
|
357
|
+
|
|
358
|
+
if (result.valid) {
|
|
359
|
+
console.log('User ID:', result.payload.sub);
|
|
360
|
+
console.log('Scopes:', result.payload.scopes);
|
|
361
|
+
} else {
|
|
362
|
+
throw new Error('Invalid token');
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Option B: Shared Secret Verification (Legacy)
|
|
367
|
+
|
|
233
368
|
```typescript
|
|
234
369
|
import { verifyAccessToken } from '@tekcify/auth-backend';
|
|
235
370
|
|
|
@@ -7,9 +7,9 @@ exports.updateApplication = updateApplication;
|
|
|
7
7
|
exports.deleteApplication = deleteApplication;
|
|
8
8
|
exports.uploadApplicationLogo = uploadApplicationLogo;
|
|
9
9
|
exports.regenerateApplicationSecret = regenerateApplicationSecret;
|
|
10
|
-
const
|
|
10
|
+
const AUTH_SERVER_URL = 'https://auth-api.tekcify.com';
|
|
11
11
|
function buildUrl(path, query) {
|
|
12
|
-
const url = new URL(`${
|
|
12
|
+
const url = new URL(`${AUTH_SERVER_URL}${path}`);
|
|
13
13
|
Object.entries(query ?? {}).forEach(([key, value]) => {
|
|
14
14
|
if (value !== undefined) {
|
|
15
15
|
url.searchParams.set(key, String(value));
|
package/dist/express/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { createAuthMiddleware } from './middleware';
|
|
2
2
|
export type { ExpressAuthOptions } from './middleware';
|
|
3
|
+
export { createJwksAuthMiddleware } from './jwks-middleware';
|
|
4
|
+
export type { ExpressJwksAuthOptions } from './jwks-middleware';
|
|
3
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/express/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/express/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,YAAY,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/express/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createAuthMiddleware = void 0;
|
|
3
|
+
exports.createJwksAuthMiddleware = exports.createAuthMiddleware = void 0;
|
|
4
4
|
var middleware_1 = require("./middleware");
|
|
5
5
|
Object.defineProperty(exports, "createAuthMiddleware", { enumerable: true, get: function () { return middleware_1.createAuthMiddleware; } });
|
|
6
|
+
var jwks_middleware_1 = require("./jwks-middleware");
|
|
7
|
+
Object.defineProperty(exports, "createJwksAuthMiddleware", { enumerable: true, get: function () { return jwks_middleware_1.createJwksAuthMiddleware; } });
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Request, Response, NextFunction } from 'express';
|
|
2
|
+
import { type JwksVerifierOptions } from '../jwks-verifier';
|
|
3
|
+
import type { UserPayload } from '../types';
|
|
4
|
+
export interface ExpressJwksAuthOptions extends JwksVerifierOptions {
|
|
5
|
+
getUserInfo?: (userId: string) => Promise<{
|
|
6
|
+
email: string;
|
|
7
|
+
} | null>;
|
|
8
|
+
}
|
|
9
|
+
declare global {
|
|
10
|
+
namespace Express {
|
|
11
|
+
interface Request {
|
|
12
|
+
user?: UserPayload;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export declare function createJwksAuthMiddleware(options: ExpressJwksAuthOptions): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
17
|
+
//# sourceMappingURL=jwks-middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwks-middleware.d.ts","sourceRoot":"","sources":["../../src/express/jwks-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,MAAM,WAAW,sBAAuB,SAAQ,mBAAmB;IACjE,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;CACrE;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,OAAO,CAAC;QAChB,UAAU,OAAO;YACf,IAAI,CAAC,EAAE,WAAW,CAAC;SACpB;KACF;CACF;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,sBAAsB,IAIpE,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,YAAY,KACjB,OAAO,CAAC,IAAI,CAAC,CA2CjB"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createJwksAuthMiddleware = createJwksAuthMiddleware;
|
|
4
|
+
const jwks_verifier_1 = require("../jwks-verifier");
|
|
5
|
+
function createJwksAuthMiddleware(options) {
|
|
6
|
+
const verifier = new jwks_verifier_1.JwksVerifier(options);
|
|
7
|
+
return async (req, res, next) => {
|
|
8
|
+
const authHeader = req.headers.authorization;
|
|
9
|
+
if (!authHeader?.startsWith('Bearer ')) {
|
|
10
|
+
res
|
|
11
|
+
.status(401)
|
|
12
|
+
.json({ message: 'Missing or invalid authorization header' });
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const token = authHeader.substring(7);
|
|
16
|
+
try {
|
|
17
|
+
const verified = await verifier.verify(token);
|
|
18
|
+
if (!verified.valid) {
|
|
19
|
+
res.status(401).json({ message: 'Invalid or expired token' });
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
let email = '';
|
|
23
|
+
if (options.getUserInfo) {
|
|
24
|
+
const userInfo = await options.getUserInfo(verified.payload.sub);
|
|
25
|
+
if (!userInfo) {
|
|
26
|
+
res.status(401).json({ message: 'User not found' });
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
email = userInfo.email;
|
|
30
|
+
}
|
|
31
|
+
req.user = {
|
|
32
|
+
userId: verified.payload.sub,
|
|
33
|
+
email,
|
|
34
|
+
scopes: Array.isArray(verified.payload.scopes)
|
|
35
|
+
? verified.payload.scopes
|
|
36
|
+
: [],
|
|
37
|
+
};
|
|
38
|
+
next();
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
res.status(500).json({ message: 'Internal server error' });
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./types"), exports);
|
|
18
18
|
__exportStar(require("./verify"), exports);
|
|
19
|
+
__exportStar(require("./jwks-verifier"), exports);
|
|
19
20
|
__exportStar(require("./userinfo"), exports);
|
|
20
21
|
__exportStar(require("./user-profile"), exports);
|
|
21
22
|
__exportStar(require("./application-management"), exports);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { VerifiedToken } from './types';
|
|
2
|
+
export interface JwksVerifierOptions {
|
|
3
|
+
authServerUrl?: string;
|
|
4
|
+
jwksUri?: string;
|
|
5
|
+
cacheTime?: number;
|
|
6
|
+
rateLimit?: boolean;
|
|
7
|
+
issuer?: string;
|
|
8
|
+
audience?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class JwksVerifier {
|
|
11
|
+
private readonly client;
|
|
12
|
+
private readonly issuer;
|
|
13
|
+
private readonly audience;
|
|
14
|
+
constructor(options: JwksVerifierOptions);
|
|
15
|
+
verify(token: string): Promise<VerifiedToken>;
|
|
16
|
+
getPublicKey(kid: string): Promise<string>;
|
|
17
|
+
}
|
|
18
|
+
export declare function createJwksVerifier(options: JwksVerifierOptions): JwksVerifier;
|
|
19
|
+
//# sourceMappingURL=jwks-verifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwks-verifier.d.ts","sourceRoot":"","sources":["../src/jwks-verifier.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAgB,aAAa,EAAE,MAAM,SAAS,CAAC;AAE3D,MAAM,WAAW,mBAAmB;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAmBD,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,OAAO,EAAE,mBAAmB;IAelC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAsC7C,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAIjD;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAE7E"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.JwksVerifier = void 0;
|
|
7
|
+
exports.createJwksVerifier = createJwksVerifier;
|
|
8
|
+
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
9
|
+
const jwks_rsa_1 = __importDefault(require("jwks-rsa"));
|
|
10
|
+
const JWKS_PATH = '/api/.well-known/jwks.json';
|
|
11
|
+
function resolveJwksUri(options) {
|
|
12
|
+
if (options.jwksUri) {
|
|
13
|
+
return options.jwksUri;
|
|
14
|
+
}
|
|
15
|
+
if (options.authServerUrl) {
|
|
16
|
+
const baseUrl = options.authServerUrl.replace(/\/$/, '');
|
|
17
|
+
return `${baseUrl}${JWKS_PATH}`;
|
|
18
|
+
}
|
|
19
|
+
throw new Error('Either authServerUrl or jwksUri must be provided in JwksVerifierOptions');
|
|
20
|
+
}
|
|
21
|
+
class JwksVerifier {
|
|
22
|
+
constructor(options) {
|
|
23
|
+
const jwksUri = resolveJwksUri(options);
|
|
24
|
+
this.client = (0, jwks_rsa_1.default)({
|
|
25
|
+
jwksUri,
|
|
26
|
+
cache: true,
|
|
27
|
+
cacheMaxAge: options.cacheTime ?? 3600000,
|
|
28
|
+
rateLimit: options.rateLimit ?? true,
|
|
29
|
+
jwksRequestsPerMinute: 10,
|
|
30
|
+
});
|
|
31
|
+
this.issuer = options.issuer ?? 'tekcify-auth';
|
|
32
|
+
this.audience = options.audience ?? 'tekcify-api';
|
|
33
|
+
}
|
|
34
|
+
async verify(token) {
|
|
35
|
+
try {
|
|
36
|
+
const decodedToken = jsonwebtoken_1.default.decode(token, { complete: true });
|
|
37
|
+
if (!decodedToken || typeof decodedToken === 'string') {
|
|
38
|
+
return { payload: {}, valid: false };
|
|
39
|
+
}
|
|
40
|
+
const kid = decodedToken.header.kid;
|
|
41
|
+
if (!kid) {
|
|
42
|
+
return { payload: {}, valid: false };
|
|
43
|
+
}
|
|
44
|
+
const key = await this.client.getSigningKey(kid);
|
|
45
|
+
const publicKey = key.getPublicKey();
|
|
46
|
+
const decoded = jsonwebtoken_1.default.verify(token, publicKey, {
|
|
47
|
+
algorithms: ['RS256'],
|
|
48
|
+
issuer: this.issuer,
|
|
49
|
+
audience: this.audience,
|
|
50
|
+
});
|
|
51
|
+
if (decoded.type !== 'access') {
|
|
52
|
+
return { payload: decoded, valid: false };
|
|
53
|
+
}
|
|
54
|
+
return { payload: decoded, valid: true };
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
if (error instanceof jsonwebtoken_1.default.JsonWebTokenError ||
|
|
58
|
+
error instanceof jsonwebtoken_1.default.TokenExpiredError) {
|
|
59
|
+
return { payload: {}, valid: false };
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async getPublicKey(kid) {
|
|
65
|
+
const key = await this.client.getSigningKey(kid);
|
|
66
|
+
return key.getPublicKey();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.JwksVerifier = JwksVerifier;
|
|
70
|
+
function createJwksVerifier(options) {
|
|
71
|
+
return new JwksVerifier(options);
|
|
72
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/nestjs/guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EACX,gBAAgB,EAEjB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,KAAK,EAAE,kBAAkB,EAAe,MAAM,UAAU,CAAC;AAEhE,MAAM,WAAW,mBAAoB,SAAQ,kBAAkB;IAC7D,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;CACrE;AAED,qBACa,YAAa,YAAW,WAAW;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,mBAAmB;IAEnD,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/nestjs/guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EACX,gBAAgB,EAEjB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,KAAK,EAAE,kBAAkB,EAAe,MAAM,UAAU,CAAC;AAEhE,MAAM,WAAW,mBAAoB,SAAQ,kBAAkB;IAC7D,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;CACrE;AAED,qBACa,YAAa,YAAW,WAAW;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,mBAAmB;IAEnD,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;CAuC/D"}
|
package/dist/nestjs/guard.js
CHANGED
|
@@ -1,94 +1,55 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
var _, done = false;
|
|
8
|
-
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
9
|
-
var context = {};
|
|
10
|
-
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
11
|
-
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
12
|
-
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
13
|
-
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
14
|
-
if (kind === "accessor") {
|
|
15
|
-
if (result === void 0) continue;
|
|
16
|
-
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
17
|
-
if (_ = accept(result.get)) descriptor.get = _;
|
|
18
|
-
if (_ = accept(result.set)) descriptor.set = _;
|
|
19
|
-
if (_ = accept(result.init)) initializers.unshift(_);
|
|
20
|
-
}
|
|
21
|
-
else if (_ = accept(result)) {
|
|
22
|
-
if (kind === "field") initializers.unshift(_);
|
|
23
|
-
else descriptor[key] = _;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
27
|
-
done = true;
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
28
7
|
};
|
|
29
|
-
var
|
|
30
|
-
|
|
31
|
-
for (var i = 0; i < initializers.length; i++) {
|
|
32
|
-
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
33
|
-
}
|
|
34
|
-
return useValue ? value : void 0;
|
|
35
|
-
};
|
|
36
|
-
var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
|
|
37
|
-
if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
|
|
38
|
-
return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
39
10
|
};
|
|
40
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
12
|
exports.JwtAuthGuard = void 0;
|
|
42
13
|
const common_1 = require("@nestjs/common");
|
|
43
14
|
const verify_1 = require("../verify");
|
|
44
|
-
let JwtAuthGuard =
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
15
|
+
let JwtAuthGuard = class JwtAuthGuard {
|
|
16
|
+
constructor(options) {
|
|
17
|
+
this.options = options;
|
|
18
|
+
}
|
|
19
|
+
async canActivate(context) {
|
|
20
|
+
const request = context
|
|
21
|
+
.switchToHttp()
|
|
22
|
+
.getRequest();
|
|
23
|
+
const authHeader = request.headers.authorization;
|
|
24
|
+
if (!authHeader?.startsWith('Bearer ')) {
|
|
25
|
+
throw new common_1.UnauthorizedException('Missing or invalid authorization header');
|
|
52
26
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (!verified.valid) {
|
|
64
|
-
throw new common_1.UnauthorizedException('Invalid or expired token');
|
|
65
|
-
}
|
|
66
|
-
let email = '';
|
|
67
|
-
if (this.options.getUserInfo) {
|
|
68
|
-
const userInfo = await this.options.getUserInfo(verified.payload.sub);
|
|
69
|
-
if (!userInfo) {
|
|
70
|
-
throw new common_1.UnauthorizedException('User not found');
|
|
71
|
-
}
|
|
72
|
-
email = userInfo.email;
|
|
27
|
+
const token = authHeader.substring(7);
|
|
28
|
+
const verified = (0, verify_1.verifyAccessToken)(token, this.options);
|
|
29
|
+
if (!verified.valid) {
|
|
30
|
+
throw new common_1.UnauthorizedException('Invalid or expired token');
|
|
31
|
+
}
|
|
32
|
+
let email = '';
|
|
33
|
+
if (this.options.getUserInfo) {
|
|
34
|
+
const userInfo = await this.options.getUserInfo(verified.payload.sub);
|
|
35
|
+
if (!userInfo) {
|
|
36
|
+
throw new common_1.UnauthorizedException('User not found');
|
|
73
37
|
}
|
|
74
|
-
|
|
75
|
-
userId: verified.payload.sub,
|
|
76
|
-
email,
|
|
77
|
-
scopes: Array.isArray(verified.payload.scopes)
|
|
78
|
-
? verified.payload.scopes
|
|
79
|
-
: [],
|
|
80
|
-
};
|
|
81
|
-
return true;
|
|
38
|
+
email = userInfo.email;
|
|
82
39
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
40
|
+
request.user = {
|
|
41
|
+
userId: verified.payload.sub,
|
|
42
|
+
email,
|
|
43
|
+
scopes: Array.isArray(verified.payload.scopes)
|
|
44
|
+
? verified.payload.scopes
|
|
45
|
+
: [],
|
|
46
|
+
role: typeof verified.payload.role === 'string' ? verified.payload.role : undefined,
|
|
47
|
+
};
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
94
51
|
exports.JwtAuthGuard = JwtAuthGuard;
|
|
52
|
+
exports.JwtAuthGuard = JwtAuthGuard = __decorate([
|
|
53
|
+
(0, common_1.Injectable)(),
|
|
54
|
+
__metadata("design:paramtypes", [Object])
|
|
55
|
+
], JwtAuthGuard);
|
package/dist/nestjs/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { JwtAuthGuard } from './guard';
|
|
2
2
|
export type { JwtAuthGuardOptions } from './guard';
|
|
3
|
+
export { JwksAuthGuard } from './jwks-guard';
|
|
4
|
+
export type { JwksAuthGuardOptions } from './jwks-guard';
|
|
3
5
|
export { CurrentUser } from './decorator';
|
|
4
6
|
export type { UserPayload } from '../types';
|
|
5
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nestjs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nestjs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/nestjs/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CurrentUser = exports.JwtAuthGuard = void 0;
|
|
3
|
+
exports.CurrentUser = exports.JwksAuthGuard = exports.JwtAuthGuard = void 0;
|
|
4
4
|
var guard_1 = require("./guard");
|
|
5
5
|
Object.defineProperty(exports, "JwtAuthGuard", { enumerable: true, get: function () { return guard_1.JwtAuthGuard; } });
|
|
6
|
+
var jwks_guard_1 = require("./jwks-guard");
|
|
7
|
+
Object.defineProperty(exports, "JwksAuthGuard", { enumerable: true, get: function () { return jwks_guard_1.JwksAuthGuard; } });
|
|
6
8
|
var decorator_1 = require("./decorator");
|
|
7
9
|
Object.defineProperty(exports, "CurrentUser", { enumerable: true, get: function () { return decorator_1.CurrentUser; } });
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { CanActivate, ExecutionContext } from '@nestjs/common';
|
|
2
|
+
import { type JwksVerifierOptions } from '../jwks-verifier';
|
|
3
|
+
export interface JwksAuthGuardOptions extends JwksVerifierOptions {
|
|
4
|
+
getUserInfo?: (userId: string) => Promise<{
|
|
5
|
+
email: string;
|
|
6
|
+
} | null>;
|
|
7
|
+
}
|
|
8
|
+
export declare class JwksAuthGuard implements CanActivate {
|
|
9
|
+
private readonly options;
|
|
10
|
+
private readonly verifier;
|
|
11
|
+
constructor(options: JwksAuthGuardOptions);
|
|
12
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=jwks-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwks-guard.d.ts","sourceRoot":"","sources":["../../src/nestjs/jwks-guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EACX,gBAAgB,EAEjB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAG1E,MAAM,WAAW,oBAAqB,SAAQ,mBAAmB;IAC/D,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;CACrE;AAED,qBACa,aAAc,YAAW,WAAW;IAGnC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;gBAEX,OAAO,EAAE,oBAAoB;IAIpD,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;CAkD/D"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.JwksAuthGuard = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const jwks_verifier_1 = require("../jwks-verifier");
|
|
15
|
+
let JwksAuthGuard = class JwksAuthGuard {
|
|
16
|
+
constructor(options) {
|
|
17
|
+
this.options = options;
|
|
18
|
+
this.verifier = new jwks_verifier_1.JwksVerifier(options);
|
|
19
|
+
}
|
|
20
|
+
async canActivate(context) {
|
|
21
|
+
const request = context
|
|
22
|
+
.switchToHttp()
|
|
23
|
+
.getRequest();
|
|
24
|
+
const authHeader = request.headers.authorization;
|
|
25
|
+
if (!authHeader?.startsWith('Bearer ')) {
|
|
26
|
+
throw new common_1.UnauthorizedException('Missing or invalid authorization header');
|
|
27
|
+
}
|
|
28
|
+
const token = authHeader.substring(7);
|
|
29
|
+
try {
|
|
30
|
+
const verified = await this.verifier.verify(token);
|
|
31
|
+
if (!verified.valid) {
|
|
32
|
+
throw new common_1.UnauthorizedException('Invalid or expired token');
|
|
33
|
+
}
|
|
34
|
+
let email = '';
|
|
35
|
+
if (this.options.getUserInfo) {
|
|
36
|
+
const userInfo = await this.options.getUserInfo(verified.payload.sub);
|
|
37
|
+
if (!userInfo) {
|
|
38
|
+
throw new common_1.UnauthorizedException('User not found');
|
|
39
|
+
}
|
|
40
|
+
email = userInfo.email;
|
|
41
|
+
}
|
|
42
|
+
request.user = {
|
|
43
|
+
userId: verified.payload.sub,
|
|
44
|
+
email,
|
|
45
|
+
scopes: Array.isArray(verified.payload.scopes)
|
|
46
|
+
? verified.payload.scopes
|
|
47
|
+
: [],
|
|
48
|
+
role: typeof verified.payload.role === 'string'
|
|
49
|
+
? verified.payload.role
|
|
50
|
+
: undefined,
|
|
51
|
+
};
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (error instanceof common_1.UnauthorizedException) {
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
throw new common_1.UnauthorizedException('Invalid or expired token');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
exports.JwksAuthGuard = JwksAuthGuard;
|
|
63
|
+
exports.JwksAuthGuard = JwksAuthGuard = __decorate([
|
|
64
|
+
(0, common_1.Injectable)(),
|
|
65
|
+
__metadata("design:paramtypes", [Object])
|
|
66
|
+
], JwksAuthGuard);
|
package/dist/types.d.ts
CHANGED
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-profile.d.ts","sourceRoot":"","sources":["../src/user-profile.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"user-profile.d.ts","sourceRoot":"","sources":["../src/user-profile.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,WAAW,CAAC,CAqBtB;AAED,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,WAAW,CAAC,CAsBtB;AAED,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,IAAI,GAAG,IAAI,EACjB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC,CA6BzB"}
|
package/dist/user-profile.js
CHANGED
|
@@ -3,9 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getUserProfile = getUserProfile;
|
|
4
4
|
exports.updateUserProfile = updateUserProfile;
|
|
5
5
|
exports.uploadProfilePicture = uploadProfilePicture;
|
|
6
|
-
const
|
|
6
|
+
const AUTH_SERVER_URL = 'https://auth-api.tekcify.com';
|
|
7
7
|
async function getUserProfile(accessToken) {
|
|
8
|
-
const response = await fetch(`${
|
|
8
|
+
const response = await fetch(`${AUTH_SERVER_URL}/api/user/profile`, {
|
|
9
9
|
method: 'GET',
|
|
10
10
|
headers: {
|
|
11
11
|
Authorization: `Bearer ${accessToken}`,
|
|
@@ -13,12 +13,10 @@ async function getUserProfile(accessToken) {
|
|
|
13
13
|
},
|
|
14
14
|
});
|
|
15
15
|
if (!response.ok) {
|
|
16
|
-
const error = await response
|
|
16
|
+
const error = (await response
|
|
17
17
|
.json()
|
|
18
|
-
.catch(() => ({ message: response.statusText }));
|
|
19
|
-
const errorMessage = error?.message &&
|
|
20
|
-
typeof error.message === 'string' &&
|
|
21
|
-
error.message.trim()
|
|
18
|
+
.catch(() => ({ message: response.statusText })));
|
|
19
|
+
const errorMessage = error?.message && typeof error.message === 'string' && error.message.trim()
|
|
22
20
|
? error.message
|
|
23
21
|
: `Failed to get user profile: ${response.statusText} (Status: ${response.status})`;
|
|
24
22
|
throw new Error(errorMessage);
|
|
@@ -26,7 +24,7 @@ async function getUserProfile(accessToken) {
|
|
|
26
24
|
return response.json();
|
|
27
25
|
}
|
|
28
26
|
async function updateUserProfile(accessToken, data) {
|
|
29
|
-
const response = await fetch(`${
|
|
27
|
+
const response = await fetch(`${AUTH_SERVER_URL}/api/user/profile`, {
|
|
30
28
|
method: 'PUT',
|
|
31
29
|
headers: {
|
|
32
30
|
Authorization: `Bearer ${accessToken}`,
|
|
@@ -35,12 +33,10 @@ async function updateUserProfile(accessToken, data) {
|
|
|
35
33
|
body: JSON.stringify(data),
|
|
36
34
|
});
|
|
37
35
|
if (!response.ok) {
|
|
38
|
-
const error = await response
|
|
36
|
+
const error = (await response
|
|
39
37
|
.json()
|
|
40
|
-
.catch(() => ({ message: response.statusText }));
|
|
41
|
-
const errorMessage = error?.message &&
|
|
42
|
-
typeof error.message === 'string' &&
|
|
43
|
-
error.message.trim()
|
|
38
|
+
.catch(() => ({ message: response.statusText })));
|
|
39
|
+
const errorMessage = error?.message && typeof error.message === 'string' && error.message.trim()
|
|
44
40
|
? error.message
|
|
45
41
|
: `Failed to update user profile: ${response.statusText} (Status: ${response.status})`;
|
|
46
42
|
throw new Error(errorMessage);
|
|
@@ -55,7 +51,7 @@ async function uploadProfilePicture(accessToken, file, fileName) {
|
|
|
55
51
|
else {
|
|
56
52
|
formData.append('file', file);
|
|
57
53
|
}
|
|
58
|
-
const response = await fetch(`${
|
|
54
|
+
const response = await fetch(`${AUTH_SERVER_URL}/api/user/profile/picture`, {
|
|
59
55
|
method: 'POST',
|
|
60
56
|
headers: {
|
|
61
57
|
Authorization: `Bearer ${accessToken}`,
|
|
@@ -63,12 +59,10 @@ async function uploadProfilePicture(accessToken, file, fileName) {
|
|
|
63
59
|
body: formData,
|
|
64
60
|
});
|
|
65
61
|
if (!response.ok) {
|
|
66
|
-
const error = await response
|
|
62
|
+
const error = (await response
|
|
67
63
|
.json()
|
|
68
|
-
.catch(() => ({ message: response.statusText }));
|
|
69
|
-
const errorMessage = error?.message &&
|
|
70
|
-
typeof error.message === 'string' &&
|
|
71
|
-
error.message.trim()
|
|
64
|
+
.catch(() => ({ message: response.statusText })));
|
|
65
|
+
const errorMessage = error?.message && typeof error.message === 'string' && error.message.trim()
|
|
72
66
|
? error.message
|
|
73
67
|
: `Failed to upload profile picture: ${response.statusText} (Status: ${response.status})`;
|
|
74
68
|
throw new Error(errorMessage);
|
package/dist/userinfo.d.ts
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
export interface UserInfo {
|
|
2
|
+
sub: string;
|
|
3
|
+
email: string;
|
|
4
|
+
email_verified: boolean;
|
|
5
|
+
given_name: string | null;
|
|
6
|
+
family_name: string | null;
|
|
7
|
+
name: string | null;
|
|
8
|
+
picture: string | null;
|
|
9
|
+
updated_at: number;
|
|
10
|
+
}
|
|
11
|
+
export interface IntrospectResult {
|
|
12
|
+
active: boolean;
|
|
13
|
+
clientId?: string;
|
|
14
|
+
username?: string;
|
|
15
|
+
scope?: string;
|
|
16
|
+
sub?: string;
|
|
17
|
+
exp?: number;
|
|
18
|
+
iat?: number;
|
|
19
|
+
tokenType?: string;
|
|
20
|
+
}
|
|
2
21
|
export declare function fetchUserInfo(accessToken: string): Promise<UserInfo>;
|
|
3
22
|
export interface IntrospectTokenOptions {
|
|
4
23
|
token: string;
|
package/dist/userinfo.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"userinfo.d.ts","sourceRoot":"","sources":["../src/userinfo.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"userinfo.d.ts","sourceRoot":"","sources":["../src/userinfo.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,OAAO,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAc1E;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CA0B3B"}
|
package/dist/userinfo.js
CHANGED
|
@@ -2,14 +2,40 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.fetchUserInfo = fetchUserInfo;
|
|
4
4
|
exports.introspectAccessToken = introspectAccessToken;
|
|
5
|
-
const
|
|
5
|
+
const AUTH_SERVER_URL = 'https://auth-api.tekcify.com';
|
|
6
6
|
async function fetchUserInfo(accessToken) {
|
|
7
|
-
|
|
7
|
+
const response = await fetch(`${AUTH_SERVER_URL}/api/oauth/userinfo`, {
|
|
8
|
+
method: 'GET',
|
|
9
|
+
headers: {
|
|
10
|
+
Authorization: `Bearer ${accessToken}`,
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
if (!response.ok) {
|
|
14
|
+
const error = (await response.json().catch(() => ({})));
|
|
15
|
+
throw new Error(error.message ?? 'Failed to get user info');
|
|
16
|
+
}
|
|
17
|
+
return response.json();
|
|
8
18
|
}
|
|
9
19
|
async function introspectAccessToken(options) {
|
|
10
|
-
|
|
20
|
+
const body = {
|
|
11
21
|
token: options.token,
|
|
12
|
-
|
|
13
|
-
|
|
22
|
+
};
|
|
23
|
+
if (options.clientId) {
|
|
24
|
+
body.clientId = options.clientId;
|
|
25
|
+
}
|
|
26
|
+
if (options.clientSecret) {
|
|
27
|
+
body.clientSecret = options.clientSecret;
|
|
28
|
+
}
|
|
29
|
+
const response = await fetch(`${AUTH_SERVER_URL}/api/oauth/token/introspect`, {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
headers: {
|
|
32
|
+
'Content-Type': 'application/json',
|
|
33
|
+
},
|
|
34
|
+
body: JSON.stringify(body),
|
|
14
35
|
});
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
const error = (await response.json().catch(() => ({})));
|
|
38
|
+
throw new Error(error.message ?? 'Failed to introspect token');
|
|
39
|
+
}
|
|
40
|
+
return response.json();
|
|
15
41
|
}
|
package/dist/verify.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TokenPayload, VerifyTokenOptions, VerifiedToken } from
|
|
1
|
+
import type { TokenPayload, VerifyTokenOptions, VerifiedToken } from './types';
|
|
2
2
|
export declare function verifyAccessToken(token: string, options: VerifyTokenOptions): VerifiedToken;
|
|
3
3
|
export declare function decodeToken(token: string): TokenPayload | null;
|
|
4
4
|
//# sourceMappingURL=verify.d.ts.map
|
package/dist/verify.js
CHANGED
|
@@ -1,57 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
35
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
6
|
exports.verifyAccessToken = verifyAccessToken;
|
|
37
7
|
exports.decodeToken = decodeToken;
|
|
38
|
-
const
|
|
8
|
+
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
39
9
|
function verifyAccessToken(token, options) {
|
|
40
10
|
try {
|
|
41
|
-
const decoded =
|
|
42
|
-
issuer: options.issuer ??
|
|
43
|
-
audience: options.audience ??
|
|
11
|
+
const decoded = jsonwebtoken_1.default.verify(token, options.secret, {
|
|
12
|
+
issuer: options.issuer ?? 'tekcify-auth',
|
|
13
|
+
audience: options.audience ?? 'tekcify-api',
|
|
44
14
|
});
|
|
45
|
-
if (decoded.type !==
|
|
15
|
+
if (decoded.type !== 'access') {
|
|
46
16
|
return { payload: decoded, valid: false };
|
|
47
17
|
}
|
|
48
18
|
return { payload: decoded, valid: true };
|
|
49
19
|
}
|
|
50
20
|
catch (error) {
|
|
51
|
-
if (error instanceof
|
|
21
|
+
if (error instanceof jsonwebtoken_1.default.JsonWebTokenError) {
|
|
52
22
|
return { payload: {}, valid: false };
|
|
53
23
|
}
|
|
54
|
-
if (error instanceof
|
|
24
|
+
if (error instanceof jsonwebtoken_1.default.TokenExpiredError) {
|
|
55
25
|
return { payload: {}, valid: false };
|
|
56
26
|
}
|
|
57
27
|
throw error;
|
|
@@ -59,7 +29,7 @@ function verifyAccessToken(token, options) {
|
|
|
59
29
|
}
|
|
60
30
|
function decodeToken(token) {
|
|
61
31
|
try {
|
|
62
|
-
return
|
|
32
|
+
return jsonwebtoken_1.default.decode(token);
|
|
63
33
|
}
|
|
64
34
|
catch {
|
|
65
35
|
return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekcify/auth-backend",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Backend authentication helpers for Tekcify Auth. Provides middleware, guards, and utilities for validating JWT tokens and protecting API routes in NestJS and Express applications.",
|
|
5
5
|
"author": "Tekcify",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -51,7 +51,8 @@
|
|
|
51
51
|
"license": "MIT",
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@tekcify/auth-core-client": "^2.0.0",
|
|
54
|
-
"jsonwebtoken": "^9.0.3"
|
|
54
|
+
"jsonwebtoken": "^9.0.3",
|
|
55
|
+
"jwks-rsa": "^3.2.1"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
57
58
|
"@nestjs/common": "^11.1.9",
|