@tekcify/auth-backend 2.0.4 → 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 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 HMAC/RS256
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
- You need the JWT access secret from your Tekcify Auth server. This should match the `JWT_ACCESS_SECRET` environment variable used by the auth server.
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
- ### Step 1: Install Dependencies
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
- ### Step 2: Create and Configure the Guard
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
- Create a guard provider in your module:
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
- ### Step 1: Install Dependencies
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
- ### Step 2: Create Auth Middleware
236
+ #### Step 2: Create Auth Middleware
163
237
 
164
238
  ```typescript
165
239
  import express from 'express';
166
- import { createAuthMiddleware } from '@tekcify/auth-backend/express';
240
+ import { createJwksAuthMiddleware } from '@tekcify/auth-backend/express';
167
241
 
168
242
  const app = express();
169
243
 
170
- const authMiddleware = createAuthMiddleware({
171
- secret: process.env.JWT_ACCESS_SECRET!,
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,7 +7,6 @@ export interface Application {
7
7
  redirectUris: string[];
8
8
  authorizedOrigins: string[] | null;
9
9
  scopes: string[];
10
- primaryColor: string | null;
11
10
  createdAt: string;
12
11
  updatedAt: string;
13
12
  }
@@ -17,7 +16,6 @@ export interface CreateApplicationDto {
17
16
  redirectUris: string[];
18
17
  authorizedOrigins?: string[];
19
18
  scopes: string[];
20
- primaryColor?: string;
21
19
  }
22
20
  export interface CreateApplicationResponse extends Application {
23
21
  clientSecret: string;
@@ -28,7 +26,6 @@ export interface UpdateApplicationDto {
28
26
  redirectUris?: string[];
29
27
  authorizedOrigins?: string[];
30
28
  scopes?: string[];
31
- primaryColor?: string;
32
29
  }
33
30
  export interface LogoUploadResponse {
34
31
  logoUrl: string;
@@ -1 +1 @@
1
- {"version":3,"file":"application-management.d.ts","sourceRoot":"","sources":["../src/application-management.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"application-management.d.ts","sourceRoot":"","sources":["../src/application-management.ts"],"names":[],"mappings":"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"}
@@ -7,9 +7,9 @@ exports.updateApplication = updateApplication;
7
7
  exports.deleteApplication = deleteApplication;
8
8
  exports.uploadApplicationLogo = uploadApplicationLogo;
9
9
  exports.regenerateApplicationSecret = regenerateApplicationSecret;
10
- const auth_core_client_1 = require("@tekcify/auth-core-client");
10
+ const AUTH_SERVER_URL = 'https://auth-api.tekcify.com';
11
11
  function buildUrl(path, query) {
12
- const url = new URL(`${auth_core_client_1.AUTH_SERVER_URL}${path}`);
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));
@@ -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"}
@@ -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
@@ -1,5 +1,6 @@
1
1
  export * from './types';
2
2
  export * from './verify';
3
+ export * from './jwks-verifier';
3
4
  export * from './userinfo';
4
5
  export * from './user-profile';
5
6
  export * from './application-management';
@@ -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;CAsC/D"}
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"}
@@ -1,94 +1,55 @@
1
1
  "use strict";
2
- var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
3
- function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
4
- var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
5
- var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
6
- var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
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 __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
30
- var useValue = arguments.length > 2;
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
- let _classDecorators = [(0, common_1.Injectable)()];
46
- let _classDescriptor;
47
- let _classExtraInitializers = [];
48
- let _classThis;
49
- var JwtAuthGuard = _classThis = class {
50
- constructor(options) {
51
- this.options = options;
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
- async canActivate(context) {
54
- const request = context
55
- .switchToHttp()
56
- .getRequest();
57
- const authHeader = request.headers.authorization;
58
- if (!authHeader?.startsWith('Bearer ')) {
59
- throw new common_1.UnauthorizedException('Missing or invalid authorization header');
60
- }
61
- const token = authHeader.substring(7);
62
- const verified = (0, verify_1.verifyAccessToken)(token, this.options);
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
- request.user = {
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
- __setFunctionName(_classThis, "JwtAuthGuard");
85
- (() => {
86
- const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
87
- __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
88
- JwtAuthGuard = _classThis = _classDescriptor.value;
89
- if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
90
- __runInitializers(_classThis, _classExtraInitializers);
91
- })();
92
- return JwtAuthGuard = _classThis;
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);
@@ -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"}
@@ -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
@@ -18,5 +18,6 @@ export interface UserPayload {
18
18
  userId: string;
19
19
  email: string;
20
20
  scopes?: string[];
21
+ role?: string;
21
22
  }
22
23
  //# sourceMappingURL=types.d.ts.map
@@ -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;CACnB"}
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":"AAEA,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,CAuBtB;AAED,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,WAAW,CAAC,CAwBtB;AAED,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,IAAI,GAAG,IAAI,EACjB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC,CA+BzB"}
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"}
@@ -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 auth_core_client_1 = require("@tekcify/auth-core-client");
6
+ const AUTH_SERVER_URL = 'https://auth-api.tekcify.com';
7
7
  async function getUserProfile(accessToken) {
8
- const response = await fetch(`${auth_core_client_1.AUTH_SERVER_URL}/api/user/profile`, {
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(`${auth_core_client_1.AUTH_SERVER_URL}/api/user/profile`, {
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(`${auth_core_client_1.AUTH_SERVER_URL}/api/user/profile/picture`, {
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);
@@ -1,4 +1,23 @@
1
- import type { UserInfo, IntrospectResult } from '@tekcify/auth-core-client';
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;
@@ -1 +1 @@
1
- {"version":3,"file":"userinfo.d.ts","sourceRoot":"","sources":["../src/userinfo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAG5E,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAE1E;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,CAM3B"}
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 auth_core_client_1 = require("@tekcify/auth-core-client");
5
+ const AUTH_SERVER_URL = 'https://auth-api.tekcify.com';
6
6
  async function fetchUserInfo(accessToken) {
7
- return (0, auth_core_client_1.getUserInfo)(accessToken);
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
- return (0, auth_core_client_1.introspectToken)({
20
+ const body = {
11
21
  token: options.token,
12
- clientId: options.clientId,
13
- clientSecret: options.clientSecret,
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 "./types";
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 __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
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 jwt = __importStar(require("jsonwebtoken"));
8
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
39
9
  function verifyAccessToken(token, options) {
40
10
  try {
41
- const decoded = jwt.verify(token, options.secret, {
42
- issuer: options.issuer ?? "tekcify-auth",
43
- audience: options.audience ?? "tekcify-api",
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 !== "access") {
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 jwt.JsonWebTokenError) {
21
+ if (error instanceof jsonwebtoken_1.default.JsonWebTokenError) {
52
22
  return { payload: {}, valid: false };
53
23
  }
54
- if (error instanceof jwt.TokenExpiredError) {
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 jwt.decode(token);
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.4",
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",