@oxyhq/core 1.2.5 → 1.3.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.
@@ -6,6 +6,66 @@ function OxyServicesAuthMixin(Base) {
6
6
  return class extends Base {
7
7
  constructor(...args) {
8
8
  super(...args);
9
+ /** @internal */ this._serviceToken = null;
10
+ /** @internal */ this._serviceTokenExp = 0;
11
+ /** @internal */ this._serviceApiKey = null;
12
+ /** @internal */ this._serviceApiSecret = null;
13
+ }
14
+ /**
15
+ * Configure service credentials for internal service-to-service communication.
16
+ * Call this once at startup so that getServiceToken() and makeServiceRequest()
17
+ * can automatically obtain and refresh tokens.
18
+ *
19
+ * @param apiKey - DeveloperApp API key (oxy_dk_*)
20
+ * @param apiSecret - DeveloperApp API secret
21
+ */
22
+ configureServiceAuth(apiKey, apiSecret) {
23
+ this._serviceApiKey = apiKey;
24
+ this._serviceApiSecret = apiSecret;
25
+ // Invalidate any cached token
26
+ this._serviceToken = null;
27
+ this._serviceTokenExp = 0;
28
+ }
29
+ /**
30
+ * Get a service token for internal service-to-service communication.
31
+ * Tokens are short-lived (1h) and automatically cached/refreshed.
32
+ *
33
+ * @param apiKey - DeveloperApp API key (optional if configureServiceAuth was called)
34
+ * @param apiSecret - DeveloperApp API secret (optional if configureServiceAuth was called)
35
+ */
36
+ async getServiceToken(apiKey, apiSecret) {
37
+ const key = apiKey || this._serviceApiKey;
38
+ const secret = apiSecret || this._serviceApiSecret;
39
+ if (!key || !secret) {
40
+ throw new Error('Service credentials not provided. Call configureServiceAuth() or pass apiKey and apiSecret.');
41
+ }
42
+ // Return cached token if still valid (with 60s buffer)
43
+ if (this._serviceToken && this._serviceTokenExp > Date.now() + 60000) {
44
+ return this._serviceToken;
45
+ }
46
+ const response = await this.makeRequest('POST', '/api/auth/service-token', { apiKey: key, apiSecret: secret }, { cache: false, retry: false });
47
+ this._serviceToken = response.token;
48
+ this._serviceTokenExp = Date.now() + response.expiresIn * 1000;
49
+ return this._serviceToken;
50
+ }
51
+ /**
52
+ * Make an authenticated request on behalf of a user using a service token.
53
+ * Automatically obtains/refreshes the service token.
54
+ *
55
+ * @param method - HTTP method
56
+ * @param url - API endpoint URL
57
+ * @param data - Request body or query params
58
+ * @param userId - Optional user ID to act on behalf of (sent as X-Oxy-User-Id)
59
+ */
60
+ async makeServiceRequest(method, url, data, userId) {
61
+ const token = await this.getServiceToken();
62
+ const headers = {
63
+ Authorization: `Bearer ${token}`,
64
+ };
65
+ if (userId) {
66
+ headers['X-Oxy-User-Id'] = userId;
67
+ }
68
+ return this.makeRequest(method, url, data, { headers, cache: false });
9
69
  }
10
70
  /**
11
71
  * Register a new identity with public key authentication
@@ -114,6 +114,35 @@ function OxyServicesUtilityMixin(Base) {
114
114
  return onError(error);
115
115
  return res.status(401).json(error);
116
116
  }
117
+ // Handle service tokens (internal service-to-service auth)
118
+ // Service tokens are stateless JWTs with type: 'service' — no session validation needed
119
+ if (decoded.type === 'service') {
120
+ // Check expiration
121
+ if (decoded.exp && decoded.exp < Math.floor(Date.now() / 1000)) {
122
+ if (optional) {
123
+ req.userId = null;
124
+ req.user = null;
125
+ return next();
126
+ }
127
+ const error = { message: 'Service token expired', code: 'TOKEN_EXPIRED', status: 401 };
128
+ if (onError)
129
+ return onError(error);
130
+ return res.status(401).json(error);
131
+ }
132
+ // Read delegated user ID from header
133
+ const oxyUserId = req.headers['x-oxy-user-id'];
134
+ req.userId = oxyUserId || null;
135
+ req.user = oxyUserId ? { id: oxyUserId } : null;
136
+ req.accessToken = token;
137
+ req.serviceApp = {
138
+ appId: decoded.appId || '',
139
+ appName: decoded.appName || 'unknown',
140
+ };
141
+ if (debug) {
142
+ console.log(`[oxy.auth] Service token OK app=${decoded.appName} delegateUser=${oxyUserId || '(none)'}`);
143
+ }
144
+ return next();
145
+ }
117
146
  const userId = decoded.userId || decoded.id;
118
147
  if (!userId) {
119
148
  if (optional) {
@@ -326,5 +355,36 @@ function OxyServicesUtilityMixin(Base) {
326
355
  }
327
356
  };
328
357
  }
358
+ /**
359
+ * Express.js middleware that only allows service tokens.
360
+ * Use this for internal-only endpoints that should not be accessible
361
+ * to regular users or API key consumers.
362
+ *
363
+ * @example
364
+ * ```typescript
365
+ * // Protect internal endpoints
366
+ * app.use('/internal', oxy.serviceAuth());
367
+ *
368
+ * app.post('/internal/trigger', (req, res) => {
369
+ * console.log('Service app:', req.serviceApp);
370
+ * console.log('Acting on behalf of user:', req.userId);
371
+ * });
372
+ * ```
373
+ */
374
+ serviceAuth(options = {}) {
375
+ const innerAuth = this.auth({ ...options });
376
+ return async (req, res, next) => {
377
+ await innerAuth(req, res, () => {
378
+ if (!req.serviceApp) {
379
+ return res.status(403).json({
380
+ error: 'Service token required',
381
+ message: 'This endpoint is only accessible to internal services',
382
+ code: 'SERVICE_TOKEN_REQUIRED',
383
+ });
384
+ }
385
+ next();
386
+ });
387
+ };
388
+ }
329
389
  };
330
390
  }
@@ -3,6 +3,66 @@ export function OxyServicesAuthMixin(Base) {
3
3
  return class extends Base {
4
4
  constructor(...args) {
5
5
  super(...args);
6
+ /** @internal */ this._serviceToken = null;
7
+ /** @internal */ this._serviceTokenExp = 0;
8
+ /** @internal */ this._serviceApiKey = null;
9
+ /** @internal */ this._serviceApiSecret = null;
10
+ }
11
+ /**
12
+ * Configure service credentials for internal service-to-service communication.
13
+ * Call this once at startup so that getServiceToken() and makeServiceRequest()
14
+ * can automatically obtain and refresh tokens.
15
+ *
16
+ * @param apiKey - DeveloperApp API key (oxy_dk_*)
17
+ * @param apiSecret - DeveloperApp API secret
18
+ */
19
+ configureServiceAuth(apiKey, apiSecret) {
20
+ this._serviceApiKey = apiKey;
21
+ this._serviceApiSecret = apiSecret;
22
+ // Invalidate any cached token
23
+ this._serviceToken = null;
24
+ this._serviceTokenExp = 0;
25
+ }
26
+ /**
27
+ * Get a service token for internal service-to-service communication.
28
+ * Tokens are short-lived (1h) and automatically cached/refreshed.
29
+ *
30
+ * @param apiKey - DeveloperApp API key (optional if configureServiceAuth was called)
31
+ * @param apiSecret - DeveloperApp API secret (optional if configureServiceAuth was called)
32
+ */
33
+ async getServiceToken(apiKey, apiSecret) {
34
+ const key = apiKey || this._serviceApiKey;
35
+ const secret = apiSecret || this._serviceApiSecret;
36
+ if (!key || !secret) {
37
+ throw new Error('Service credentials not provided. Call configureServiceAuth() or pass apiKey and apiSecret.');
38
+ }
39
+ // Return cached token if still valid (with 60s buffer)
40
+ if (this._serviceToken && this._serviceTokenExp > Date.now() + 60000) {
41
+ return this._serviceToken;
42
+ }
43
+ const response = await this.makeRequest('POST', '/api/auth/service-token', { apiKey: key, apiSecret: secret }, { cache: false, retry: false });
44
+ this._serviceToken = response.token;
45
+ this._serviceTokenExp = Date.now() + response.expiresIn * 1000;
46
+ return this._serviceToken;
47
+ }
48
+ /**
49
+ * Make an authenticated request on behalf of a user using a service token.
50
+ * Automatically obtains/refreshes the service token.
51
+ *
52
+ * @param method - HTTP method
53
+ * @param url - API endpoint URL
54
+ * @param data - Request body or query params
55
+ * @param userId - Optional user ID to act on behalf of (sent as X-Oxy-User-Id)
56
+ */
57
+ async makeServiceRequest(method, url, data, userId) {
58
+ const token = await this.getServiceToken();
59
+ const headers = {
60
+ Authorization: `Bearer ${token}`,
61
+ };
62
+ if (userId) {
63
+ headers['X-Oxy-User-Id'] = userId;
64
+ }
65
+ return this.makeRequest(method, url, data, { headers, cache: false });
6
66
  }
7
67
  /**
8
68
  * Register a new identity with public key authentication
@@ -111,6 +111,35 @@ export function OxyServicesUtilityMixin(Base) {
111
111
  return onError(error);
112
112
  return res.status(401).json(error);
113
113
  }
114
+ // Handle service tokens (internal service-to-service auth)
115
+ // Service tokens are stateless JWTs with type: 'service' — no session validation needed
116
+ if (decoded.type === 'service') {
117
+ // Check expiration
118
+ if (decoded.exp && decoded.exp < Math.floor(Date.now() / 1000)) {
119
+ if (optional) {
120
+ req.userId = null;
121
+ req.user = null;
122
+ return next();
123
+ }
124
+ const error = { message: 'Service token expired', code: 'TOKEN_EXPIRED', status: 401 };
125
+ if (onError)
126
+ return onError(error);
127
+ return res.status(401).json(error);
128
+ }
129
+ // Read delegated user ID from header
130
+ const oxyUserId = req.headers['x-oxy-user-id'];
131
+ req.userId = oxyUserId || null;
132
+ req.user = oxyUserId ? { id: oxyUserId } : null;
133
+ req.accessToken = token;
134
+ req.serviceApp = {
135
+ appId: decoded.appId || '',
136
+ appName: decoded.appName || 'unknown',
137
+ };
138
+ if (debug) {
139
+ console.log(`[oxy.auth] Service token OK app=${decoded.appName} delegateUser=${oxyUserId || '(none)'}`);
140
+ }
141
+ return next();
142
+ }
114
143
  const userId = decoded.userId || decoded.id;
115
144
  if (!userId) {
116
145
  if (optional) {
@@ -323,5 +352,36 @@ export function OxyServicesUtilityMixin(Base) {
323
352
  }
324
353
  };
325
354
  }
355
+ /**
356
+ * Express.js middleware that only allows service tokens.
357
+ * Use this for internal-only endpoints that should not be accessible
358
+ * to regular users or API key consumers.
359
+ *
360
+ * @example
361
+ * ```typescript
362
+ * // Protect internal endpoints
363
+ * app.use('/internal', oxy.serviceAuth());
364
+ *
365
+ * app.post('/internal/trigger', (req, res) => {
366
+ * console.log('Service app:', req.serviceApp);
367
+ * console.log('Acting on behalf of user:', req.userId);
368
+ * });
369
+ * ```
370
+ */
371
+ serviceAuth(options = {}) {
372
+ const innerAuth = this.auth({ ...options });
373
+ return async (req, res, next) => {
374
+ await innerAuth(req, res, () => {
375
+ if (!req.serviceApp) {
376
+ return res.status(403).json({
377
+ error: 'Service token required',
378
+ message: 'This endpoint is only accessible to internal services',
379
+ code: 'SERVICE_TOKEN_REQUIRED',
380
+ });
381
+ }
382
+ next();
383
+ });
384
+ };
385
+ }
326
386
  };
327
387
  }
@@ -23,6 +23,8 @@ export type { CrossDomainAuthOptions } from './CrossDomainAuth';
23
23
  export type { FedCMAuthOptions, FedCMConfig } from './mixins/OxyServices.fedcm';
24
24
  export type { PopupAuthOptions } from './mixins/OxyServices.popup';
25
25
  export type { RedirectAuthOptions } from './mixins/OxyServices.redirect';
26
+ export type { ServiceTokenResponse } from './mixins/OxyServices.auth';
27
+ export type { ServiceApp } from './mixins/OxyServices.utility';
26
28
  export { KeyManager, SignatureService, RecoveryPhraseService } from './crypto';
27
29
  export type { KeyPair, SignedMessage, AuthChallenge, RecoveryPhraseResult } from './crypto';
28
30
  export * from './models/interfaces';
@@ -29,8 +29,44 @@ export interface PublicKeyCheckResponse {
29
29
  registered: boolean;
30
30
  message: string;
31
31
  }
32
+ export interface ServiceTokenResponse {
33
+ token: string;
34
+ expiresIn: number;
35
+ appName: string;
36
+ }
32
37
  export declare function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(Base: T): {
33
38
  new (...args: any[]): {
39
+ /** @internal */ _serviceToken: string | null;
40
+ /** @internal */ _serviceTokenExp: number;
41
+ /** @internal */ _serviceApiKey: string | null;
42
+ /** @internal */ _serviceApiSecret: string | null;
43
+ /**
44
+ * Configure service credentials for internal service-to-service communication.
45
+ * Call this once at startup so that getServiceToken() and makeServiceRequest()
46
+ * can automatically obtain and refresh tokens.
47
+ *
48
+ * @param apiKey - DeveloperApp API key (oxy_dk_*)
49
+ * @param apiSecret - DeveloperApp API secret
50
+ */
51
+ configureServiceAuth(apiKey: string, apiSecret: string): void;
52
+ /**
53
+ * Get a service token for internal service-to-service communication.
54
+ * Tokens are short-lived (1h) and automatically cached/refreshed.
55
+ *
56
+ * @param apiKey - DeveloperApp API key (optional if configureServiceAuth was called)
57
+ * @param apiSecret - DeveloperApp API secret (optional if configureServiceAuth was called)
58
+ */
59
+ getServiceToken(apiKey?: string, apiSecret?: string): Promise<string>;
60
+ /**
61
+ * Make an authenticated request on behalf of a user using a service token.
62
+ * Automatically obtains/refreshes the service token.
63
+ *
64
+ * @param method - HTTP method
65
+ * @param url - API endpoint URL
66
+ * @param data - Request body or query params
67
+ * @param userId - Optional user ID to act on behalf of (sent as X-Oxy-User-Id)
68
+ */
69
+ makeServiceRequest<R = any>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, userId?: string): Promise<R>;
34
70
  /**
35
71
  * Register a new identity with public key authentication
36
72
  * Identity is purely cryptographic - username and profile data are optional
@@ -1,5 +1,12 @@
1
1
  import type { ApiError } from '../models/interfaces';
2
2
  import type { OxyServicesBase } from '../OxyServices.base';
3
+ /**
4
+ * Service app metadata attached to requests authenticated with service tokens
5
+ */
6
+ export interface ServiceApp {
7
+ appId: string;
8
+ appName: string;
9
+ }
3
10
  /**
4
11
  * Options for oxyClient.auth() middleware
5
12
  */
@@ -80,6 +87,25 @@ export declare function OxyServicesUtilityMixin<T extends typeof OxyServicesBase
80
87
  authSocket(options?: {
81
88
  debug?: boolean;
82
89
  }): (socket: any, next: (err?: Error) => void) => Promise<void>;
90
+ /**
91
+ * Express.js middleware that only allows service tokens.
92
+ * Use this for internal-only endpoints that should not be accessible
93
+ * to regular users or API key consumers.
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * // Protect internal endpoints
98
+ * app.use('/internal', oxy.serviceAuth());
99
+ *
100
+ * app.post('/internal/trigger', (req, res) => {
101
+ * console.log('Service app:', req.serviceApp);
102
+ * console.log('Acting on behalf of user:', req.userId);
103
+ * });
104
+ * ```
105
+ */
106
+ serviceAuth(options?: {
107
+ debug?: boolean;
108
+ }): (req: any, res: any, next: any) => Promise<void>;
83
109
  httpService: import("../HttpService").HttpService;
84
110
  cloudURL: string;
85
111
  config: import("../OxyServices.base").OxyConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/core",
3
- "version": "1.2.5",
3
+ "version": "1.3.0",
4
4
  "description": "OxyHQ SDK Foundation — API client, authentication, cryptographic identity, and shared utilities",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
package/src/index.ts CHANGED
@@ -30,6 +30,8 @@ export type { CrossDomainAuthOptions } from './CrossDomainAuth';
30
30
  export type { FedCMAuthOptions, FedCMConfig } from './mixins/OxyServices.fedcm';
31
31
  export type { PopupAuthOptions } from './mixins/OxyServices.popup';
32
32
  export type { RedirectAuthOptions } from './mixins/OxyServices.redirect';
33
+ export type { ServiceTokenResponse } from './mixins/OxyServices.auth';
34
+ export type { ServiceApp } from './mixins/OxyServices.utility';
33
35
 
34
36
  // --- Crypto / Identity ---
35
37
  export { KeyManager, SignatureService, RecoveryPhraseService } from './crypto';
@@ -35,12 +35,99 @@ export interface PublicKeyCheckResponse {
35
35
  message: string;
36
36
  }
37
37
 
38
+ export interface ServiceTokenResponse {
39
+ token: string;
40
+ expiresIn: number;
41
+ appName: string;
42
+ }
43
+
38
44
  export function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(Base: T) {
39
45
  return class extends Base {
46
+ /** @internal */ _serviceToken: string | null = null;
47
+ /** @internal */ _serviceTokenExp: number = 0;
48
+ /** @internal */ _serviceApiKey: string | null = null;
49
+ /** @internal */ _serviceApiSecret: string | null = null;
50
+
40
51
  constructor(...args: any[]) {
41
52
  super(...(args as [any]));
42
53
  }
43
54
 
55
+ /**
56
+ * Configure service credentials for internal service-to-service communication.
57
+ * Call this once at startup so that getServiceToken() and makeServiceRequest()
58
+ * can automatically obtain and refresh tokens.
59
+ *
60
+ * @param apiKey - DeveloperApp API key (oxy_dk_*)
61
+ * @param apiSecret - DeveloperApp API secret
62
+ */
63
+ configureServiceAuth(apiKey: string, apiSecret: string): void {
64
+ this._serviceApiKey = apiKey;
65
+ this._serviceApiSecret = apiSecret;
66
+ // Invalidate any cached token
67
+ this._serviceToken = null;
68
+ this._serviceTokenExp = 0;
69
+ }
70
+
71
+ /**
72
+ * Get a service token for internal service-to-service communication.
73
+ * Tokens are short-lived (1h) and automatically cached/refreshed.
74
+ *
75
+ * @param apiKey - DeveloperApp API key (optional if configureServiceAuth was called)
76
+ * @param apiSecret - DeveloperApp API secret (optional if configureServiceAuth was called)
77
+ */
78
+ async getServiceToken(apiKey?: string, apiSecret?: string): Promise<string> {
79
+ const key = apiKey || this._serviceApiKey;
80
+ const secret = apiSecret || this._serviceApiSecret;
81
+
82
+ if (!key || !secret) {
83
+ throw new Error('Service credentials not provided. Call configureServiceAuth() or pass apiKey and apiSecret.');
84
+ }
85
+
86
+ // Return cached token if still valid (with 60s buffer)
87
+ if (this._serviceToken && this._serviceTokenExp > Date.now() + 60_000) {
88
+ return this._serviceToken;
89
+ }
90
+
91
+ const response = await this.makeRequest<ServiceTokenResponse>(
92
+ 'POST',
93
+ '/api/auth/service-token',
94
+ { apiKey: key, apiSecret: secret },
95
+ { cache: false, retry: false }
96
+ );
97
+
98
+ this._serviceToken = response.token;
99
+ this._serviceTokenExp = Date.now() + response.expiresIn * 1000;
100
+
101
+ return this._serviceToken;
102
+ }
103
+
104
+ /**
105
+ * Make an authenticated request on behalf of a user using a service token.
106
+ * Automatically obtains/refreshes the service token.
107
+ *
108
+ * @param method - HTTP method
109
+ * @param url - API endpoint URL
110
+ * @param data - Request body or query params
111
+ * @param userId - Optional user ID to act on behalf of (sent as X-Oxy-User-Id)
112
+ */
113
+ async makeServiceRequest<R = any>(
114
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
115
+ url: string,
116
+ data?: any,
117
+ userId?: string
118
+ ): Promise<R> {
119
+ const token = await this.getServiceToken();
120
+
121
+ const headers: Record<string, string> = {
122
+ Authorization: `Bearer ${token}`,
123
+ };
124
+ if (userId) {
125
+ headers['X-Oxy-User-Id'] = userId;
126
+ }
127
+
128
+ return this.makeRequest<R>(method, url, data, { headers, cache: false });
129
+ }
130
+
44
131
  /**
45
132
  * Register a new identity with public key authentication
46
133
  * Identity is purely cryptographic - username and profile data are optional
@@ -14,9 +14,20 @@ interface JwtPayload {
14
14
  userId?: string;
15
15
  id?: string;
16
16
  sessionId?: string;
17
+ type?: string;
18
+ appId?: string;
19
+ appName?: string;
17
20
  [key: string]: any;
18
21
  }
19
22
 
23
+ /**
24
+ * Service app metadata attached to requests authenticated with service tokens
25
+ */
26
+ export interface ServiceApp {
27
+ appId: string;
28
+ appName: string;
29
+ }
30
+
20
31
  /**
21
32
  * Options for oxyClient.auth() middleware
22
33
  */
@@ -149,6 +160,39 @@ export function OxyServicesUtilityMixin<T extends typeof OxyServicesBase>(Base:
149
160
  return res.status(401).json(error);
150
161
  }
151
162
 
163
+ // Handle service tokens (internal service-to-service auth)
164
+ // Service tokens are stateless JWTs with type: 'service' — no session validation needed
165
+ if (decoded.type === 'service') {
166
+ // Check expiration
167
+ if (decoded.exp && decoded.exp < Math.floor(Date.now() / 1000)) {
168
+ if (optional) {
169
+ req.userId = null;
170
+ req.user = null;
171
+ return next();
172
+ }
173
+ const error = { message: 'Service token expired', code: 'TOKEN_EXPIRED', status: 401 };
174
+ if (onError) return onError(error);
175
+ return res.status(401).json(error);
176
+ }
177
+
178
+ // Read delegated user ID from header
179
+ const oxyUserId = req.headers['x-oxy-user-id'] as string;
180
+
181
+ req.userId = oxyUserId || null;
182
+ req.user = oxyUserId ? ({ id: oxyUserId } as User) : null;
183
+ req.accessToken = token;
184
+ req.serviceApp = {
185
+ appId: decoded.appId || '',
186
+ appName: decoded.appName || 'unknown',
187
+ };
188
+
189
+ if (debug) {
190
+ console.log(`[oxy.auth] Service token OK app=${decoded.appName} delegateUser=${oxyUserId || '(none)'}`);
191
+ }
192
+
193
+ return next();
194
+ }
195
+
152
196
  const userId = decoded.userId || decoded.id;
153
197
  if (!userId) {
154
198
  if (optional) {
@@ -378,6 +422,38 @@ export function OxyServicesUtilityMixin<T extends typeof OxyServicesBase>(Base:
378
422
  }
379
423
  };
380
424
  }
425
+ /**
426
+ * Express.js middleware that only allows service tokens.
427
+ * Use this for internal-only endpoints that should not be accessible
428
+ * to regular users or API key consumers.
429
+ *
430
+ * @example
431
+ * ```typescript
432
+ * // Protect internal endpoints
433
+ * app.use('/internal', oxy.serviceAuth());
434
+ *
435
+ * app.post('/internal/trigger', (req, res) => {
436
+ * console.log('Service app:', req.serviceApp);
437
+ * console.log('Acting on behalf of user:', req.userId);
438
+ * });
439
+ * ```
440
+ */
441
+ serviceAuth(options: { debug?: boolean } = {}) {
442
+ const innerAuth = this.auth({ ...options });
443
+
444
+ return async (req: any, res: any, next: any) => {
445
+ await innerAuth(req, res, () => {
446
+ if (!req.serviceApp) {
447
+ return res.status(403).json({
448
+ error: 'Service token required',
449
+ message: 'This endpoint is only accessible to internal services',
450
+ code: 'SERVICE_TOKEN_REQUIRED',
451
+ });
452
+ }
453
+ next();
454
+ });
455
+ };
456
+ }
381
457
  };
382
458
  }
383
459