@astralbeam/sdk 0.7.0 → 0.10.1

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/server.d.ts CHANGED
@@ -25,18 +25,32 @@ interface TenantUser {
25
25
  readonly admin?: boolean | undefined;
26
26
  readonly metadata?: JsonMetadata | undefined;
27
27
  }
28
- interface CreateChatAuthTokenOptions<TTenantUser extends TenantUser = TenantUser, TTenant extends Tenant = Tenant> {
28
+ interface CreateAstralBeamTokenOptions<TTenantUser extends TenantUser = TenantUser, TTenant extends Tenant = Tenant> {
29
29
  readonly apiKey: string;
30
30
  readonly user: TTenantUser;
31
31
  readonly tenant: TTenant;
32
32
  readonly expiresInSeconds?: number | undefined;
33
33
  }
34
+ interface CreateAstralBeamOrganizationTokenOptions {
35
+ readonly apiKey: string;
36
+ /** Host-authenticated email of an existing organization member, never browser-supplied identity. */
37
+ readonly email: string;
38
+ readonly organizationId: string;
39
+ readonly expiresInSeconds?: number | undefined;
40
+ }
41
+ /** Delegates a member's current database permissions. Does not restrict the API-key holder's authority. */
42
+ declare function createAstralBeamOrganizationToken({
43
+ apiKey,
44
+ email,
45
+ organizationId,
46
+ expiresInSeconds
47
+ }: CreateAstralBeamOrganizationTokenOptions): Promise<string>;
34
48
  /** Creates the short-lived bearer token returned by an application's server auth endpoint. */
35
- declare function createChatAuthToken<TTenantUser extends TenantUser = TenantUser, TTenant extends Tenant = Tenant>({
49
+ declare function createAstralBeamToken<TTenantUser extends TenantUser = TenantUser, TTenant extends Tenant = Tenant>({
36
50
  apiKey,
37
51
  user,
38
52
  tenant,
39
53
  expiresInSeconds
40
- }: CreateChatAuthTokenOptions<TTenantUser, TTenant>): Promise<string>;
54
+ }: CreateAstralBeamTokenOptions<TTenantUser, TTenant>): Promise<string>;
41
55
  //#endregion
42
- export { CHAT_AUTH_TOKEN_AUDIENCE, CHAT_AUTH_TOKEN_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_MAX_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_TYPE, CHAT_AUTH_TOKEN_VERSION, CreateChatAuthTokenOptions, JsonMetadata, JsonValue, Tenant, TenantUser, createChatAuthToken };
56
+ export { CHAT_AUTH_TOKEN_AUDIENCE, CHAT_AUTH_TOKEN_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_MAX_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_TYPE, CHAT_AUTH_TOKEN_VERSION, CreateAstralBeamOrganizationTokenOptions, CreateAstralBeamTokenOptions, JsonMetadata, JsonValue, Tenant, TenantUser, createAstralBeamOrganizationToken, createAstralBeamToken };
package/dist/server.js CHANGED
@@ -645,7 +645,7 @@ const CHAT_AUTH_TOKEN_MAX_LIFETIME_SECONDS = 600;
645
645
  const CHAT_AUTH_TOKEN_MAX_BYTES = 16384;
646
646
  const IDENTITY_MAX_BYTES = 8192;
647
647
  const EXTERNAL_ID_MAX_LENGTH = 255;
648
- const API_KEY_PATTERN = /^key_[0-9a-z-]{1,63}_[0-9a-z-]{1,63}_abo_[A-Za-z]{64}$/;
648
+ const API_KEY_PATTERN = /^key_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}_abo_[A-Za-z]{64}$/;
649
649
  const TENANT_FIELDS = [
650
650
  "id",
651
651
  "name",
@@ -659,13 +659,13 @@ const TENANT_USER_FIELDS = [
659
659
  ];
660
660
  const textEncoder = new TextEncoder();
661
661
  function parseApiKey(apiKey) {
662
- if (!API_KEY_PATTERN.test(apiKey)) throw new Error("apiKey must match key_<organization>_<key>_abo_<secret>");
662
+ if (!API_KEY_PATTERN.test(apiKey)) throw new Error("apiKey must match key_<organizationId>_<id>_abo_<secret>");
663
663
  const separator = apiKey.lastIndexOf("_abo_");
664
664
  const keyId = apiKey.slice(0, separator);
665
665
  const keySecret = apiKey.slice(separator + 1);
666
666
  return {
667
667
  keyId,
668
- organizationSlug: keyId.slice(4, keyId.indexOf("_", 4)),
668
+ organizationId: keyId.slice(4, keyId.indexOf("_", 4)),
669
669
  keySecret
670
670
  };
671
671
  }
@@ -704,10 +704,27 @@ async function signingKey(secret) {
704
704
  const digest = await crypto.subtle.digest("SHA-256", textEncoder.encode(secret));
705
705
  return textEncoder.encode(encode(new Uint8Array(digest)));
706
706
  }
707
+ /** Delegates a member's current database permissions. Does not restrict the API-key holder's authority. */
708
+ async function createAstralBeamOrganizationToken({ apiKey, email, organizationId, expiresInSeconds = 300 }) {
709
+ if (typeof email !== "string" || email.length > 320 || email.includes("\0") || !/^[^\s@]+@[^\s@]+$/.test(email)) throw new Error("email must be an email address of at most 320 characters");
710
+ if (!Number.isInteger(expiresInSeconds) || expiresInSeconds < 60 || expiresInSeconds > 600) throw new Error("organization auth tokens must live for 60-600 seconds");
711
+ const { keyId, organizationId: keyOrganizationId, keySecret } = parseApiKey(apiKey);
712
+ if (organizationId !== keyOrganizationId) throw new Error("organizationId must match the API key organization");
713
+ const now = Math.floor(Date.now() / 1e3);
714
+ return await new SignJWT({
715
+ ver: 1,
716
+ email,
717
+ organization_id: organizationId
718
+ }).setProtectedHeader({
719
+ alg: "HS256",
720
+ typ: "astralbeam-organization+jwt",
721
+ kid: keyId
722
+ }).setIssuer(organizationId).setAudience(CHAT_AUTH_TOKEN_AUDIENCE).setIssuedAt(now).setExpirationTime(now + expiresInSeconds).sign(await signingKey(keySecret));
723
+ }
707
724
  /** Creates the short-lived bearer token returned by an application's server auth endpoint. */
708
- async function createChatAuthToken({ apiKey, user, tenant, expiresInSeconds = 300 }) {
725
+ async function createAstralBeamToken({ apiKey, user, tenant, expiresInSeconds = 300 }) {
709
726
  if (!Number.isInteger(expiresInSeconds) || expiresInSeconds < 60 || expiresInSeconds > 600) throw new Error("chat auth tokens must live for 60-600 seconds");
710
- const { keyId, organizationSlug, keySecret } = parseApiKey(apiKey);
727
+ const { keyId, organizationId, keySecret } = parseApiKey(apiKey);
711
728
  const identity = validatedIdentity(user, tenant);
712
729
  const now = Math.floor(Date.now() / 1e3);
713
730
  const token = await new SignJWT({
@@ -718,9 +735,9 @@ async function createChatAuthToken({ apiKey, user, tenant, expiresInSeconds = 30
718
735
  alg: "HS256",
719
736
  typ: CHAT_AUTH_TOKEN_TYPE,
720
737
  kid: keyId
721
- }).setIssuer(organizationSlug).setAudience(CHAT_AUTH_TOKEN_AUDIENCE).setIssuedAt(now).setExpirationTime(now + expiresInSeconds).sign(await signingKey(keySecret));
738
+ }).setIssuer(organizationId).setAudience(CHAT_AUTH_TOKEN_AUDIENCE).setIssuedAt(now).setExpirationTime(now + expiresInSeconds).sign(await signingKey(keySecret));
722
739
  if (textEncoder.encode(token).byteLength > CHAT_AUTH_TOKEN_MAX_BYTES) throw new Error(`chat auth tokens must not exceed ${CHAT_AUTH_TOKEN_MAX_BYTES} bytes`);
723
740
  return token;
724
741
  }
725
742
  //#endregion
726
- export { CHAT_AUTH_TOKEN_AUDIENCE, CHAT_AUTH_TOKEN_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_MAX_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_TYPE, CHAT_AUTH_TOKEN_VERSION, createChatAuthToken };
743
+ export { CHAT_AUTH_TOKEN_AUDIENCE, CHAT_AUTH_TOKEN_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_MAX_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_TYPE, CHAT_AUTH_TOKEN_VERSION, createAstralBeamOrganizationToken, createAstralBeamToken };