@atlasauth/backend 0.9.0 → 0.10.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.
- package/CHANGELOG.md +17 -0
- package/README.md +2 -0
- package/dist/client/branding.d.ts +20 -0
- package/dist/client/branding.js.map +1 -1
- package/dist/client/diagnostics.d.ts +16 -0
- package/dist/client/diagnostics.js.map +1 -1
- package/dist/client/error.d.ts +10 -0
- package/dist/client/error.js +10 -0
- package/dist/client/error.js.map +1 -1
- package/dist/client/index.d.ts +7 -1
- package/dist/client/instance-security.d.ts +39 -2
- package/dist/client/instance-security.js +12 -0
- package/dist/client/instance-security.js.map +1 -1
- package/dist/client/instance.d.ts +21 -2
- package/dist/client/instance.js.map +1 -1
- package/dist/client/messaging.d.ts +1 -1
- package/dist/client/platform.d.ts +61 -15
- package/dist/client/platform.js +23 -7
- package/dist/client/platform.js.map +1 -1
- package/dist/client/webhooks.d.ts +25 -2
- package/dist/client/webhooks.js +7 -3
- package/dist/client/webhooks.js.map +1 -1
- package/dist/webhooks.js +32 -7
- package/dist/webhooks.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @atlasauth/backend changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.0
|
|
4
|
+
- **Trusted reverse proxy secret.** `instanceSecurity.getProxySecret()` (`ProxySecret`: `set`, `created_at`, `previous_expires_at`, `secret_header`, `client_ip_header`), `instanceSecurity.createProxySecret({ overlap_seconds? })` (`ProxySecretCreated`: adds `secret`, shown once, and `rotated`; `CreateProxySecretBody`) and `instanceSecurity.deleteProxySecret()`. A proxy that serves the Frontend API from your own origin sends the secret in `X-Atlas-Proxy-Secret` and the browser's IP in `X-Atlas-Client-IP`; Atlas then uses that IP for rate limits, lockout, risk signals, geo, audit logs and session metadata.
|
|
5
|
+
- **Managed email feedback on Resend.** `ManagedEmail.feedback_webhook` is now also returned for Resend (on create and verify): Atlas registers a bounce / complaint webhook on your own Resend account, so `usage.bounces` / `usage.complaints` count for Resend domains too.
|
|
6
|
+
- **Webhook secret rotation with an overlap.** `webhooks.endpoints.rotateSecret(id, { overlap_seconds? })` (new type `RotateWebhookSecretBody`) can keep the previous signing secret signing every delivery for `overlap_seconds` (at most 604800, 7 days). The default is 0: the old secret stops at once, as before. Set an overlap only once every receiver verifies with a verifier that accepts several signatures. During the overlap the `atlas-signature` header carries two entries, `v1,<new> v1,<old>`; `verifyWebhook` (0.9.1 and later) passes with either secret. `WebhookSigningSecret` gains `overlap_seconds` and `previous_secret_expires_at` (epoch ms, or null when the old secret already stopped). `WebhookEndpoint` gains `previous_secret_expires_at` while an overlap is open.
|
|
7
|
+
- **Sign in with Atlas apps.** `atlasIdApps.rotateSecret(id, { overlap_seconds? })` (default 24 hours, `0` = now) and `atlasIdApps.rotateWebhookSecret(id, { overlap_seconds? })` (default 0, as for instance webhooks); new type `RotateAtlasIdAppSecretBody`. `AtlasIdApp` gains `previous_secret_expires_at`, `secret_issued_at` (the real time the current client secret was issued) and `webhook_previous_secret_expires_at`; `AtlasIdAppWithSecret` gains `overlap_seconds`.
|
|
8
|
+
- **Per-instance platform rotation with an overlap.** `apps.instances.rotateSecret(appId, instanceId, { overlap_seconds? }, idempotencyKey?)` keeps the prior keys valid like `apps.rotateSecret` (default 259200 = 72 hours, `0` = revoke now). The 0.9 call `rotateSecret(appId, instanceId, idempotencyKey)` still works. `PlatformInstanceSecretRotation` now types what the API returns: `app_id`, `instance_id`, `overlap_seconds`, `previous_keys_expire_at` (`id` is deprecated: it was never sent). New type `RotatePlatformSecretBody`, also used by `apps.rotateSecret`.
|
|
9
|
+
- **Behaviour change on the server.** A per-instance platform rotation without `overlap_seconds` now keeps the prior keys for 72 hours (they used to be revoked at once). Pass `overlap_seconds: 0` for the old behaviour. Webhook rotations without it behave as before.
|
|
10
|
+
- **Compatible.** New methods, optional parameters, fields and types only.
|
|
11
|
+
|
|
12
|
+
## 0.9.1
|
|
13
|
+
- **Allowed origins with an integration.** `UpdateInstanceBody.allowed_origins` and the platform app, instance and accountless-app bodies accept `AllowedOriginInput` entries: an origin string or `{ origin, integration }` (`OriginIntegration`: `redirect` | `embedded` | `auto`, or `null` to clear). `Instance`, `InstanceUpdateResult` and `PlatformApp` gain optional `allowed_origin_integrations`. New types: `AllowedOriginEntry`, `AllowedOriginInput`, `OriginIntegration`.
|
|
14
|
+
- **Login readiness.** `LoginReadiness` gains `integration` (`LoginReadinessIntegration`: `assumed`, `source`, `reason`); `LoginReadinessCheck` gains `applies`.
|
|
15
|
+
- **Branding audiences.** `Branding` accepts `audiences` (hint → `BrandingAudience` `{ heading, subheading, logoUrl, primaryColor }`, `null` deletes one); `ResolvedBranding` gains `audiences`. Start a hosted sign-in with `audienceHint` (or `audience_hint` on the URL) to use one.
|
|
16
|
+
- **Error codes.** `AtlasErrorCodes` adds `READ_ONLY_FIELD`, `DEVELOPMENT_EMAIL_NOT_ALLOWED`, `DEVELOPMENT_EMAIL_UNAVAILABLE`, `DEVELOPMENT_EMAIL_LIMIT_REACHED` and `DEVELOPMENT_EMAIL_TEMPLATE_NOT_ALLOWED`.
|
|
17
|
+
- **Webhooks, several signatures.** `verifyWebhook` accepts an `atlas-signature` header with several space-separated `v1,<signature>` entries and verifies when any one matches (each compared in constant time). This prepares for webhook secret rotation with an overlap. A header with a single signature works as before.
|
|
18
|
+
- **Compatible.** New types, optional fields and error codes only; a single-signature webhook header verifies as before.
|
|
19
|
+
|
|
3
20
|
## 0.9.0
|
|
4
21
|
- **OIDC relying party.** New `oidc` namespace and standalone functions for the hosted-redirect sign-in: `oidcStart` (`oidc.start`), `oidcCallback` (`oidc.callback`), `oidcLogoutUrl` (`oidc.logoutUrl`), `oidcVerifyIdToken` (`oidc.verifyIdToken`), `oidcClearCookie` (`oidc.clearCookie`). `start({ issuer, clientId, redirectUri, scope?, prompt?, loginHint?, audienceHint?, extraParams?, cookieName?, secret? })` returns `{ url, cookie, state, nonce }`: a PKCE (S256) authorize URL from discovery and a `Set-Cookie` for a 10-minute, httpOnly, `SameSite=Lax`, callback-path flow cookie, AES-GCM sealed when `secret` is set. `callback({ …, clientSecret?, clientAuth?: 'client_secret_basic' | 'client_secret_post' | 'none', query, cookie, secret?, clockToleranceSec? })` returns `OidcIdentity` (`sub`, `namespacedSub` = `${issuer}#${sub}`, `email`, `emailVerified`, `name`, `idToken`, `accessToken`, `refreshToken?`, `expiresAt` in epoch ms, `claims`, `clearCookie`). The ID token is verified with RS256 pinned, exact `iss`, `aud` containing the client id (and `azp` when present or when there are several audiences), `nonce`, `exp`/`iat` with tolerance, and `at_hash`. Discovery is cached per issuer for an hour.
|
|
5
22
|
- **Errors.** `AtlasOidcError` with `code` (`AtlasOidcErrorCode`: `state_mismatch`, `issuer_mismatch`, `invalid_request`, `invalid_token`, `token_exchange_failed`, `access_denied`, `discovery_failed`), `oauthError`, `description`, `status`, `clearCookie` and `cause`.
|
package/README.md
CHANGED
|
@@ -399,3 +399,5 @@ To check the route end to end, `oauthClients.testBackchannelLogout(id)` sends a
|
|
|
399
399
|
## Webhooks
|
|
400
400
|
|
|
401
401
|
`verifyWebhook` checks the signature on events Atlas sends to your webhook endpoints. You manage endpoints with `atlas.webhooks.endpoints`: create, update, reveal or rotate a secret, send a test event, and redeliver a past delivery.
|
|
402
|
+
|
|
403
|
+
The `atlas-signature` header can carry more than one signature, separated by spaces (`v1,<sig> v1,<sig>`). While an endpoint secret rotates, Atlas may sign each delivery with the old and the new secret. `verifyWebhook` accepts the delivery when any one signature matches your secret.
|
|
@@ -51,6 +51,13 @@ export interface Branding {
|
|
|
51
51
|
};
|
|
52
52
|
/** How a verification code is entered — segmented `boxes` (default) or `single`. */
|
|
53
53
|
codeInput?: 'boxes' | 'single';
|
|
54
|
+
/**
|
|
55
|
+
* Per-audience branding for the hosted pages, keyed by audience hint
|
|
56
|
+
* (`[a-z0-9_-]{1,32}`, at most 20). A sign-in started with
|
|
57
|
+
* `audience_hint=<hint>` uses that entry. An update merges per hint: an entry
|
|
58
|
+
* replaces that hint, `null` deletes it, `audiences: null` removes them all.
|
|
59
|
+
*/
|
|
60
|
+
audiences?: Record<string, BrandingAudience | null> | null;
|
|
54
61
|
card?: {
|
|
55
62
|
width?: 'narrow' | 'default' | 'wide';
|
|
56
63
|
align?: 'left' | 'center';
|
|
@@ -108,6 +115,17 @@ export interface HostedBackground {
|
|
|
108
115
|
angle?: number;
|
|
109
116
|
imageUrl?: string;
|
|
110
117
|
}
|
|
118
|
+
/** The hosted-page branding for one audience hint. */
|
|
119
|
+
export interface BrandingAudience {
|
|
120
|
+
/** Replaces the card heading (up to 120 characters). */
|
|
121
|
+
heading?: string;
|
|
122
|
+
/** Replaces the line under the heading (up to 240 characters). */
|
|
123
|
+
subheading?: string;
|
|
124
|
+
/** Replaces the logo (https). */
|
|
125
|
+
logoUrl?: string;
|
|
126
|
+
/** Replaces the primary colour (hex, rgb() or a CSS colour name). */
|
|
127
|
+
primaryColor?: string;
|
|
128
|
+
}
|
|
111
129
|
/** What the renderers actually use — everything already escaped or dropped. */
|
|
112
130
|
export interface ResolvedBranding {
|
|
113
131
|
applicationName: string;
|
|
@@ -137,6 +155,8 @@ export interface ResolvedBranding {
|
|
|
137
155
|
providerHidden?: string[];
|
|
138
156
|
socialButtons?: Record<string, unknown>;
|
|
139
157
|
codeInput?: 'boxes' | 'single';
|
|
158
|
+
/** Every audience the hosted page can theme for, as it reads them (invalid values dropped). */
|
|
159
|
+
audiences?: Record<string, BrandingAudience>;
|
|
140
160
|
card?: {
|
|
141
161
|
width: string;
|
|
142
162
|
align: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branding.js","sourceRoot":"","sources":["../../src/client/branding.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"branding.js","sourceRoot":"","sources":["../../src/client/branding.ts"],"names":[],"mappings":";;AAuMA,4CAcC;AAdD,SAAgB,gBAAgB,CAAC,OAAkB;IACjD,OAAO;QACL,GAAG;YACD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,kEAAkE;QAClE,MAAM,CAAC,IAAuB;YAC5B,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,CAAC;QACD,0EAA0E;QAC1E,OAAO,CAAC,IAAuB;YAC7B,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,EAAE,IAAI,EAAE,CAAC,CAAC;QACzE,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -8,6 +8,11 @@ export interface LoginReadinessParams {
|
|
|
8
8
|
export interface LoginReadinessCheck {
|
|
9
9
|
name: string;
|
|
10
10
|
pass: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* `false` when the check does not matter for the assumed integration (it
|
|
13
|
+
* then never makes `ready` false). Absent from older servers.
|
|
14
|
+
*/
|
|
15
|
+
applies?: boolean;
|
|
11
16
|
observed: unknown;
|
|
12
17
|
/** What was seen, and the fix when it fails. */
|
|
13
18
|
detail: string;
|
|
@@ -17,9 +22,20 @@ export interface LoginReadiness {
|
|
|
17
22
|
origin: string;
|
|
18
23
|
live: boolean;
|
|
19
24
|
data_region?: string | null;
|
|
25
|
+
/** Every check that applies to the assumed integration passes. */
|
|
20
26
|
ready: boolean;
|
|
27
|
+
/** The integration the checks assumed for this origin, and why. */
|
|
28
|
+
integration?: LoginReadinessIntegration;
|
|
21
29
|
checks: LoginReadinessCheck[];
|
|
22
30
|
}
|
|
31
|
+
export interface LoginReadinessIntegration {
|
|
32
|
+
/** `redirect`: hosted-redirect OIDC, so the `cors` and `session_cookie` checks do not apply. */
|
|
33
|
+
assumed: 'redirect' | 'embedded';
|
|
34
|
+
/** `declared` | `embedded_traffic` | `redirect_clients` | `default`. */
|
|
35
|
+
source: 'declared' | 'embedded_traffic' | 'redirect_clients' | 'default';
|
|
36
|
+
/** A plain sentence saying why. */
|
|
37
|
+
reason: string;
|
|
38
|
+
}
|
|
23
39
|
export interface RequestInspectorParams {
|
|
24
40
|
/** The browser origin making the request. */
|
|
25
41
|
origin: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/client/diagnostics.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/client/diagnostics.ts"],"names":[],"mappings":";;AAuEA,kDAmBC;AAnBD,SAAgB,mBAAmB,CAAC,OAAkB;IACpD,OAAO;QACL,yGAAyG;QACzG,cAAc,CAAC,MAA4B;YACzC,OAAO,OAAO,CAAC;gBACb,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,iCAAiC;gBACvC,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE;aACpD,CAAC,CAAC;QACL,CAAC;QACD,uFAAuF;QACvF,gBAAgB,CAAC,MAA8B;YAC7C,OAAO,OAAO,CAAC;gBACb,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,mCAAmC;gBACzC,KAAK,EAAE,EAAE,GAAG,MAAM,EAAE;aACrB,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/client/error.d.ts
CHANGED
|
@@ -47,6 +47,16 @@ export declare const AtlasErrorCodes: {
|
|
|
47
47
|
readonly CAPTCHA_IN_USE: "CAPTCHA_IN_USE";
|
|
48
48
|
/** Strict mode: the body had a field this endpoint does not accept (`param` names it). */
|
|
49
49
|
readonly UNKNOWN_FIELD: "UNKNOWN_FIELD";
|
|
50
|
+
/** 422 — the body set a field that is not writable on this endpoint (`param` names it; the message names the call that changes it). */
|
|
51
|
+
readonly READ_ONLY_FIELD: "READ_ONLY_FIELD";
|
|
52
|
+
/** 409 — the development email sender is not available on a production instance. */
|
|
53
|
+
readonly DEVELOPMENT_EMAIL_NOT_ALLOWED: "DEVELOPMENT_EMAIL_NOT_ALLOWED";
|
|
54
|
+
/** 503 — the development email sender is not available on this deployment. */
|
|
55
|
+
readonly DEVELOPMENT_EMAIL_UNAVAILABLE: "DEVELOPMENT_EMAIL_UNAVAILABLE";
|
|
56
|
+
/** 429 — the development sender's daily or per-recipient hourly cap is spent (`meta.window`, `meta.retry_after_seconds`). */
|
|
57
|
+
readonly DEVELOPMENT_EMAIL_LIMIT_REACHED: "DEVELOPMENT_EMAIL_LIMIT_REACHED";
|
|
58
|
+
/** 422 — the development sender sends only Atlas's own sign-in emails, never custom templates. */
|
|
59
|
+
readonly DEVELOPMENT_EMAIL_TEMPLATE_NOT_ALLOWED: "DEVELOPMENT_EMAIL_TEMPLATE_NOT_ALLOWED";
|
|
50
60
|
/** The billing plan still has subscribers and cannot be removed. */
|
|
51
61
|
readonly PLAN_IN_USE: "PLAN_IN_USE";
|
|
52
62
|
/** The key is valid but for another API surface (e.g. a platform key on the Backend API). */
|
package/dist/client/error.js
CHANGED
|
@@ -43,6 +43,16 @@ exports.AtlasErrorCodes = {
|
|
|
43
43
|
CAPTCHA_IN_USE: 'CAPTCHA_IN_USE',
|
|
44
44
|
/** Strict mode: the body had a field this endpoint does not accept (`param` names it). */
|
|
45
45
|
UNKNOWN_FIELD: 'UNKNOWN_FIELD',
|
|
46
|
+
/** 422 — the body set a field that is not writable on this endpoint (`param` names it; the message names the call that changes it). */
|
|
47
|
+
READ_ONLY_FIELD: 'READ_ONLY_FIELD',
|
|
48
|
+
/** 409 — the development email sender is not available on a production instance. */
|
|
49
|
+
DEVELOPMENT_EMAIL_NOT_ALLOWED: 'DEVELOPMENT_EMAIL_NOT_ALLOWED',
|
|
50
|
+
/** 503 — the development email sender is not available on this deployment. */
|
|
51
|
+
DEVELOPMENT_EMAIL_UNAVAILABLE: 'DEVELOPMENT_EMAIL_UNAVAILABLE',
|
|
52
|
+
/** 429 — the development sender's daily or per-recipient hourly cap is spent (`meta.window`, `meta.retry_after_seconds`). */
|
|
53
|
+
DEVELOPMENT_EMAIL_LIMIT_REACHED: 'DEVELOPMENT_EMAIL_LIMIT_REACHED',
|
|
54
|
+
/** 422 — the development sender sends only Atlas's own sign-in emails, never custom templates. */
|
|
55
|
+
DEVELOPMENT_EMAIL_TEMPLATE_NOT_ALLOWED: 'DEVELOPMENT_EMAIL_TEMPLATE_NOT_ALLOWED',
|
|
46
56
|
/** The billing plan still has subscribers and cannot be removed. */
|
|
47
57
|
PLAN_IN_USE: 'PLAN_IN_USE',
|
|
48
58
|
/** The key is valid but for another API surface (e.g. a platform key on the Backend API). */
|
package/dist/client/error.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/client/error.ts"],"names":[],"mappings":";;;AAoBA,MAAa,aAAc,SAAQ,KAAK;IACpB,IAAI,GAAG,eAAe,CAAC;IACzC,0CAA0C;IACjC,MAAM,CAAS;IACxB,8CAA8C;IACrC,MAAM,CAAmB;IAElC,YAAY,MAAc,EAAE,MAAwB,EAAE,OAAgB;QACpE,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,wCAAwC,MAAM,EAAE,CAAC,CAAC;QACzF,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,uEAAuE;QACvE,sEAAsE;QACtE,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IACvD,CAAC;IAED,uEAAuE;IACvE,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;IAC9B,CAAC;IAED,yEAAyE;IACzE,OAAO,CAAC,IAAY;QAClB,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAClD,CAAC;CACF;AAzBD,sCAyBC;AAED;;;;GAIG;AACU,QAAA,eAAe,GAAG;IAC7B,+EAA+E;IAC/E,6BAA6B,EAAE,+BAA+B;IAC9D,mEAAmE;IACnE,uBAAuB,EAAE,yBAAyB;IAClD,oGAAoG;IACpG,qBAAqB,EAAE,uBAAuB;IAC9C,oGAAoG;IACpG,qBAAqB,EAAE,uBAAuB;IAC9C,wGAAwG;IACxG,cAAc,EAAE,gBAAgB;IAChC,0FAA0F;IAC1F,aAAa,EAAE,eAAe;IAC9B,oEAAoE;IACpE,WAAW,EAAE,aAAa;IAC1B,6FAA6F;IAC7F,iBAAiB,EAAE,mBAAmB;IACtC,gDAAgD;IAChD,WAAW,EAAE,aAAa;IAC1B,SAAS,EAAE,WAAW;IACtB,iBAAiB,EAAE,mBAAmB;IACtC,YAAY,EAAE,cAAc;IAC5B,4FAA4F;IAC5F,oBAAoB,EAAE,sBAAsB;IAC5C,yFAAyF;IACzF,cAAc,EAAE,gBAAgB;IAChC,eAAe,EAAE,iBAAiB;IAClC,oBAAoB,EAAE,sBAAsB;IAC5C,eAAe,EAAE,iBAAiB;IAClC,aAAa,EAAE,eAAe;IAC9B,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,UAAU;CACZ,CAAC"}
|
|
1
|
+
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/client/error.ts"],"names":[],"mappings":";;;AAoBA,MAAa,aAAc,SAAQ,KAAK;IACpB,IAAI,GAAG,eAAe,CAAC;IACzC,0CAA0C;IACjC,MAAM,CAAS;IACxB,8CAA8C;IACrC,MAAM,CAAmB;IAElC,YAAY,MAAc,EAAE,MAAwB,EAAE,OAAgB;QACpE,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,wCAAwC,MAAM,EAAE,CAAC,CAAC;QACzF,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,uEAAuE;QACvE,sEAAsE;QACtE,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IACvD,CAAC;IAED,uEAAuE;IACvE,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;IAC9B,CAAC;IAED,yEAAyE;IACzE,OAAO,CAAC,IAAY;QAClB,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAClD,CAAC;CACF;AAzBD,sCAyBC;AAED;;;;GAIG;AACU,QAAA,eAAe,GAAG;IAC7B,+EAA+E;IAC/E,6BAA6B,EAAE,+BAA+B;IAC9D,mEAAmE;IACnE,uBAAuB,EAAE,yBAAyB;IAClD,oGAAoG;IACpG,qBAAqB,EAAE,uBAAuB;IAC9C,oGAAoG;IACpG,qBAAqB,EAAE,uBAAuB;IAC9C,wGAAwG;IACxG,cAAc,EAAE,gBAAgB;IAChC,0FAA0F;IAC1F,aAAa,EAAE,eAAe;IAC9B,uIAAuI;IACvI,eAAe,EAAE,iBAAiB;IAClC,oFAAoF;IACpF,6BAA6B,EAAE,+BAA+B;IAC9D,8EAA8E;IAC9E,6BAA6B,EAAE,+BAA+B;IAC9D,6HAA6H;IAC7H,+BAA+B,EAAE,iCAAiC;IAClE,kGAAkG;IAClG,sCAAsC,EAAE,wCAAwC;IAChF,oEAAoE;IACpE,WAAW,EAAE,aAAa;IAC1B,6FAA6F;IAC7F,iBAAiB,EAAE,mBAAmB;IACtC,gDAAgD;IAChD,WAAW,EAAE,aAAa;IAC1B,SAAS,EAAE,WAAW;IACtB,iBAAiB,EAAE,mBAAmB;IACtC,YAAY,EAAE,cAAc;IAC5B,4FAA4F;IAC5F,oBAAoB,EAAE,sBAAsB;IAC5C,yFAAyF;IACzF,cAAc,EAAE,gBAAgB;IAChC,eAAe,EAAE,iBAAiB;IAClC,oBAAoB,EAAE,sBAAsB;IAC5C,eAAe,EAAE,iBAAiB;IAClC,aAAa,EAAE,eAAe;IAC9B,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,UAAU;CACZ,CAAC"}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -479,7 +479,7 @@ declare function buildNamespaces(request: RequestFn): {
|
|
|
479
479
|
list(): Promise<import("./webhooks").WebhookEndpointList>;
|
|
480
480
|
update(id: string, body: import("./webhooks").UpdateWebhookEndpointBody): Promise<import("./webhooks").WebhookEndpoint>;
|
|
481
481
|
revealSecret(id: string): Promise<import("./webhooks").WebhookSigningSecret>;
|
|
482
|
-
rotateSecret(id: string): Promise<import("./webhooks").WebhookSigningSecret>;
|
|
482
|
+
rotateSecret(id: string, body?: import("./webhooks").RotateWebhookSecretBody): Promise<import("./webhooks").WebhookSigningSecret>;
|
|
483
483
|
test(id: string): Promise<import("./webhooks").WebhookDeliveryQueued>;
|
|
484
484
|
redeliver(id: string, deliveryId: string): Promise<import("./webhooks").WebhookDeliveryQueued>;
|
|
485
485
|
create(body: import("./webhooks").CreateWebhookEndpointBody, idempotencyKey?: string): Promise<import("./webhooks").WebhookEndpointWithSecret>;
|
|
@@ -951,6 +951,12 @@ declare function buildNamespaces(request: RequestFn): {
|
|
|
951
951
|
object: "kerberos_secret";
|
|
952
952
|
deleted: true;
|
|
953
953
|
}>;
|
|
954
|
+
getProxySecret(): Promise<import("./instance-security").ProxySecret>;
|
|
955
|
+
createProxySecret(body?: import("./instance-security").CreateProxySecretBody): Promise<import("./instance-security").ProxySecretCreated>;
|
|
956
|
+
deleteProxySecret(): Promise<{
|
|
957
|
+
object: "proxy_secret";
|
|
958
|
+
deleted: true;
|
|
959
|
+
}>;
|
|
954
960
|
setLdapBindPassword(connectionId: string, body: {
|
|
955
961
|
bind_password: string;
|
|
956
962
|
}): Promise<{
|
|
@@ -3,8 +3,8 @@ import { type RequestFn } from './request';
|
|
|
3
3
|
* §5/§11.2 instance security config — the agent mirror of the dashboard security
|
|
4
4
|
* screen. Settings that live OUTSIDE `auth_config` (so a raw `PATCH /v1/instance`
|
|
5
5
|
* cannot reach them): the per-flow kill switches, the customer IP allowlist, and
|
|
6
|
-
* the write-only provider secrets (captcha, Kerberos proxy, LDAP bind password)
|
|
7
|
-
* No secret is ever readable back.
|
|
6
|
+
* the write-only provider secrets (captcha, Kerberos proxy, LDAP bind password),
|
|
7
|
+
* and the trusted reverse proxy secret. No secret is ever readable back.
|
|
8
8
|
*/
|
|
9
9
|
export interface InstanceKillSwitches {
|
|
10
10
|
/** Refuse sign-ups for this instance only. */
|
|
@@ -31,6 +31,34 @@ export interface UpdateInstanceSecurityBody {
|
|
|
31
31
|
*/
|
|
32
32
|
confirm_lockout?: boolean;
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* The trusted reverse proxy secret: with it, a proxy that serves the Frontend
|
|
36
|
+
* API from your own origin sends `X-Atlas-Proxy-Secret` + `X-Atlas-Client-IP`,
|
|
37
|
+
* and Atlas uses the browser's IP for rate limits, lockout, risk signals, geo,
|
|
38
|
+
* audit logs and session metadata. Never returned after creation.
|
|
39
|
+
*/
|
|
40
|
+
export interface ProxySecret {
|
|
41
|
+
object: 'proxy_secret';
|
|
42
|
+
/** Whether a proxy secret is set. */
|
|
43
|
+
set: boolean;
|
|
44
|
+
/** When the current secret was created (epoch ms), or null. */
|
|
45
|
+
created_at: number | null;
|
|
46
|
+
/** After a rotation: when the replaced secret stops working (epoch ms), or null. */
|
|
47
|
+
previous_expires_at: number | null;
|
|
48
|
+
secret_header: 'X-Atlas-Proxy-Secret';
|
|
49
|
+
client_ip_header: 'X-Atlas-Client-IP';
|
|
50
|
+
}
|
|
51
|
+
/** Create / rotate result: the secret, shown only here. */
|
|
52
|
+
export interface ProxySecretCreated extends ProxySecret {
|
|
53
|
+
/** `aps_…`. Store it in your proxy's environment; Atlas keeps only a hash. */
|
|
54
|
+
secret: string;
|
|
55
|
+
/** true when an existing secret was replaced. */
|
|
56
|
+
rotated: boolean;
|
|
57
|
+
}
|
|
58
|
+
export interface CreateProxySecretBody {
|
|
59
|
+
/** On rotation: seconds the replaced secret keeps working. Default 86400, 0 = stop now, at most 604800. */
|
|
60
|
+
overlap_seconds?: number | null;
|
|
61
|
+
}
|
|
34
62
|
/** A confirmed self-lockout returns the config plus a plain-language `warning`. */
|
|
35
63
|
export interface InstanceSecurityResult extends InstanceSecurity {
|
|
36
64
|
warning?: string;
|
|
@@ -62,6 +90,15 @@ export declare function instanceSecurityResource(request: RequestFn): {
|
|
|
62
90
|
object: "kerberos_secret";
|
|
63
91
|
deleted: true;
|
|
64
92
|
}>;
|
|
93
|
+
/** Whether a trusted reverse proxy secret is set (never the secret). */
|
|
94
|
+
getProxySecret(): Promise<ProxySecret>;
|
|
95
|
+
/** Create, or rotate (with `overlap_seconds`), the trusted reverse proxy secret. The secret is returned once. */
|
|
96
|
+
createProxySecret(body?: CreateProxySecretBody): Promise<ProxySecretCreated>;
|
|
97
|
+
/** Remove the proxy secret: `X-Atlas-Client-IP` is ignored again. */
|
|
98
|
+
deleteProxySecret(): Promise<{
|
|
99
|
+
object: "proxy_secret";
|
|
100
|
+
deleted: true;
|
|
101
|
+
}>;
|
|
65
102
|
/** Store an LDAP connection's service-account bind password. The connection
|
|
66
103
|
* must already exist in `auth_config.ldap.connections`. */
|
|
67
104
|
setLdapBindPassword(connectionId: string, body: {
|
|
@@ -24,6 +24,18 @@ function instanceSecurityResource(request) {
|
|
|
24
24
|
deleteKerberosSecret() {
|
|
25
25
|
return request({ method: 'DELETE', path: '/v1/instance/kerberos_secret' });
|
|
26
26
|
},
|
|
27
|
+
/** Whether a trusted reverse proxy secret is set (never the secret). */
|
|
28
|
+
getProxySecret() {
|
|
29
|
+
return request({ method: 'GET', path: '/v1/instance/proxy_secret' });
|
|
30
|
+
},
|
|
31
|
+
/** Create, or rotate (with `overlap_seconds`), the trusted reverse proxy secret. The secret is returned once. */
|
|
32
|
+
createProxySecret(body = {}) {
|
|
33
|
+
return request({ method: 'POST', path: '/v1/instance/proxy_secret', body });
|
|
34
|
+
},
|
|
35
|
+
/** Remove the proxy secret: `X-Atlas-Client-IP` is ignored again. */
|
|
36
|
+
deleteProxySecret() {
|
|
37
|
+
return request({ method: 'DELETE', path: '/v1/instance/proxy_secret' });
|
|
38
|
+
},
|
|
27
39
|
/** Store an LDAP connection's service-account bind password. The connection
|
|
28
40
|
* must already exist in `auth_config.ldap.connections`. */
|
|
29
41
|
setLdapBindPassword(connectionId, body) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instance-security.js","sourceRoot":"","sources":["../../src/client/instance-security.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"instance-security.js","sourceRoot":"","sources":["../../src/client/instance-security.ts"],"names":[],"mappings":";;AA0EA,4DA6DC;AAvID,uCAAgD;AA0EhD,SAAgB,wBAAwB,CAAC,OAAkB;IACzD,OAAO;QACL,GAAG;YACD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,CAAC,IAAgC;YACrC,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,uBAAuB,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3E,CAAC;QAED,+EAA+E;QAC/E,gBAAgB,CACd,IAAwB;YAExB,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,6BAA6B,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/E,CAAC;QACD,mBAAmB;YACjB,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,6BAA6B,EAAE,CAAC,CAAC;QAC5E,CAAC;QAED,sFAAsF;QACtF,iBAAiB,CAAC,IAAwB;YACxC,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,8BAA8B,EAAE,IAAI,EAAE,CAAC,CAAC;QAChF,CAAC;QACD,oBAAoB;YAClB,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,8BAA8B,EAAE,CAAC,CAAC;QAC7E,CAAC;QAED,wEAAwE;QACxE,cAAc;YACZ,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,2BAA2B,EAAE,CAAC,CAAC;QACvE,CAAC;QACD,iHAAiH;QACjH,iBAAiB,CAAC,OAA8B,EAAE;YAChD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,2BAA2B,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9E,CAAC;QACD,qEAAqE;QACrE,iBAAiB;YACf,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,2BAA2B,EAAE,CAAC,CAAC;QAC1E,CAAC;QAED;oEAC4D;QAC5D,mBAAmB,CACjB,YAAoB,EACpB,IAA+B;YAE/B,OAAO,OAAO,CAAC;gBACb,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,iCAAiC,IAAA,aAAG,EAAC,YAAY,CAAC,gBAAgB;gBACxE,IAAI;aACL,CAAC,CAAC;QACL,CAAC;QACD,sBAAsB,CACpB,YAAoB;YAEpB,OAAO,OAAO,CAAC;gBACb,MAAM,EAAE,QAAQ;gBAChB,IAAI,EAAE,iCAAiC,IAAA,aAAG,EAAC,YAAY,CAAC,gBAAgB;aACzE,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -22,12 +22,30 @@ export interface Instance {
|
|
|
22
22
|
/** The JWKS holding this instance's signing keys. */
|
|
23
23
|
jwks_uri: string;
|
|
24
24
|
allowed_origins: string[];
|
|
25
|
+
/**
|
|
26
|
+
* The declared integration per allowed origin (`redirect` or `embedded`). An
|
|
27
|
+
* origin not listed is inferred by preflight and login readiness.
|
|
28
|
+
*/
|
|
29
|
+
allowed_origin_integrations?: Record<string, 'redirect' | 'embedded'>;
|
|
25
30
|
auth_config: Metadata;
|
|
26
31
|
created_at: number;
|
|
27
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* How an allowed origin signs in: `redirect` (hosted-redirect OIDC: the app
|
|
35
|
+
* never runs the embedded components), `embedded` (the sign-in components run
|
|
36
|
+
* on the origin), or `auto` / `null` (clear the hint; Atlas infers it).
|
|
37
|
+
*/
|
|
38
|
+
export type OriginIntegration = 'redirect' | 'embedded' | 'auto';
|
|
39
|
+
/** An `allowed_origins` entry that also declares how the origin signs in. */
|
|
40
|
+
export interface AllowedOriginEntry {
|
|
41
|
+
origin: string;
|
|
42
|
+
integration?: OriginIntegration | null;
|
|
43
|
+
}
|
|
44
|
+
/** An origin string, or `{ origin, integration }`. A plain string keeps the origin's current hint. */
|
|
45
|
+
export type AllowedOriginInput = string | AllowedOriginEntry;
|
|
28
46
|
export interface UpdateInstanceBody {
|
|
29
|
-
/** Exact origins only — a wildcard or a path is rejected (§13.1). */
|
|
30
|
-
allowed_origins?:
|
|
47
|
+
/** Exact origins only — a wildcard or a path is rejected (§13.1). The list replaces the stored one. */
|
|
48
|
+
allowed_origins?: AllowedOriginInput[];
|
|
31
49
|
auth_config?: Record<string, unknown>;
|
|
32
50
|
}
|
|
33
51
|
/** `PATCH /v1/instance` echoes only the mutable fields, not a full Instance. */
|
|
@@ -35,6 +53,7 @@ export interface InstanceUpdateResult {
|
|
|
35
53
|
object: 'instance';
|
|
36
54
|
id: string;
|
|
37
55
|
allowed_origins: string[];
|
|
56
|
+
allowed_origin_integrations?: Record<string, 'redirect' | 'embedded'>;
|
|
38
57
|
auth_config: Metadata;
|
|
39
58
|
}
|
|
40
59
|
/** One problem (or note) the setup check found. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instance.js","sourceRoot":"","sources":["../../src/client/instance.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"instance.js","sourceRoot":"","sources":["../../src/client/instance.ts"],"names":[],"mappings":";;AAwTA,4CAiEC;AAjED,SAAgB,gBAAgB,CAAC,OAAkB;IACjD,OAAO;QACL,GAAG;YACD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,CAAC,IAAwB;YAC7B,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,CAAC;QACD,yFAAyF;QACzF,SAAS;YACP,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,wBAAwB,EAAE,CAAC,CAAC;QACpE,CAAC;QACD;;;;WAIG;QACH,UAAU,CAAC,IAAuB;YAChC,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,0BAA0B,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7E,CAAC;QACD,2EAA2E;QAC3E,WAAW;YACT,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,0BAA0B,EAAE,CAAC,CAAC;QACtE,CAAC;QACD,+CAA+C;QAC/C,YAAY,EAAE;YACZ,MAAM,EAAE;gBACN,GAAG;oBACD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,kCAAkC,EAAE,CAAC,CAAC;gBAC9E,CAAC;gBACD,GAAG,CAAC,IAA8B;oBAChC,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,kCAAkC,EAAE,IAAI,EAAE,CAAC,CAAC;gBACpF,CAAC;gBACD,MAAM;oBACJ,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,kCAAkC,EAAE,CAAC,CAAC;gBACjF,CAAC;aACF;YACD,UAAU,EAAE;gBACV,GAAG;oBACD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,sCAAsC,EAAE,CAAC,CAAC;gBAClF,CAAC;gBACD,qCAAqC;gBACrC,GAAG,CAAC,IAAkC;oBACpC,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,sCAAsC,EAAE,IAAI,EAAE,CAAC,CAAC;gBACxF,CAAC;gBACD,MAAM;oBACJ,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,sCAAsC,EAAE,CAAC,CAAC;gBACrF,CAAC;aACF;SACF;QACD,oEAAoE;QACpE,aAAa,EAAE;YACb,GAAG;gBACD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,6BAA6B,EAAE,CAAC,CAAC;YACzE,CAAC;YACD,kCAAkC;YAClC,GAAG,CAAC,IAA0B;gBAC5B,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,6BAA6B,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/E,CAAC;YACD,+EAA+E;YAC/E,MAAM;gBACJ,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,6BAA6B,EAAE,CAAC,CAAC;YAC5E,CAAC;SACF;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -333,7 +333,7 @@ export interface ManagedEmail {
|
|
|
333
333
|
last_complaint_at: number | null;
|
|
334
334
|
};
|
|
335
335
|
reputation: 'your_provider_account';
|
|
336
|
-
/** Brevo
|
|
336
|
+
/** On create (Brevo and Resend) and verify (Resend): whether the bounce / complaint webhook was registered on your account. */
|
|
337
337
|
feedback_webhook?: 'registered' | 'not_registered';
|
|
338
338
|
/** DNS automation, when set. The token is never returned. */
|
|
339
339
|
dns: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type AtlasRequestSettings, type RequestFn, type UnknownFieldsEvent } from './request';
|
|
2
2
|
import type { ListPage } from './types';
|
|
3
3
|
import type { BackchannelDeliveriesQuery, BackchannelLogoutDeliveryPage, BackchannelLogoutTest } from './oauth-clients';
|
|
4
|
-
import type { OriginMove, OriginMoveRequest, PreflightFinding } from './instance';
|
|
4
|
+
import type { AllowedOriginInput, OriginMove, OriginMoveRequest, PreflightFinding } from './instance';
|
|
5
5
|
import type { ConfigChangelogEntry, ListConfigChangelogParams } from './activity';
|
|
6
6
|
import type { AtlasDesign, AtlasDesignPatch, DeletedDesignAsset, DesignAsset, DesignAssetData, DesignPreview, ResolvedAtlasDesign, UploadDesignAssetOptions } from './design';
|
|
7
7
|
/** `GET /v1/platform/atlas_id_apps/{id}/preflight`. */
|
|
@@ -52,6 +52,8 @@ export interface PlatformApp {
|
|
|
52
52
|
/** The JWKS holding this app's signing keys. */
|
|
53
53
|
jwks_uri: string;
|
|
54
54
|
allowed_origins: string[];
|
|
55
|
+
/** The declared integration per allowed origin. Write `allowed_origins` entries as `{ origin, integration }` to set one. */
|
|
56
|
+
allowed_origin_integrations?: Record<string, 'redirect' | 'embedded'>;
|
|
55
57
|
/** Housekeeping labels (up to 20, each up to 40 chars). */
|
|
56
58
|
tags: string[];
|
|
57
59
|
/** Hidden from the default listing. A visibility flag only: the app still serves auth. */
|
|
@@ -79,14 +81,16 @@ export interface CreatedPlatformApp extends PlatformAppWithSecret {
|
|
|
79
81
|
export interface CreatePlatformAppBody {
|
|
80
82
|
name?: string;
|
|
81
83
|
environment?: 'production' | 'development';
|
|
82
|
-
|
|
84
|
+
/** Origin strings, or `{ origin, integration }` entries (`redirect` | `embedded` | `auto`). */
|
|
85
|
+
allowed_origins?: AllowedOriginInput[];
|
|
83
86
|
/** Default true: also provision a test-mode sibling instance. */
|
|
84
87
|
create_development_sibling?: boolean;
|
|
85
88
|
tags?: string[];
|
|
86
89
|
}
|
|
87
90
|
export interface UpdatePlatformAppBody {
|
|
88
91
|
name?: string;
|
|
89
|
-
|
|
92
|
+
/** Origin strings, or `{ origin, integration }` entries (`redirect` | `embedded` | `auto`). */
|
|
93
|
+
allowed_origins?: AllowedOriginInput[];
|
|
90
94
|
tags?: string[];
|
|
91
95
|
}
|
|
92
96
|
export interface ListPlatformAppsParams {
|
|
@@ -99,7 +103,8 @@ export interface ListPlatformAppsParams {
|
|
|
99
103
|
}
|
|
100
104
|
export interface CreatePlatformInstanceBody {
|
|
101
105
|
environment?: 'production' | 'development';
|
|
102
|
-
|
|
106
|
+
/** Origin strings, or `{ origin, integration }` entries (`redirect` | `embedded` | `auto`). */
|
|
107
|
+
allowed_origins?: AllowedOriginInput[];
|
|
103
108
|
}
|
|
104
109
|
export interface PlatformAppSecretRotation {
|
|
105
110
|
object: 'app_secret';
|
|
@@ -113,10 +118,32 @@ export interface PlatformAppSecretRotation {
|
|
|
113
118
|
}
|
|
114
119
|
export interface PlatformInstanceSecretRotation {
|
|
115
120
|
object: 'app_secret';
|
|
116
|
-
|
|
121
|
+
app_id: string;
|
|
122
|
+
instance_id: string;
|
|
123
|
+
/** @deprecated Never sent; read `instance_id`. */
|
|
124
|
+
id?: string;
|
|
125
|
+
/** The new secret key, revealed exactly once. */
|
|
117
126
|
secret: string;
|
|
127
|
+
/** The overlap applied, in seconds. */
|
|
128
|
+
overlap_seconds: number;
|
|
129
|
+
/** When the prior keys stop working (epoch ms); null on a hard cut. */
|
|
130
|
+
previous_keys_expire_at: number | null;
|
|
118
131
|
note: string;
|
|
119
132
|
}
|
|
133
|
+
/** Body of the platform app and instance rotate_secret calls. */
|
|
134
|
+
export interface RotatePlatformSecretBody {
|
|
135
|
+
/** How long the prior keys keep working, in seconds. Default 259200 (72 hours). `0` revokes them now. At most 604800 (7 days). */
|
|
136
|
+
overlap_seconds?: number;
|
|
137
|
+
}
|
|
138
|
+
/** Body of the Sign in with Atlas app rotate calls (client secret and webhook secret). */
|
|
139
|
+
export interface RotateAtlasIdAppSecretBody {
|
|
140
|
+
/**
|
|
141
|
+
* How long the previous secret keeps working, in seconds; at most 604800 (7
|
|
142
|
+
* days). Default for the client secret: 86400 (24 hours). Default for the
|
|
143
|
+
* webhook secret: 0 (it stops signing at once). `0` stops it now.
|
|
144
|
+
*/
|
|
145
|
+
overlap_seconds?: number;
|
|
146
|
+
}
|
|
120
147
|
/** Metadata of a currently-valid secret key. Never the secret itself. */
|
|
121
148
|
export interface PlatformSecretKey {
|
|
122
149
|
object: 'secret_key';
|
|
@@ -155,7 +182,8 @@ export interface AccountlessApp {
|
|
|
155
182
|
export interface CreateAccountlessAppBody {
|
|
156
183
|
name?: string;
|
|
157
184
|
environment?: 'production' | 'development';
|
|
158
|
-
|
|
185
|
+
/** Origin strings, or `{ origin, integration }` entries (`redirect` | `embedded` | `auto`). */
|
|
186
|
+
allowed_origins?: AllowedOriginInput[];
|
|
159
187
|
}
|
|
160
188
|
/** Page text on the Sign in with Atlas page. Plain text; null or empty uses the default. */
|
|
161
189
|
export interface AtlasIdAppCopy {
|
|
@@ -230,12 +258,20 @@ export interface AtlasIdApp {
|
|
|
230
258
|
webhook_events?: Array<'user.updated' | 'user.deleted' | 'grant.revoked'>;
|
|
231
259
|
/** false once auto-disabled after repeated failures; null without a webhook. */
|
|
232
260
|
webhook_active?: boolean | null;
|
|
261
|
+
/** While a webhook secret rotation overlap is open: when the previous secret stops signing deliveries (epoch ms); else null. */
|
|
262
|
+
webhook_previous_secret_expires_at?: number | null;
|
|
263
|
+
/** After a client secret rotation with overlap: when the previous secret stops working (epoch ms); else null. */
|
|
264
|
+
previous_secret_expires_at?: number | null;
|
|
265
|
+
/** When the current client secret was issued (epoch ms); null for a public app. */
|
|
266
|
+
secret_issued_at?: number | null;
|
|
233
267
|
}
|
|
234
268
|
/** Create / rotate response: confidential apps carry the client secret exactly once. */
|
|
235
269
|
export interface AtlasIdAppWithSecret extends AtlasIdApp {
|
|
236
270
|
client_secret?: string;
|
|
237
271
|
/** The per-app webhook signing secret, returned exactly once (first set, or rotated). */
|
|
238
272
|
webhook_secret?: string;
|
|
273
|
+
/** On a rotation: the overlap applied, in seconds. */
|
|
274
|
+
overlap_seconds?: number;
|
|
239
275
|
note?: string;
|
|
240
276
|
}
|
|
241
277
|
/** A person who has approved a Sign in with Atlas app (privacy-request lookup). */
|
|
@@ -349,17 +385,20 @@ declare function platformNamespaces(request: RequestFn): {
|
|
|
349
385
|
* Mint a new secret key while the prior keys stay valid for
|
|
350
386
|
* `overlap_seconds` (default 72h, max 7 days; 0 = immediate cut).
|
|
351
387
|
*/
|
|
352
|
-
rotateSecret(id: string, body?:
|
|
353
|
-
overlap_seconds?: number;
|
|
354
|
-
}, idempotencyKey?: string): Promise<PlatformAppSecretRotation>;
|
|
388
|
+
rotateSecret(id: string, body?: RotatePlatformSecretBody, idempotencyKey?: string): Promise<PlatformAppSecretRotation>;
|
|
355
389
|
/** Currently-valid secret keys (metadata only). */
|
|
356
390
|
secretKeys(id: string): Promise<ListPage<PlatformSecretKey>>;
|
|
357
391
|
instances: {
|
|
358
392
|
list(appId: string): Promise<ListPage<PlatformApp>>;
|
|
359
393
|
/** Add an instance (e.g. development beside production); secret revealed once. */
|
|
360
394
|
create(appId: string, body?: CreatePlatformInstanceBody, idempotencyKey?: string): Promise<PlatformAppWithSecret>;
|
|
361
|
-
/**
|
|
362
|
-
|
|
395
|
+
/**
|
|
396
|
+
* Mint a new secret key for one instance, revealed once. The prior keys
|
|
397
|
+
* stay valid for `overlap_seconds` (default 72h, max 7 days; 0 =
|
|
398
|
+
* immediate cut), like `apps.rotateSecret`. The third argument may also
|
|
399
|
+
* be the idempotency key (the 0.9 signature).
|
|
400
|
+
*/
|
|
401
|
+
rotateSecret(appId: string, instanceId: string, bodyOrIdempotencyKey?: RotatePlatformSecretBody | string, idempotencyKey?: string): Promise<PlatformInstanceSecretRotation>;
|
|
363
402
|
};
|
|
364
403
|
/** Start a transfer: a one-time token the receiving platform accepts. */
|
|
365
404
|
transfer(id: string, idempotencyKey?: string): Promise<PlatformAppTransfer>;
|
|
@@ -388,8 +427,11 @@ declare function platformNamespaces(request: RequestFn): {
|
|
|
388
427
|
id: string;
|
|
389
428
|
deleted: true;
|
|
390
429
|
}>;
|
|
391
|
-
/**
|
|
392
|
-
|
|
430
|
+
/**
|
|
431
|
+
* New client secret, returned exactly once. The previous one keeps working
|
|
432
|
+
* for `overlap_seconds` (default 24h; 0 = revoke now). Public apps have none (409).
|
|
433
|
+
*/
|
|
434
|
+
rotateSecret(id: string, body?: RotateAtlasIdAppSecretBody): Promise<AtlasIdAppWithSecret>;
|
|
393
435
|
/** The app's back-channel logout delivery log, newest first. */
|
|
394
436
|
listBackchannelDeliveries(id: string, query?: BackchannelDeliveriesQuery): Promise<BackchannelLogoutDeliveryPage>;
|
|
395
437
|
/** Send a test back-channel logout to the app now and return what it answered. */
|
|
@@ -408,8 +450,12 @@ declare function platformNamespaces(request: RequestFn): {
|
|
|
408
450
|
revokeGrant(id: string, grantId: string): Promise<AtlasIdGrantRevocation>;
|
|
409
451
|
/** Move the app's URLs from one origin to another. `dry_run: true` writes nothing. */
|
|
410
452
|
moveOrigin(id: string, body: OriginMoveRequest): Promise<OriginMove>;
|
|
411
|
-
/**
|
|
412
|
-
|
|
453
|
+
/**
|
|
454
|
+
* New per-app webhook signing secret (`webhook_secret`), returned once. By
|
|
455
|
+
* default the previous secret stops at once; for `overlap_seconds` it signs
|
|
456
|
+
* each delivery too. 409 without a webhook.
|
|
457
|
+
*/
|
|
458
|
+
rotateWebhookSecret(id: string, body?: RotateAtlasIdAppSecretBody): Promise<AtlasIdAppWithSecret>;
|
|
413
459
|
/** Preview a design on the Sign in with Atlas page without saving it. */
|
|
414
460
|
previewDesign(design: AtlasDesign): Promise<DesignPreview>;
|
|
415
461
|
};
|
package/dist/client/platform.js
CHANGED
|
@@ -64,12 +64,19 @@ function platformNamespaces(request) {
|
|
|
64
64
|
idempotencyKey,
|
|
65
65
|
});
|
|
66
66
|
},
|
|
67
|
-
/**
|
|
68
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Mint a new secret key for one instance, revealed once. The prior keys
|
|
69
|
+
* stay valid for `overlap_seconds` (default 72h, max 7 days; 0 =
|
|
70
|
+
* immediate cut), like `apps.rotateSecret`. The third argument may also
|
|
71
|
+
* be the idempotency key (the 0.9 signature).
|
|
72
|
+
*/
|
|
73
|
+
rotateSecret(appId, instanceId, bodyOrIdempotencyKey, idempotencyKey) {
|
|
74
|
+
const legacyKey = typeof bodyOrIdempotencyKey === 'string';
|
|
69
75
|
return request({
|
|
70
76
|
method: 'POST',
|
|
71
77
|
path: `/v1/platform/apps/${(0, request_1.enc)(appId)}/instances/${(0, request_1.enc)(instanceId)}/rotate_secret`,
|
|
72
|
-
|
|
78
|
+
body: legacyKey ? undefined : bodyOrIdempotencyKey,
|
|
79
|
+
idempotencyKey: legacyKey ? bodyOrIdempotencyKey : idempotencyKey,
|
|
73
80
|
});
|
|
74
81
|
},
|
|
75
82
|
},
|
|
@@ -136,11 +143,15 @@ function platformNamespaces(request) {
|
|
|
136
143
|
delete(id) {
|
|
137
144
|
return request({ method: 'DELETE', path: `/v1/platform/atlas_id_apps/${(0, request_1.enc)(id)}` });
|
|
138
145
|
},
|
|
139
|
-
/**
|
|
140
|
-
|
|
146
|
+
/**
|
|
147
|
+
* New client secret, returned exactly once. The previous one keeps working
|
|
148
|
+
* for `overlap_seconds` (default 24h; 0 = revoke now). Public apps have none (409).
|
|
149
|
+
*/
|
|
150
|
+
rotateSecret(id, body) {
|
|
141
151
|
return request({
|
|
142
152
|
method: 'POST',
|
|
143
153
|
path: `/v1/platform/atlas_id_apps/${(0, request_1.enc)(id)}/rotate_secret`,
|
|
154
|
+
body,
|
|
144
155
|
});
|
|
145
156
|
},
|
|
146
157
|
/** The app's back-channel logout delivery log, newest first. */
|
|
@@ -188,11 +199,16 @@ function platformNamespaces(request) {
|
|
|
188
199
|
body,
|
|
189
200
|
});
|
|
190
201
|
},
|
|
191
|
-
/**
|
|
192
|
-
|
|
202
|
+
/**
|
|
203
|
+
* New per-app webhook signing secret (`webhook_secret`), returned once. By
|
|
204
|
+
* default the previous secret stops at once; for `overlap_seconds` it signs
|
|
205
|
+
* each delivery too. 409 without a webhook.
|
|
206
|
+
*/
|
|
207
|
+
rotateWebhookSecret(id, body) {
|
|
193
208
|
return request({
|
|
194
209
|
method: 'POST',
|
|
195
210
|
path: `/v1/platform/atlas_id_apps/${(0, request_1.enc)(id)}/webhook/rotate_secret`,
|
|
211
|
+
body,
|
|
196
212
|
});
|
|
197
213
|
},
|
|
198
214
|
/** Preview a design on the Sign in with Atlas page without saving it. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/client/platform.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/client/platform.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,12 @@ export interface WebhookEndpoint {
|
|
|
8
8
|
/** Start of the current failure streak (epoch ms), or null when the last delivery succeeded. */
|
|
9
9
|
failing_since?: number | null;
|
|
10
10
|
disabled_at: number | null;
|
|
11
|
+
/**
|
|
12
|
+
* While a secret rotation overlap is open: when the previous secret stops
|
|
13
|
+
* signing deliveries (epoch ms). Until then every delivery carries two
|
|
14
|
+
* signatures, one per secret. Null otherwise.
|
|
15
|
+
*/
|
|
16
|
+
previous_secret_expires_at?: number | null;
|
|
11
17
|
created_at: number;
|
|
12
18
|
}
|
|
13
19
|
/** Create reveals the signing secret (`whsec_...`) exactly once. */
|
|
@@ -80,8 +86,21 @@ export interface WebhookSigningSecret {
|
|
|
80
86
|
object: 'webhook_secret';
|
|
81
87
|
/** The `whsec_...` signing secret. */
|
|
82
88
|
secret: string;
|
|
89
|
+
/** On a rotation: the overlap applied, in seconds. */
|
|
90
|
+
overlap_seconds?: number;
|
|
91
|
+
/** On a rotation: when the previous secret stops signing deliveries (epoch ms), or null when it already has. */
|
|
92
|
+
previous_secret_expires_at?: number | null;
|
|
83
93
|
warning?: string;
|
|
84
94
|
}
|
|
95
|
+
export interface RotateWebhookSecretBody {
|
|
96
|
+
/**
|
|
97
|
+
* How long the previous secret keeps signing deliveries (as a second
|
|
98
|
+
* `atlas-signature` entry), in seconds. Default 0: it stops at once. At most
|
|
99
|
+
* 604800 (7 days). Set it only once every receiver verifies with a verifier
|
|
100
|
+
* that accepts several signatures (this package 0.9.1+).
|
|
101
|
+
*/
|
|
102
|
+
overlap_seconds?: number;
|
|
103
|
+
}
|
|
85
104
|
/** A queued delivery (from `test` or `redeliver`). */
|
|
86
105
|
export interface WebhookDeliveryQueued {
|
|
87
106
|
object: 'webhook_delivery';
|
|
@@ -101,8 +120,12 @@ export declare function webhooksResource(request: RequestFn): {
|
|
|
101
120
|
update(id: string, body: UpdateWebhookEndpointBody): Promise<WebhookEndpoint>;
|
|
102
121
|
/** Show the current signing secret again. Audited. */
|
|
103
122
|
revealSecret(id: string): Promise<WebhookSigningSecret>;
|
|
104
|
-
/**
|
|
105
|
-
|
|
123
|
+
/**
|
|
124
|
+
* Rotate the signing secret; the new one is revealed exactly once. By
|
|
125
|
+
* default the old secret stops signing at once; for `overlap_seconds`
|
|
126
|
+
* every delivery is signed with both, so a verifier holding either passes.
|
|
127
|
+
*/
|
|
128
|
+
rotateSecret(id: string, body?: RotateWebhookSecretBody): Promise<WebhookSigningSecret>;
|
|
106
129
|
/** Send a synthetic `webhook.test` event to this endpoint only. */
|
|
107
130
|
test(id: string): Promise<WebhookDeliveryQueued>;
|
|
108
131
|
/** Redeliver a past event as a fresh attempt (409 once past its retention). */
|
package/dist/client/webhooks.js
CHANGED
|
@@ -16,9 +16,13 @@ function webhooksResource(request) {
|
|
|
16
16
|
revealSecret(id) {
|
|
17
17
|
return request({ method: 'POST', path: `/v1/webhook_endpoints/${(0, request_1.enc)(id)}/reveal_secret` });
|
|
18
18
|
},
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Rotate the signing secret; the new one is revealed exactly once. By
|
|
21
|
+
* default the old secret stops signing at once; for `overlap_seconds`
|
|
22
|
+
* every delivery is signed with both, so a verifier holding either passes.
|
|
23
|
+
*/
|
|
24
|
+
rotateSecret(id, body) {
|
|
25
|
+
return request({ method: 'POST', path: `/v1/webhook_endpoints/${(0, request_1.enc)(id)}/rotate_secret`, body });
|
|
22
26
|
},
|
|
23
27
|
/** Send a synthetic `webhook.test` event to this endpoint only. */
|
|
24
28
|
test(id) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/client/webhooks.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/client/webhooks.ts"],"names":[],"mappings":";;AA+HA,4CAgEC;AA/LD,uCAAgD;AA+HhD,SAAgB,gBAAgB,CAAC,OAAkB;IACjD,OAAO;QACL,SAAS,EAAE;YACT,IAAI;gBACF,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC,CAAC;YACnE,CAAC;YACD,wEAAwE;YACxE,MAAM,CAAC,EAAU,EAAE,IAA+B;gBAChD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,yBAAyB,IAAA,aAAG,EAAC,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;YACtF,CAAC;YACD,sDAAsD;YACtD,YAAY,CAAC,EAAU;gBACrB,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,yBAAyB,IAAA,aAAG,EAAC,EAAE,CAAC,gBAAgB,EAAE,CAAC,CAAC;YAC7F,CAAC;YACD;;;;eAIG;YACH,YAAY,CAAC,EAAU,EAAE,IAA8B;gBACrD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,yBAAyB,IAAA,aAAG,EAAC,EAAE,CAAC,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC;YACnG,CAAC;YACD,mEAAmE;YACnE,IAAI,CAAC,EAAU;gBACb,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,yBAAyB,IAAA,aAAG,EAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;YACpF,CAAC;YACD,+EAA+E;YAC/E,SAAS,CAAC,EAAU,EAAE,UAAkB;gBACtC,OAAO,OAAO,CAAC;oBACb,MAAM,EAAE,MAAM;oBACd,IAAI,EAAE,yBAAyB,IAAA,aAAG,EAAC,EAAE,CAAC,eAAe,IAAA,aAAG,EAAC,UAAU,CAAC,YAAY;iBACjF,CAAC,CAAC;YACL,CAAC;YACD,MAAM,CACJ,IAA+B,EAC/B,cAAuB;gBAEvB,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;YAC1F,CAAC;YACD,MAAM,CACJ,EAAU;gBAEV,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,yBAAyB,IAAA,aAAG,EAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YACjF,CAAC;SACF;QACD,oCAAoC;QACpC,UAAU,CACR,EAAU,EACV,SAAsC,EAAE;YAExC,OAAO,OAAO,CAAC;gBACb,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,yBAAyB,IAAA,aAAG,EAAC,EAAE,CAAC,aAAa;gBACnD,KAAK,EAAE,EAAE,GAAG,MAAM,EAAE;aACrB,CAAC,CAAC;QACL,CAAC;QACD,8DAA8D;QAC9D,SAAS,CAAC,UAAkB,EAAE,UAAkB;YAC9C,OAAO,OAAO,CAAC;gBACb,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,yBAAyB,IAAA,aAAG,EAAC,UAAU,CAAC,eAAe,IAAA,aAAG,EAAC,UAAU,CAAC,YAAY;aACzF,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/webhooks.js
CHANGED
|
@@ -11,7 +11,9 @@ const node_crypto_1 = require("node:crypto");
|
|
|
11
11
|
* and `atlas-timestamp` (epoch ms) headers. The timestamp is INSIDE the signed
|
|
12
12
|
* material — a header-only timestamp would let an attacker replay a captured
|
|
13
13
|
* body under a fresh timestamp — and a delivery older than 5 minutes is rejected
|
|
14
|
-
* so a captured request cannot be replayed later.
|
|
14
|
+
* so a captured request cannot be replayed later. The header may carry several
|
|
15
|
+
* space-separated `v1,<base64>` entries (one per secret while a secret rotates
|
|
16
|
+
* with an overlap); the delivery verifies when any one of them matches.
|
|
15
17
|
*
|
|
16
18
|
* The signing logic is identical to the server's (`@atlas/core` `verifyWebhook`),
|
|
17
19
|
* reimplemented here with zero dependencies so an integrator gets a first-party
|
|
@@ -41,6 +43,24 @@ function readHeader(headers, name) {
|
|
|
41
43
|
const found = hit ? bag[hit] : undefined;
|
|
42
44
|
return Array.isArray(found) ? found[0] : found;
|
|
43
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* The `v1` signatures in an `atlas-signature` header. The header carries one
|
|
48
|
+
* `v1,<base64>` entry, or several separated by spaces (while an endpoint
|
|
49
|
+
* secret rotates, Atlas signs with the old and the new secret). Entries of
|
|
50
|
+
* another version are skipped, so a future scheme never breaks this verifier.
|
|
51
|
+
*/
|
|
52
|
+
function parseSignatures(header) {
|
|
53
|
+
const out = [];
|
|
54
|
+
for (const entry of header.trim().split(/\s+/)) {
|
|
55
|
+
const comma = entry.indexOf(',');
|
|
56
|
+
if (comma <= 0)
|
|
57
|
+
continue;
|
|
58
|
+
const value = entry.slice(comma + 1);
|
|
59
|
+
if (entry.slice(0, comma) === SIGNATURE_VERSION && value)
|
|
60
|
+
out.push(value);
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
44
64
|
/**
|
|
45
65
|
* Verify and parse an Atlas webhook. Returns `{ verified, reason?, event?,
|
|
46
66
|
* deliveryId?, timestamp? }` — it never throws on a bad signature (a thrown
|
|
@@ -67,8 +87,8 @@ function verifyWebhook(input) {
|
|
|
67
87
|
const signature = readHeader(input.headers, exports.ATLAS_WEBHOOK_HEADERS.signature);
|
|
68
88
|
if (!id || !timestampRaw || !signature)
|
|
69
89
|
return { verified: false, reason: 'missing_headers' };
|
|
70
|
-
const
|
|
71
|
-
if (
|
|
90
|
+
const provided = parseSignatures(signature);
|
|
91
|
+
if (provided.length === 0)
|
|
72
92
|
return { verified: false, reason: 'malformed_signature' };
|
|
73
93
|
const timestamp = Number(timestampRaw);
|
|
74
94
|
const now = (input.now ?? new Date()).getTime();
|
|
@@ -78,10 +98,15 @@ function verifyWebhook(input) {
|
|
|
78
98
|
if (!Number.isFinite(timestamp) || Math.abs(now - timestamp) > tolerance) {
|
|
79
99
|
return { verified: false, reason: 'stale_timestamp' };
|
|
80
100
|
}
|
|
81
|
-
const expected = (0, node_crypto_1.createHmac)('sha256', input.secret).update(`${id}.${timestamp}.${raw}`).digest('base64');
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
101
|
+
const expected = Buffer.from((0, node_crypto_1.createHmac)('sha256', input.secret).update(`${id}.${timestamp}.${raw}`).digest('base64'));
|
|
102
|
+
// Compare every entry (no early exit), each in constant time.
|
|
103
|
+
let matched = false;
|
|
104
|
+
for (const candidate of provided) {
|
|
105
|
+
const a = Buffer.from(candidate);
|
|
106
|
+
if (a.length === expected.length && (0, node_crypto_1.timingSafeEqual)(a, expected))
|
|
107
|
+
matched = true;
|
|
108
|
+
}
|
|
109
|
+
if (!matched)
|
|
85
110
|
return { verified: false, reason: 'signature_mismatch' };
|
|
86
111
|
try {
|
|
87
112
|
return { verified: true, event: JSON.parse(raw), deliveryId: id, timestamp };
|
package/dist/webhooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"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"}
|