@arcote.tech/arc-host 0.1.4 → 0.1.5

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/dist/host.d.ts CHANGED
@@ -15,6 +15,10 @@ declare class RTCHost implements RealTimeCommunicationAdapter {
15
15
  * Convert JWT payload to AuthContext
16
16
  */
17
17
  private tokenToAuthContext;
18
+ /**
19
+ * Extract client IP address from request headers
20
+ */
21
+ private getClientIpAddress;
18
22
  /**
19
23
  * Get default auth context for anonymous users
20
24
  */
@@ -1 +1 @@
1
- {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../host.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,aAAa,EAElB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EAGvB,KAAK,4BAA4B,EAClC,MAAM,kBAAkB,CAAC;AAI1B,cAAM,OAAQ,YAAW,4BAA4B;IAMjD,OAAO,CAAC,OAAO;IALjB,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,KAAK,CAAuB;gBAG1B,OAAO,EAAE,aAAa,EAC9B,SAAS,EAAE,OAAO,CAAC,eAAe,CAAC;IASrC,aAAa,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,IAAI;IAI5C,IAAI,CACR,gBAAgB,EAAE,CAAC,EACjB,KAAK,EACL,IAAI,GACL,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,KAAK,IAAI,GACT,OAAO,CAAC,IAAI,CAAC;YAIF,eAAe;IAU7B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAO1B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAO7B;;OAEG;YACW,qBAAqB;IAWnC;;OAEG;IACH,OAAO,CAAC,iBAAiB;YAwCX,aAAa;YAmDb,WAAW;IAgEzB,OAAO,CAAC,WAAW;IA6DnB,OAAO,CAAC,gBAAgB;YAOV,UAAU;YA+CV,SAAS;IAevB,OAAO,CAAC,cAAc;CAGvB;AAED,eAAO,MAAM,cAAc,YACf,aAAa,aAAa,OAAO,CAAC,eAAe,CAAC,kBAE3D,CAAC"}
1
+ {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../host.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,aAAa,EAElB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EAGvB,KAAK,4BAA4B,EAClC,MAAM,kBAAkB,CAAC;AAI1B,cAAM,OAAQ,YAAW,4BAA4B;IAMjD,OAAO,CAAC,OAAO;IALjB,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,KAAK,CAAuB;gBAG1B,OAAO,EAAE,aAAa,EAC9B,SAAS,EAAE,OAAO,CAAC,eAAe,CAAC;IASrC,aAAa,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,IAAI;IAI5C,IAAI,CACR,gBAAgB,EAAE,CAAC,EACjB,KAAK,EACL,IAAI,GACL,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,KAAK,IAAI,GACT,OAAO,CAAC,IAAI,CAAC;YAIF,eAAe;IAS7B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAW1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAuB1B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAQ7B;;OAEG;YACW,qBAAqB;IAWnC;;OAEG;IACH,OAAO,CAAC,iBAAiB;YAwCX,aAAa;YAoDb,WAAW;IAiEzB,OAAO,CAAC,WAAW;IA6DnB,OAAO,CAAC,gBAAgB;YAsBV,UAAU;YA+CV,SAAS;IAevB,OAAO,CAAC,cAAc;CAGvB;AAED,eAAO,MAAM,cAAc,YACf,aAAa,aAAa,OAAO,CAAC,eAAe,CAAC,kBAE3D,CAAC"}
package/dist/index.js CHANGED
@@ -6719,20 +6719,36 @@ class RTCHost {
6719
6719
  const payload = verifyToken(token);
6720
6720
  return payload;
6721
6721
  } catch (error) {
6722
- console.error("Token verification failed:", error);
6723
6722
  return null;
6724
6723
  }
6725
6724
  }
6726
- tokenToAuthContext(payload) {
6725
+ tokenToAuthContext(payload, ipAddress) {
6727
6726
  return {
6728
6727
  userId: payload.userId,
6729
- roles: []
6728
+ roles: [],
6729
+ ipAddress
6730
6730
  };
6731
6731
  }
6732
- getDefaultAuthContext() {
6732
+ getClientIpAddress(req) {
6733
+ const xForwardedFor = req.headers.get("x-forwarded-for");
6734
+ const xRealIp = req.headers.get("x-real-ip");
6735
+ const cfConnectingIp = req.headers.get("cf-connecting-ip");
6736
+ if (xForwardedFor) {
6737
+ return xForwardedFor.split(",")[0].trim();
6738
+ }
6739
+ if (xRealIp) {
6740
+ return xRealIp;
6741
+ }
6742
+ if (cfConnectingIp) {
6743
+ return cfConnectingIp;
6744
+ }
6745
+ return;
6746
+ }
6747
+ getDefaultAuthContext(ipAddress) {
6733
6748
  return {
6734
6749
  userId: "anonymous",
6735
- roles: []
6750
+ roles: [],
6751
+ ipAddress
6736
6752
  };
6737
6753
  }
6738
6754
  async parseFormDataToObject(formData) {
@@ -6784,15 +6800,16 @@ class RTCHost {
6784
6800
  try {
6785
6801
  const authHeader = req.headers.get("Authorization");
6786
6802
  const token = authHeader?.replace("Bearer ", "");
6803
+ const clientIp = this.getClientIpAddress(req);
6787
6804
  let authContext;
6788
6805
  if (token && !this.isPublicEndpoint(url.pathname)) {
6789
6806
  const payload = await this.verifyAuthToken(token);
6790
6807
  if (!payload) {
6791
6808
  return new Response("Invalid or expired token", { status: 401 });
6792
6809
  }
6793
- authContext = this.tokenToAuthContext(payload);
6810
+ authContext = this.tokenToAuthContext(payload, clientIp);
6794
6811
  } else {
6795
- authContext = this.getDefaultAuthContext();
6812
+ authContext = this.getDefaultAuthContext(clientIp);
6796
6813
  }
6797
6814
  let argument;
6798
6815
  const contentType = req.headers.get("Content-Type");
@@ -6817,13 +6834,14 @@ class RTCHost {
6817
6834
  try {
6818
6835
  const authHeader = req.headers.get("Authorization");
6819
6836
  const token = authHeader?.replace("Bearer ", "");
6837
+ const clientIp = this.getClientIpAddress(req);
6820
6838
  let authContext;
6821
6839
  if (token) {
6822
6840
  const payload = await this.verifyAuthToken(token);
6823
6841
  if (!payload) {
6824
6842
  return new Response("Invalid or expired token", { status: 401 });
6825
6843
  }
6826
- authContext = this.tokenToAuthContext(payload);
6844
+ authContext = this.tokenToAuthContext(payload, clientIp);
6827
6845
  } else {
6828
6846
  return new Response("Authorization token required", { status: 401 });
6829
6847
  }
@@ -6901,8 +6919,15 @@ class RTCHost {
6901
6919
  });
6902
6920
  }
6903
6921
  isPublicEndpoint(pathname) {
6904
- const publicEndpoints = ["/command/signin", "/command/register"];
6905
- return publicEndpoints.some((endpoint) => pathname === endpoint);
6922
+ if (!pathname.startsWith("/command/")) {
6923
+ return false;
6924
+ }
6925
+ const commandName = pathname.split("/command/")[1];
6926
+ if (!commandName) {
6927
+ return false;
6928
+ }
6929
+ const contextElement = this.context.elements.find((element) => element.name === commandName);
6930
+ return contextElement && "isPublic" in contextElement ? contextElement.isPublic === true : false;
6906
6931
  }
6907
6932
  async handleSync(lastDate) {
6908
6933
  const syncDate = new Date;
package/host.ts CHANGED
@@ -53,7 +53,6 @@ class RTCHost implements RealTimeCommunicationAdapter {
53
53
  const payload = verifyToken(token);
54
54
  return payload;
55
55
  } catch (error) {
56
- console.error("Token verification failed:", error);
57
56
  return null;
58
57
  }
59
58
  }
@@ -61,20 +60,51 @@ class RTCHost implements RealTimeCommunicationAdapter {
61
60
  /**
62
61
  * Convert JWT payload to AuthContext
63
62
  */
64
- private tokenToAuthContext(payload: TokenPayload): AuthContext {
63
+ private tokenToAuthContext(
64
+ payload: TokenPayload,
65
+ ipAddress?: string,
66
+ ): AuthContext {
65
67
  return {
66
68
  userId: payload.userId,
67
69
  roles: [], // Default to no roles, you may want to extend TokenPayload to include roles
70
+ ipAddress,
68
71
  };
69
72
  }
70
73
 
74
+ /**
75
+ * Extract client IP address from request headers
76
+ */
77
+ private getClientIpAddress(req: Request): string | undefined {
78
+ // Check common headers for client IP
79
+ const xForwardedFor = req.headers.get("x-forwarded-for");
80
+ const xRealIp = req.headers.get("x-real-ip");
81
+ const cfConnectingIp = req.headers.get("cf-connecting-ip");
82
+
83
+ if (xForwardedFor) {
84
+ // x-forwarded-for can contain multiple IPs, take the first one
85
+ return xForwardedFor.split(",")[0].trim();
86
+ }
87
+
88
+ if (xRealIp) {
89
+ return xRealIp;
90
+ }
91
+
92
+ if (cfConnectingIp) {
93
+ return cfConnectingIp;
94
+ }
95
+
96
+ // Fallback - this might not work in all environments
97
+ return undefined;
98
+ }
99
+
71
100
  /**
72
101
  * Get default auth context for anonymous users
73
102
  */
74
- private getDefaultAuthContext(): AuthContext {
103
+ private getDefaultAuthContext(ipAddress?: string): AuthContext {
75
104
  return {
76
105
  userId: "anonymous",
77
106
  roles: [],
107
+ ipAddress,
78
108
  };
79
109
  }
80
110
 
@@ -148,15 +178,16 @@ class RTCHost implements RealTimeCommunicationAdapter {
148
178
  const authHeader = req.headers.get("Authorization");
149
179
  const token = authHeader?.replace("Bearer ", "");
150
180
 
181
+ const clientIp = this.getClientIpAddress(req);
151
182
  let authContext: AuthContext;
152
183
  if (token && !this.isPublicEndpoint(url.pathname)) {
153
184
  const payload = await this.verifyAuthToken(token);
154
185
  if (!payload) {
155
186
  return new Response("Invalid or expired token", { status: 401 });
156
187
  }
157
- authContext = this.tokenToAuthContext(payload);
188
+ authContext = this.tokenToAuthContext(payload, clientIp);
158
189
  } else {
159
- authContext = this.getDefaultAuthContext();
190
+ authContext = this.getDefaultAuthContext(clientIp);
160
191
  }
161
192
 
162
193
  let argument: any;
@@ -192,13 +223,14 @@ class RTCHost implements RealTimeCommunicationAdapter {
192
223
  const authHeader = req.headers.get("Authorization");
193
224
  const token = authHeader?.replace("Bearer ", "");
194
225
 
226
+ const clientIp = this.getClientIpAddress(req);
195
227
  let authContext: AuthContext;
196
228
  if (token) {
197
229
  const payload = await this.verifyAuthToken(token);
198
230
  if (!payload) {
199
231
  return new Response("Invalid or expired token", { status: 401 });
200
232
  }
201
- authContext = this.tokenToAuthContext(payload);
233
+ authContext = this.tokenToAuthContext(payload, clientIp);
202
234
  } else {
203
235
  return new Response("Authorization token required", { status: 401 });
204
236
  }
@@ -312,10 +344,25 @@ class RTCHost implements RealTimeCommunicationAdapter {
312
344
  }
313
345
 
314
346
  private isPublicEndpoint(pathname: string): boolean {
315
- // Define which endpoints don't require authentication
316
- const publicEndpoints = ["/command/signin", "/command/register"];
347
+ // Extract command name from pathname
348
+ if (!pathname.startsWith("/command/")) {
349
+ return false;
350
+ }
351
+
352
+ const commandName = pathname.split("/command/")[1];
353
+ if (!commandName) {
354
+ return false;
355
+ }
356
+
357
+ // Get the command from the context and check if it's marked as public
358
+ const contextElement = this.context.elements.find(
359
+ (element: any) => element.name === commandName,
360
+ );
317
361
 
318
- return publicEndpoints.some((endpoint) => pathname === endpoint);
362
+ // Check if it's an ArcCommand instance and if it's marked as public
363
+ return contextElement && "isPublic" in contextElement
364
+ ? contextElement.isPublic === true
365
+ : false;
319
366
  }
320
367
 
321
368
  private async handleSync(lastDate: string | null) {
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
7
- "version": "0.1.4",
7
+ "version": "0.1.5",
8
8
  "private": false,
9
9
  "author": "Przemysław Krasiński [arcote.tech]",
10
10
  "dependencies": {