{"version":3,"sources":["../src/WebhookReceiver.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { BinaryReadOptions, JsonReadOptions, JsonValue } from '@bufbuild/protobuf';\nimport { WebhookEvent as ProtoWebhookEvent } from '@livekit/protocol';\nimport { TokenVerifier } from './AccessToken.js';\nimport { digest } from './crypto/digest.js';\n\nexport const authorizeHeader = 'Authorize';\n\nexport class WebhookEvent extends ProtoWebhookEvent {\n  event: WebhookEventNames = '';\n\n  static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): WebhookEvent {\n    return new WebhookEvent().fromBinary(bytes, options);\n  }\n\n  static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): WebhookEvent {\n    return new WebhookEvent().fromJson(jsonValue, options);\n  }\n\n  static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): WebhookEvent {\n    return new WebhookEvent().fromJsonString(jsonString, options);\n  }\n}\n\nexport type WebhookEventNames =\n  | 'room_started'\n  | 'room_finished'\n  | 'participant_joined'\n  | 'participant_left'\n  | 'participant_connection_aborted'\n  | 'track_published'\n  | 'track_unpublished'\n  | 'egress_started'\n  | 'egress_updated'\n  | 'egress_ended'\n  | 'ingress_started'\n  | 'ingress_ended'\n  /**\n   * @internal\n   * @remarks only used as a default value, not a valid webhook event\n   */\n  | '';\n\nexport class WebhookReceiver {\n  private verifier: TokenVerifier;\n\n  constructor(apiKey: string, apiSecret: string) {\n    this.verifier = new TokenVerifier(apiKey, apiSecret);\n  }\n\n  /**\n   * @param body - string of the posted body\n   * @param authHeader - `Authorization` header from the request\n   * @param skipAuth - true to skip auth validation\n   * @param clockTolerance - How much tolerance to allow for checks against the auth header to be skewed from the claims\n   * @returns The processed webhook event\n   */\n  async receive(\n    body: string,\n    authHeader?: string,\n    skipAuth: boolean = false,\n    clockTolerance?: string | number,\n  ): Promise<WebhookEvent> {\n    // verify token\n    if (!skipAuth) {\n      if (!authHeader) {\n        throw new Error('authorization header is empty');\n      }\n      const claims = await this.verifier.verify(authHeader, clockTolerance);\n      // confirm sha\n      const hash = await digest(body);\n      const hashDecoded = btoa(\n        Array.from(new Uint8Array(hash))\n          .map((v) => String.fromCharCode(v))\n          .join(''),\n      );\n\n      if (claims.sha256 !== hashDecoded) {\n        throw new Error('sha256 checksum of body does not match');\n      }\n    }\n\n    return WebhookEvent.fromJson(JSON.parse(body), { ignoreUnknownFields: true });\n  }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,sBAAkD;AAClD,yBAA8B;AAC9B,oBAAuB;AAEhB,MAAM,kBAAkB;AAExB,MAAM,qBAAqB,gBAAAA,aAAkB;AAAA,EAA7C;AAAA;AACL,iBAA2B;AAAA;AAAA,EAE3B,OAAO,WAAW,OAAmB,SAAoD;AACvF,WAAO,IAAI,aAAa,EAAE,WAAW,OAAO,OAAO;AAAA,EACrD;AAAA,EAEA,OAAO,SAAS,WAAsB,SAAkD;AACtF,WAAO,IAAI,aAAa,EAAE,SAAS,WAAW,OAAO;AAAA,EACvD;AAAA,EAEA,OAAO,eAAe,YAAoB,SAAkD;AAC1F,WAAO,IAAI,aAAa,EAAE,eAAe,YAAY,OAAO;AAAA,EAC9D;AACF;AAqBO,MAAM,gBAAgB;AAAA,EAG3B,YAAY,QAAgB,WAAmB;AAC7C,SAAK,WAAW,IAAI,iCAAc,QAAQ,SAAS;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,QACJ,MACA,YACA,WAAoB,OACpB,gBACuB;AAEvB,QAAI,CAAC,UAAU;AACb,UAAI,CAAC,YAAY;AACf,cAAM,IAAI,MAAM,+BAA+B;AAAA,MACjD;AACA,YAAM,SAAS,MAAM,KAAK,SAAS,OAAO,YAAY,cAAc;AAEpE,YAAM,OAAO,UAAM,sBAAO,IAAI;AAC9B,YAAM,cAAc;AAAA,QAClB,MAAM,KAAK,IAAI,WAAW,IAAI,CAAC,EAC5B,IAAI,CAAC,MAAM,OAAO,aAAa,CAAC,CAAC,EACjC,KAAK,EAAE;AAAA,MACZ;AAEA,UAAI,OAAO,WAAW,aAAa;AACjC,cAAM,IAAI,MAAM,wCAAwC;AAAA,MAC1D;AAAA,IACF;AAEA,WAAO,aAAa,SAAS,KAAK,MAAM,IAAI,GAAG,EAAE,qBAAqB,KAAK,CAAC;AAAA,EAC9E;AACF;","names":["ProtoWebhookEvent"]}