@scalekit-sdk/node 2.1.3 → 2.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/src/scalekit.ts CHANGED
@@ -10,6 +10,7 @@ import DomainClient from './domain';
10
10
  import OrganizationClient from './organization';
11
11
  import PasswordlessClient from './passwordless';
12
12
  import UserClient from './user';
13
+ import SessionClient from './session';
13
14
  import RoleClient from './role';
14
15
  import PermissionClient from './permission';
15
16
  import { IdpInitiatedLoginClaims, IdTokenClaim, User } from './types/auth';
@@ -39,6 +40,7 @@ export default class ScalekitClient {
39
40
  readonly directory: DirectoryClient;
40
41
  readonly passwordless: PasswordlessClient;
41
42
  readonly user: UserClient;
43
+ readonly session: SessionClient;
42
44
  readonly role: RoleClient;
43
45
  readonly permission: PermissionClient;
44
46
  constructor(
@@ -79,6 +81,10 @@ export default class ScalekitClient {
79
81
  this.grpcConnect,
80
82
  this.coreClient
81
83
  );
84
+ this.session = new SessionClient(
85
+ this.grpcConnect,
86
+ this.coreClient
87
+ );
82
88
  this.role = new RoleClient(
83
89
  this.grpcConnect,
84
90
  this.coreClient
@@ -251,7 +257,37 @@ export default class ScalekitClient {
251
257
  const webhookTimestamp = headers['webhook-timestamp'];
252
258
  const webhookSignature = headers['webhook-signature'];
253
259
 
254
- if (!webhookId || !webhookTimestamp || !webhookSignature) {
260
+ return this.verifyPayloadSignature(secret, webhookId, webhookTimestamp, webhookSignature, payload);
261
+ }
262
+
263
+ /**
264
+ * Verify interceptor payload
265
+ *
266
+ * @param {string} secret The secret
267
+ * @param {Record<string, string>} headers The headers
268
+ * @param {string} payload The payload
269
+ * @return {boolean} Returns true if the payload is valid.
270
+ */
271
+ verifyInterceptorPayload(secret: string, headers: Record<string, string>, payload: string): boolean {
272
+ const interceptorId = headers['interceptor-id'];
273
+ const interceptorTimestamp = headers['interceptor-timestamp'];
274
+ const interceptorSignature = headers['interceptor-signature'];
275
+
276
+ return this.verifyPayloadSignature(secret, interceptorId, interceptorTimestamp, interceptorSignature, payload);
277
+ }
278
+
279
+ /**
280
+ * Common payload signature verification logic
281
+ *
282
+ * @param {string} secret The secret
283
+ * @param {string} id The webhook/interceptor id
284
+ * @param {string} timestamp The timestamp
285
+ * @param {string} signature The signature
286
+ * @param {string} payload The payload
287
+ * @return {boolean} Returns true if the payload signature is valid.
288
+ */
289
+ private verifyPayloadSignature(secret: string, id: string, timestamp: string, signature: string, payload: string): boolean {
290
+ if (!id || !timestamp || !signature) {
255
291
  throw new WebhookVerificationError("Missing required headers");
256
292
  }
257
293
 
@@ -261,18 +297,18 @@ export default class ScalekitClient {
261
297
  }
262
298
 
263
299
  try {
264
- const timestamp = this.verifyTimestamp(webhookTimestamp);
265
- const data = `${webhookId}.${Math.floor(timestamp.getTime() / 1000)}.${payload}`;
300
+ const timestampDate = this.verifyTimestamp(timestamp);
301
+ const data = `${id}.${Math.floor(timestampDate.getTime() / 1000)}.${payload}`;
266
302
  const secretBytes = Buffer.from(secretParts[1], 'base64');
267
303
  const computedSignature = this.computeSignature(secretBytes, data);
268
- const receivedSignatures = webhookSignature.split(" ");
304
+ const receivedSignatures = signature.split(" ");
269
305
 
270
306
  for (const versionedSignature of receivedSignatures) {
271
- const [version, signature] = versionedSignature.split(",");
307
+ const [version, receivedSignature] = versionedSignature.split(",");
272
308
  if (version !== WEBHOOK_SIGNATURE_VERSION) {
273
309
  continue;
274
310
  }
275
- if (crypto.timingSafeEqual(Buffer.from(signature, 'base64'), Buffer.from(computedSignature, 'base64'))) {
311
+ if (crypto.timingSafeEqual(Buffer.from(receivedSignature, 'base64'), Buffer.from(computedSignature, 'base64'))) {
276
312
  return true;
277
313
  }
278
314
  }
package/src/session.ts ADDED
@@ -0,0 +1,134 @@
1
+ import { PartialMessage } from '@bufbuild/protobuf';
2
+ import { PromiseClient } from '@connectrpc/connect';
3
+ import GrpcConnect from './connect';
4
+ import CoreClient from './core';
5
+ import { SessionService } from './pkg/grpc/scalekit/v1/sessions/sessions_connect';
6
+ import {
7
+ SessionDetailsRequest,
8
+ SessionDetails,
9
+ UserSessionDetailsRequest,
10
+ UserSessionDetails,
11
+ UserSessionFilter,
12
+ RevokeSessionRequest,
13
+ RevokeSessionResponse,
14
+ RevokeAllUserSessionsRequest,
15
+ RevokeAllUserSessionsResponse
16
+ } from './pkg/grpc/scalekit/v1/sessions/sessions_pb';
17
+ import { Timestamp } from '@bufbuild/protobuf';
18
+
19
+ export default class SessionClient {
20
+ private client: PromiseClient<typeof SessionService>;
21
+
22
+ constructor(
23
+ private readonly grpcConnect: GrpcConnect,
24
+ private readonly coreClient: CoreClient
25
+ ) {
26
+ this.client = this.grpcConnect.createClient(SessionService);
27
+ }
28
+
29
+ /**
30
+ * Get details for a specific session
31
+ * @param {string} sessionId The session id
32
+ * @returns {Promise<SessionDetails>} The session details
33
+ */
34
+ async getSession(sessionId: string): Promise<SessionDetails> {
35
+ return this.coreClient.connectExec(
36
+ this.client.getSession,
37
+ {
38
+ sessionId
39
+ }
40
+ );
41
+ }
42
+
43
+ /**
44
+ * Get all session details for a user with pagination and filtering
45
+ * @param {string} userId The user id
46
+ * @param {object} options The pagination and filtering options
47
+ * @param {number} options.pageSize The page size
48
+ * @param {string} options.pageToken The page token
49
+ * @param {object} options.filter The session filter options
50
+ * @param {string[]} options.filter.status The session statuses to filter by
51
+ * @param {Date} options.filter.startTime The start time for filtering sessions
52
+ * @param {Date} options.filter.endTime The end time for filtering sessions
53
+ * @returns {Promise<UserSessionDetails>} The user session details
54
+ */
55
+ async getUserSessions(
56
+ userId: string,
57
+ options?: {
58
+ pageSize?: number,
59
+ pageToken?: string,
60
+ filter?: {
61
+ status?: string[],
62
+ startTime?: Date,
63
+ endTime?: Date
64
+ }
65
+ }
66
+ ): Promise<UserSessionDetails> {
67
+ const request: PartialMessage<UserSessionDetailsRequest> = {
68
+ userId
69
+ };
70
+
71
+ if (options?.pageSize !== undefined) {
72
+ request.pageSize = options.pageSize;
73
+ }
74
+
75
+ if (options?.pageToken) {
76
+ request.pageToken = options.pageToken;
77
+ }
78
+
79
+ if (options?.filter) {
80
+ const filter = new UserSessionFilter();
81
+
82
+ if (options.filter.status) {
83
+ filter.status = options.filter.status;
84
+ }
85
+
86
+ if (options.filter.startTime) {
87
+ filter.startTime = Timestamp.fromDate(options.filter.startTime);
88
+ }
89
+
90
+ if (options.filter.endTime) {
91
+ filter.endTime = Timestamp.fromDate(options.filter.endTime);
92
+ }
93
+
94
+ request.filter = filter;
95
+ }
96
+
97
+ return this.coreClient.connectExec(
98
+ this.client.getUserSessions,
99
+ request
100
+ );
101
+ }
102
+
103
+ /**
104
+ * Revoke a session for a user
105
+ * @param {string} sessionId The session id to revoke
106
+ * @returns {Promise<RevokeSessionResponse>} The response with revoked session details
107
+ */
108
+ async revokeSession(sessionId: string): Promise<RevokeSessionResponse> {
109
+ return this.coreClient.connectExec(
110
+ this.client.revokeSession,
111
+ {
112
+ sessionId
113
+ }
114
+ );
115
+ }
116
+
117
+ /**
118
+ * Revoke all sessions for a user
119
+ * @param {string} userId The user id whose sessions should be revoked
120
+ * @returns {Promise<RevokeAllUserSessionsResponse>} The response with all revoked session details
121
+ */
122
+ async revokeAllUserSessions(userId: string): Promise<RevokeAllUserSessionsResponse> {
123
+ if (!userId) {
124
+ throw new Error('userId is required');
125
+ }
126
+
127
+ return this.coreClient.connectExec(
128
+ this.client.revokeAllUserSessions,
129
+ {
130
+ userId
131
+ }
132
+ );
133
+ }
134
+ }