authhero 9.9.1 → 9.11.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/dist/assets/u/widget/index.esm.js +1 -1
- package/dist/authhero.cjs +145 -145
- package/dist/authhero.d.ts +585 -190
- package/dist/authhero.mjs +15529 -14313
- package/dist/tsconfig.types.tsbuildinfo +1 -1
- package/dist/types/authentication-flows/authorization-code.d.ts +2 -1
- package/dist/types/authentication-flows/client-credentials.d.ts +2 -1
- package/dist/types/authentication-flows/passwordless.d.ts +6 -5
- package/dist/types/authentication-flows/refresh-token.d.ts +2 -1
- package/dist/types/authentication-flows/token-exchange.d.ts +2 -1
- package/dist/types/helpers/client-assertion-replay.d.ts +21 -0
- package/dist/types/helpers/client-assertion.d.ts +24 -2
- package/dist/types/helpers/dcr/metadata-mapping.d.ts +1 -1
- package/dist/types/helpers/default-destinations.d.ts +7 -1
- package/dist/types/helpers/outbox-destinations/index.d.ts +1 -0
- package/dist/types/helpers/outbox-destinations/pipeline.d.ts +63 -0
- package/dist/types/helpers/outbox-relay.d.ts +3 -0
- package/dist/types/helpers/reserved-claims.d.ts +64 -0
- package/dist/types/helpers/run-outbox-relay.d.ts +8 -0
- package/dist/types/helpers/run-retention.d.ts +5 -0
- package/dist/types/helpers/users-import/map.d.ts +128 -0
- package/dist/types/helpers/users-import/process.d.ts +95 -0
- package/dist/types/helpers/users-import-cleanup.d.ts +26 -0
- package/dist/types/index.d.ts +321 -185
- package/dist/types/routes/auth-api/index.d.ts +44 -44
- package/dist/types/routes/auth-api/passwordless.d.ts +14 -14
- package/dist/types/routes/auth-api/register/index.d.ts +2 -2
- package/dist/types/routes/auth-api/token.d.ts +24 -24
- package/dist/types/routes/management-api/action-triggers.d.ts +0 -2
- package/dist/types/routes/management-api/actions.d.ts +0 -7
- package/dist/types/routes/management-api/authentication-methods.d.ts +1 -1
- package/dist/types/routes/management-api/branding.d.ts +1 -1
- package/dist/types/routes/management-api/client-grants.d.ts +9 -9
- package/dist/types/routes/management-api/clients.d.ts +8 -8
- package/dist/types/routes/management-api/connections.d.ts +1 -1
- package/dist/types/routes/management-api/email-templates.d.ts +18 -18
- package/dist/types/routes/management-api/failed-events.d.ts +22 -2
- package/dist/types/routes/management-api/guardian.d.ts +5 -5
- package/dist/types/routes/management-api/helpers.d.ts +1 -1
- package/dist/types/routes/management-api/index.d.ts +216 -86
- package/dist/types/routes/management-api/jobs.d.ts +133 -0
- package/dist/types/routes/management-api/keys.d.ts +16 -16
- package/dist/types/routes/management-api/logs.d.ts +4 -4
- package/dist/types/routes/management-api/migration-sources.d.ts +6 -6
- package/dist/types/routes/management-api/organizations.d.ts +2 -2
- package/dist/types/routes/management-api/prompts.d.ts +4 -4
- package/dist/types/routes/management-api/roles.d.ts +1 -1
- package/dist/types/routes/management-api/tenant-export-import.d.ts +5 -5
- package/dist/types/routes/management-api/tenant-operations.d.ts +33 -9
- package/dist/types/routes/management-api/tenants.d.ts +11 -11
- package/dist/types/routes/management-api/users.d.ts +24 -24
- package/dist/types/routes/universal-login/common.d.ts +2 -2
- package/dist/types/routes/universal-login/flow-api.d.ts +12 -12
- package/dist/types/routes/universal-login/identifier.d.ts +2 -2
- package/dist/types/routes/universal-login/index.d.ts +2 -2
- package/dist/types/routes/universal-login/u2-index.d.ts +5 -5
- package/dist/types/routes/universal-login/u2-routes.d.ts +5 -5
- package/dist/types/state-machines/login-session.d.ts +1 -1
- package/dist/types/types/AuthHeroConfig.d.ts +40 -0
- package/dist/types/types/Bindings.d.ts +14 -0
- package/dist/types/types/OutboxMetrics.d.ts +47 -0
- package/dist/types/types/auth0/UserImport.d.ts +317 -0
- package/dist/types/types/index.d.ts +1 -0
- package/package.json +6 -6
|
@@ -3,6 +3,7 @@ import { z } from "@hono/zod-openapi";
|
|
|
3
3
|
import { Bindings, Variables } from "../types";
|
|
4
4
|
import { TokenResponse } from "@authhero/adapter-interfaces";
|
|
5
5
|
import { GrantFlowUserResult } from "src/types/GrantFlowResult";
|
|
6
|
+
import { EnrichedClient } from "../helpers/client";
|
|
6
7
|
export declare const authorizationCodeGrantParamsSchema: z.ZodObject<{
|
|
7
8
|
grant_type: z.ZodLiteral<"authorization_code">;
|
|
8
9
|
client_id: z.ZodString;
|
|
@@ -16,7 +17,7 @@ export type AuthorizationCodeGrantTypeParams = z.infer<typeof authorizationCodeG
|
|
|
16
17
|
export declare function authorizationCodeGrantUser(ctx: Context<{
|
|
17
18
|
Bindings: Bindings;
|
|
18
19
|
Variables: Variables;
|
|
19
|
-
}>, params: AuthorizationCodeGrantTypeParams): Promise<GrantFlowUserResult>;
|
|
20
|
+
}>, params: AuthorizationCodeGrantTypeParams, preloadedClient?: EnrichedClient): Promise<GrantFlowUserResult>;
|
|
20
21
|
export declare function authorizationCodeGrant(ctx: Context<{
|
|
21
22
|
Bindings: Bindings;
|
|
22
23
|
Variables: Variables;
|
|
@@ -2,6 +2,7 @@ import { Context } from "hono";
|
|
|
2
2
|
import { z } from "@hono/zod-openapi";
|
|
3
3
|
import { Bindings, Variables } from "../types";
|
|
4
4
|
import { GrantFlowResult } from "../types/GrantFlowResult";
|
|
5
|
+
import { EnrichedClient } from "../helpers/client";
|
|
5
6
|
export declare const clientCredentialGrantParamsSchema: z.ZodObject<{
|
|
6
7
|
grant_type: z.ZodLiteral<"client_credentials">;
|
|
7
8
|
scope: z.ZodOptional<z.ZodString>;
|
|
@@ -13,4 +14,4 @@ export declare const clientCredentialGrantParamsSchema: z.ZodObject<{
|
|
|
13
14
|
export declare function clientCredentialsGrant(ctx: Context<{
|
|
14
15
|
Bindings: Bindings;
|
|
15
16
|
Variables: Variables;
|
|
16
|
-
}>, params: z.infer<typeof clientCredentialGrantParamsSchema
|
|
17
|
+
}>, params: z.infer<typeof clientCredentialGrantParamsSchema>, preloadedClient?: EnrichedClient): Promise<GrantFlowResult>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Context } from "hono";
|
|
2
2
|
import { z } from "@hono/zod-openapi";
|
|
3
3
|
import { Bindings, Variables } from "../types";
|
|
4
|
+
import { EnrichedClient } from "../helpers/client";
|
|
4
5
|
import { GrantFlowUserResult } from "../types/GrantFlowResult";
|
|
5
6
|
export declare const passwordlessGrantParamsSchema: z.ZodObject<{
|
|
6
7
|
client_id: z.ZodString;
|
|
@@ -45,7 +46,7 @@ export declare const passwordlessGrantParamsSchema: z.ZodObject<{
|
|
|
45
46
|
export declare function passwordlessGrantUser(ctx: Context<{
|
|
46
47
|
Bindings: Bindings;
|
|
47
48
|
Variables: Variables;
|
|
48
|
-
}>, { client_id, username, otp, scope, audience, authParams, enforceIpCheck, }: z.input<typeof passwordlessGrantParamsSchema
|
|
49
|
+
}>, { client_id, username, otp, scope, audience, authParams, enforceIpCheck, }: z.input<typeof passwordlessGrantParamsSchema>, preloadedClient?: EnrichedClient): Promise<{
|
|
49
50
|
user: {
|
|
50
51
|
connection: string;
|
|
51
52
|
email_verified: boolean;
|
|
@@ -474,7 +475,7 @@ export declare function passwordlessGrantUser(ctx: Context<{
|
|
|
474
475
|
custom_login_page_preview?: string | undefined;
|
|
475
476
|
form_template?: string | undefined;
|
|
476
477
|
addons?: Record<string, any> | undefined;
|
|
477
|
-
token_endpoint_auth_method?: "
|
|
478
|
+
token_endpoint_auth_method?: "client_secret_post" | "client_secret_basic" | "none" | "client_secret_jwt" | "private_key_jwt" | undefined;
|
|
478
479
|
client_metadata?: Record<string, string> | undefined;
|
|
479
480
|
hide_sign_up_disabled_error?: boolean | undefined;
|
|
480
481
|
mobile?: Record<string, any> | undefined;
|
|
@@ -557,8 +558,8 @@ export declare function passwordlessGrantUser(ctx: Context<{
|
|
|
557
558
|
} | undefined;
|
|
558
559
|
authenticated_at?: string | undefined;
|
|
559
560
|
};
|
|
560
|
-
connectionType: "
|
|
561
|
-
authConnection: "
|
|
561
|
+
connectionType: "email" | "username" | "sms";
|
|
562
|
+
authConnection: "email" | "username" | "sms";
|
|
562
563
|
session_id: string | undefined;
|
|
563
564
|
authParams: {
|
|
564
565
|
audience?: string | undefined;
|
|
@@ -612,7 +613,7 @@ export declare function passwordlessGrantUser(ctx: Context<{
|
|
|
612
613
|
export declare function passwordlessOtpGrant(ctx: Context<{
|
|
613
614
|
Bindings: Bindings;
|
|
614
615
|
Variables: Variables;
|
|
615
|
-
}>, params: z.input<typeof passwordlessGrantParamsSchema
|
|
616
|
+
}>, params: z.input<typeof passwordlessGrantParamsSchema>, preloadedClient?: EnrichedClient): Promise<GrantFlowUserResult>;
|
|
616
617
|
export declare function passwordlessGrant(ctx: Context<{
|
|
617
618
|
Bindings: Bindings;
|
|
618
619
|
Variables: Variables;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Context } from "hono";
|
|
2
2
|
import { Bindings, Variables, GrantFlowUserResult } from "../types";
|
|
3
3
|
import { z } from "@hono/zod-openapi";
|
|
4
|
+
import { EnrichedClient } from "../helpers/client";
|
|
4
5
|
export declare const refreshTokenParamsSchema: z.ZodObject<{
|
|
5
6
|
grant_type: z.ZodLiteral<"refresh_token">;
|
|
6
7
|
client_id: z.ZodString;
|
|
@@ -12,4 +13,4 @@ export declare const refreshTokenParamsSchema: z.ZodObject<{
|
|
|
12
13
|
export declare function refreshTokenGrant(ctx: Context<{
|
|
13
14
|
Bindings: Bindings;
|
|
14
15
|
Variables: Variables;
|
|
15
|
-
}>, params: z.infer<typeof refreshTokenParamsSchema
|
|
16
|
+
}>, params: z.infer<typeof refreshTokenParamsSchema>, preloadedClient?: EnrichedClient): Promise<GrantFlowUserResult>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Context } from "hono";
|
|
2
2
|
import { z } from "@hono/zod-openapi";
|
|
3
3
|
import { Bindings, Variables, GrantFlowUserResult } from "../types";
|
|
4
|
+
import { EnrichedClient } from "../helpers/client";
|
|
4
5
|
export declare const TOKEN_EXCHANGE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange";
|
|
5
6
|
export declare const tokenExchangeParamsSchema: z.ZodObject<{
|
|
6
7
|
grant_type: z.ZodLiteral<"urn:ietf:params:oauth:grant-type:token-exchange">;
|
|
@@ -16,4 +17,4 @@ export type TokenExchangeParams = z.infer<typeof tokenExchangeParamsSchema>;
|
|
|
16
17
|
export declare function tokenExchangeGrant(ctx: Context<{
|
|
17
18
|
Bindings: Bindings;
|
|
18
19
|
Variables: Variables;
|
|
19
|
-
}>, params: TokenExchangeParams): Promise<GrantFlowUserResult>;
|
|
20
|
+
}>, params: TokenExchangeParams, preloadedClient?: EnrichedClient): Promise<GrantFlowUserResult>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Context } from "hono";
|
|
2
|
+
import { Bindings, Variables } from "../types";
|
|
3
|
+
export interface ConsumeClientAssertionJtiParams {
|
|
4
|
+
clientId: string;
|
|
5
|
+
/** The assertion's `jti`. When absent there is nothing to spend. */
|
|
6
|
+
jti?: string;
|
|
7
|
+
/** The assertion's `exp`, in seconds — when the marker becomes collectable. */
|
|
8
|
+
exp: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Spend a client assertion's `jti`.
|
|
12
|
+
*
|
|
13
|
+
* @returns false when this assertion has already been presented (the caller
|
|
14
|
+
* must reject it as `invalid_client`), true otherwise. An assertion carrying
|
|
15
|
+
* no `jti` cannot be tracked, so it returns true — its replay window is
|
|
16
|
+
* bounded only by the assertion lifetime cap.
|
|
17
|
+
*/
|
|
18
|
+
export declare function consumeClientAssertionJti(ctx: Context<{
|
|
19
|
+
Bindings: Bindings;
|
|
20
|
+
Variables: Variables;
|
|
21
|
+
}>, tenantId: string, params: ConsumeClientAssertionJtiParams): Promise<boolean>;
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { LoadClientKeysOptions, ClientWithKeys } from "./client-keys";
|
|
2
2
|
declare const ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
|
|
3
|
+
/**
|
|
4
|
+
* Default upper bound on a client assertion's lifetime. RFC 7523 gives no
|
|
5
|
+
* limit, so without one a client can mint an assertion valid for a year and a
|
|
6
|
+
* captured assertion stays usable for that whole window. 300s matches the
|
|
7
|
+
* usual guidance for a single-use token presented directly to the endpoint.
|
|
8
|
+
*/
|
|
9
|
+
declare const DEFAULT_MAX_LIFETIME_SECONDS = 300;
|
|
3
10
|
export type ClientAssertionMethod = "private_key_jwt" | "client_secret_jwt";
|
|
4
11
|
export declare class ClientAssertionError extends Error {
|
|
5
12
|
code: "invalid_client" | "invalid_request" | "unsupported_alg" | "missing_keys";
|
|
@@ -18,6 +25,14 @@ export interface VerifyClientAssertionOptions extends LoadClientKeysOptions {
|
|
|
18
25
|
acceptedAudiences: string[];
|
|
19
26
|
/** Clock-skew leeway in seconds. Defaults to 30. */
|
|
20
27
|
leewaySeconds?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Maximum accepted assertion lifetime in seconds. Rejects an assertion whose
|
|
30
|
+
* `exp - iat` exceeds this, and caps the absolute `exp` at `now + max` so an
|
|
31
|
+
* assertion that omits `iat` cannot sidestep the bound. Defaults to
|
|
32
|
+
* DEFAULT_MAX_LIFETIME_SECONDS (300) — the window a captured assertion stays
|
|
33
|
+
* replayable in is bounded by this, so keep it short.
|
|
34
|
+
*/
|
|
35
|
+
maxLifetimeSeconds?: number;
|
|
21
36
|
/** Override Date.now() for tests. */
|
|
22
37
|
now?: () => number;
|
|
23
38
|
}
|
|
@@ -26,8 +41,15 @@ export interface VerifiedClientAssertion {
|
|
|
26
41
|
clientId: string;
|
|
27
42
|
/** Which authentication method was actually used. */
|
|
28
43
|
method: ClientAssertionMethod;
|
|
29
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* The `jti` claim, when present. `consumeClientAssertionJti`
|
|
46
|
+
* (helpers/client-assertion-replay.ts) spends it so an assertion cannot be
|
|
47
|
+
* presented twice; the token endpoint calls that after this verifier
|
|
48
|
+
* returns.
|
|
49
|
+
*/
|
|
30
50
|
jti?: string;
|
|
51
|
+
/** The `exp` claim, in seconds. Bounds how long the `jti` must be remembered. */
|
|
52
|
+
exp: number;
|
|
31
53
|
/** The full verified payload, in case callers need other claims. */
|
|
32
54
|
payload: Record<string, unknown>;
|
|
33
55
|
}
|
|
@@ -46,4 +68,4 @@ export interface VerifiedClientAssertion {
|
|
|
46
68
|
* any of the iss/sub/aud/exp checks.
|
|
47
69
|
*/
|
|
48
70
|
export declare function verifyClientAssertion(assertion: string, client: ClientAssertionClient, opts: VerifyClientAssertionOptions): Promise<VerifiedClientAssertion>;
|
|
49
|
-
export { ASSERTION_TYPE as CLIENT_ASSERTION_TYPE };
|
|
71
|
+
export { ASSERTION_TYPE as CLIENT_ASSERTION_TYPE, DEFAULT_MAX_LIFETIME_SECONDS as CLIENT_ASSERTION_DEFAULT_MAX_LIFETIME_SECONDS, };
|
|
@@ -23,9 +23,9 @@ export declare const dcrRequestSchema: z.ZodObject<{
|
|
|
23
23
|
grant_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
24
24
|
response_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
25
25
|
token_endpoint_auth_method: z.ZodOptional<z.ZodEnum<{
|
|
26
|
-
none: "none";
|
|
27
26
|
client_secret_post: "client_secret_post";
|
|
28
27
|
client_secret_basic: "client_secret_basic";
|
|
28
|
+
none: "none";
|
|
29
29
|
client_secret_jwt: "client_secret_jwt";
|
|
30
30
|
private_key_jwt: "private_key_jwt";
|
|
31
31
|
}>>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CodeExecutor, DataAdapters } from "@authhero/adapter-interfaces";
|
|
2
2
|
import { EventDestination } from "./outbox-relay";
|
|
3
3
|
import { type GetServiceToken } from "./outbox-destinations/webhooks";
|
|
4
|
-
import type { WebhookInvoker } from "../types/AuthHeroConfig";
|
|
4
|
+
import type { OutboxPipelineConfig, WebhookInvoker } from "../types/AuthHeroConfig";
|
|
5
5
|
export interface CreateDefaultDestinationsConfig {
|
|
6
6
|
/**
|
|
7
7
|
* Data adapter — the `logs`, `hooks`, `users`, and `logStreams` adapters are
|
|
@@ -47,6 +47,12 @@ export interface CreateDefaultDestinationsConfig {
|
|
|
47
47
|
* failed per-request delivery would be silently skipped on retry.
|
|
48
48
|
*/
|
|
49
49
|
codeExecutor?: CodeExecutor;
|
|
50
|
+
/**
|
|
51
|
+
* Same shape as `init({ outbox: { pipeline } })`. When set, cron-drained
|
|
52
|
+
* events are also archived to the Cloudflare Pipelines stream, matching the
|
|
53
|
+
* per-request destination list. Omit to leave the archive out entirely.
|
|
54
|
+
*/
|
|
55
|
+
pipeline?: OutboxPipelineConfig;
|
|
50
56
|
}
|
|
51
57
|
/**
|
|
52
58
|
* Build the same array of outbox destinations that authhero's per-request
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { AuditEvent, AuditCategory } from "@authhero/adapter-interfaces";
|
|
2
|
+
import { EventDestination } from "../outbox-relay";
|
|
3
|
+
/**
|
|
4
|
+
* One row of the archive table. The promoted columns are the query and
|
|
5
|
+
* erasure keys (`actor_id` / `target_id`); `event` carries the untouched
|
|
6
|
+
* `AuditEvent` so nothing is lost and the promoted set can grow later
|
|
7
|
+
* without a backfill.
|
|
8
|
+
*
|
|
9
|
+
* `actor_id` is emitted as `null` rather than omitted when the actor is
|
|
10
|
+
* anonymous, so every record has the same key set — Pipelines stream
|
|
11
|
+
* schemas are fixed once created.
|
|
12
|
+
*/
|
|
13
|
+
export interface PipelineRecord {
|
|
14
|
+
id: string;
|
|
15
|
+
timestamp: string;
|
|
16
|
+
tenant_id: string;
|
|
17
|
+
event_type: string;
|
|
18
|
+
log_type: string;
|
|
19
|
+
category: AuditCategory;
|
|
20
|
+
actor_id: string | null;
|
|
21
|
+
target_type: string;
|
|
22
|
+
target_id: string;
|
|
23
|
+
event: AuditEvent;
|
|
24
|
+
}
|
|
25
|
+
export interface PipelineDestinationOptions {
|
|
26
|
+
/** Stream HTTP ingest endpoint, e.g. `https://<stream-id>.ingest.cloudflare.com`. */
|
|
27
|
+
endpoint: string;
|
|
28
|
+
/** Stream ingest token, sent as `Authorization: Bearer`. */
|
|
29
|
+
token: string;
|
|
30
|
+
/** Per-request timeout (default: 10s). */
|
|
31
|
+
timeoutMs?: number;
|
|
32
|
+
/** Override for tests. */
|
|
33
|
+
fetchImpl?: typeof fetch;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Archives audit events to a Cloudflare Pipelines stream, which lands them in
|
|
37
|
+
* R2 as an Iceberg table. See `apps/docs/architecture/audit-archive.md`.
|
|
38
|
+
*
|
|
39
|
+
* HTTP ingest is used rather than the Worker binding so the destination works
|
|
40
|
+
* in every deployment (Node included) and stays symmetric with the
|
|
41
|
+
* log-streams destination.
|
|
42
|
+
*
|
|
43
|
+
* Duplicates are expected and by design: the relay retries per event, not per
|
|
44
|
+
* destination, so a failure in a later destination re-delivers this one, and
|
|
45
|
+
* the Iceberg sink is append-only. Consumers dedup on `id` at query time.
|
|
46
|
+
*/
|
|
47
|
+
export declare class PipelineDestination implements EventDestination {
|
|
48
|
+
name: string;
|
|
49
|
+
private endpoint;
|
|
50
|
+
private token;
|
|
51
|
+
private timeoutMs;
|
|
52
|
+
private fetchImpl;
|
|
53
|
+
constructor(options: PipelineDestinationOptions);
|
|
54
|
+
/**
|
|
55
|
+
* Archives the audit trail, not the delivery plumbing: `hook.*` and
|
|
56
|
+
* `controlplane.sync.*` are instructions to other destinations rather than
|
|
57
|
+
* records of something a tenant did. Same filter as the log-streams
|
|
58
|
+
* destination.
|
|
59
|
+
*/
|
|
60
|
+
accepts(event: AuditEvent): boolean;
|
|
61
|
+
transform(event: AuditEvent): PipelineRecord;
|
|
62
|
+
deliver(records: PipelineRecord[]): Promise<void>;
|
|
63
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { OutboxAdapter, AuditEvent } from "@authhero/adapter-interfaces";
|
|
2
|
+
import type { OutboxMetricsSink } from "../types/OutboxMetrics";
|
|
2
3
|
/**
|
|
3
4
|
* Interface for outbox event destinations.
|
|
4
5
|
* Each destination transforms audit events into its own format and delivers them.
|
|
@@ -21,6 +22,7 @@ export interface EventDestination {
|
|
|
21
22
|
*/
|
|
22
23
|
export declare function processOutboxEvents(outbox: OutboxAdapter, ids: string[], destinations: EventDestination[], options?: {
|
|
23
24
|
maxRetries?: number;
|
|
25
|
+
metrics?: OutboxMetricsSink;
|
|
24
26
|
}): Promise<void>;
|
|
25
27
|
/**
|
|
26
28
|
* Drain unprocessed events from the outbox and deliver to all destinations.
|
|
@@ -31,4 +33,5 @@ export declare function drainOutbox(outbox: OutboxAdapter, destinations: EventDe
|
|
|
31
33
|
batchSize?: number;
|
|
32
34
|
maxRetries?: number;
|
|
33
35
|
retentionDays?: number;
|
|
36
|
+
metrics?: OutboxMetricsSink;
|
|
34
37
|
}): Promise<void>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Context } from "hono";
|
|
2
|
+
import { Bindings, Variables } from "../types";
|
|
3
|
+
/** RFC 9068 §2.2 + AuthHero-owned access-token claims. */
|
|
4
|
+
export declare const ACCESS_TOKEN_RESERVED_CLAIMS: readonly ["iss", "sub", "aud", "exp", "nbf", "iat", "jti", "client_id", "azp", "scope", "auth_time", "acr", "amr", "act", "sid", "permissions", "tenant_id", "org_id", "org_name", "requested_userinfo_claims", "gty"];
|
|
5
|
+
/** OIDC Core ID-token claims: the access-token set plus the ID-token-only ones. */
|
|
6
|
+
export declare const ID_TOKEN_RESERVED_CLAIMS: readonly ["iss", "sub", "aud", "exp", "nbf", "iat", "jti", "client_id", "azp", "scope", "auth_time", "acr", "amr", "act", "sid", "permissions", "tenant_id", "org_id", "org_name", "requested_userinfo_claims", "gty", "nonce", "at_hash", "c_hash", "s_hash"];
|
|
7
|
+
/**
|
|
8
|
+
* /userinfo response. The identity set only — the response body is otherwise
|
|
9
|
+
* made up of user profile claims, which hooks are expected to extend.
|
|
10
|
+
*/
|
|
11
|
+
export declare const USERINFO_RESERVED_CLAIMS: readonly ["iss", "sub", "aud", "exp", "nbf", "iat", "jti"];
|
|
12
|
+
/**
|
|
13
|
+
* Internal `auth-service` mints. Same set as an access token except `azp`:
|
|
14
|
+
* trusted internal hook code overrides it to attribute the call to a
|
|
15
|
+
* vendor/tenant for downstream APIs while `sub` stays `auth-service`.
|
|
16
|
+
* Client-bound mints keep `azp` locked (see below).
|
|
17
|
+
*/
|
|
18
|
+
export declare const SERVICE_TOKEN_RESERVED_CLAIMS: ("client_id" | "scope" | "sid" | "tenant_id" | "sub" | "org_name" | "permissions" | "iat" | "org_id" | "exp" | "iss" | "aud" | "auth_time" | "acr" | "amr" | "nbf" | "jti" | "act" | "requested_userinfo_claims" | "gty")[];
|
|
19
|
+
/** Client-bound mints: `azp` must stay the registered client id. */
|
|
20
|
+
export declare const CLIENT_SERVICE_TOKEN_RESERVED_CLAIMS: ("client_id" | "scope" | "sid" | "tenant_id" | "sub" | "org_name" | "permissions" | "iat" | "org_id" | "exp" | "iss" | "aud" | "auth_time" | "acr" | "amr" | "azp" | "nbf" | "jti" | "act" | "requested_userinfo_claims" | "gty")[];
|
|
21
|
+
export type AccessTokenReservedClaim = (typeof ACCESS_TOKEN_RESERVED_CLAIMS)[number];
|
|
22
|
+
export type IdTokenReservedClaim = (typeof ID_TOKEN_RESERVED_CLAIMS)[number];
|
|
23
|
+
/**
|
|
24
|
+
* The server-owned half of an access-token payload. Typing the payload literal
|
|
25
|
+
* as this makes TypeScript's excess-property check reject any claim name that
|
|
26
|
+
* isn't in `ACCESS_TOKEN_RESERVED_CLAIMS`, so a new server-owned claim cannot
|
|
27
|
+
* be added to the mint without also being reserved.
|
|
28
|
+
*/
|
|
29
|
+
export type ServerOwnedAccessTokenClaims = Partial<Record<AccessTokenReservedClaim, unknown>>;
|
|
30
|
+
/** As `ServerOwnedAccessTokenClaims`, for the ID token. */
|
|
31
|
+
export type ServerOwnedIdTokenClaims = Partial<Record<IdTokenReservedClaim, unknown>>;
|
|
32
|
+
/** Which payload a custom claim is being written to. */
|
|
33
|
+
export type ClaimPayloadKind = "access_token" | "id_token" | "userinfo" | "service_token" | "client_service_token";
|
|
34
|
+
export declare function isReservedClaim(claim: string, kind: ClaimPayloadKind): boolean;
|
|
35
|
+
export interface ApplyCustomClaimOptions {
|
|
36
|
+
/** Which payload is being written to — selects the reserved set. */
|
|
37
|
+
kind: ClaimPayloadKind;
|
|
38
|
+
/**
|
|
39
|
+
* Who is writing. Used in the warning so an operator can tell which hook
|
|
40
|
+
* dropped a claim (e.g. `onExecuteCredentialsExchange`,
|
|
41
|
+
* `template-hook:add-roles`, `createServiceToken`).
|
|
42
|
+
*/
|
|
43
|
+
source: string;
|
|
44
|
+
/** Request context, when there is one — the warning goes to the tenant log. */
|
|
45
|
+
ctx?: Context<{
|
|
46
|
+
Bindings: Bindings;
|
|
47
|
+
Variables: Variables;
|
|
48
|
+
}>;
|
|
49
|
+
/** Tenant to log against. Defaults to `ctx.var.tenant_id`. */
|
|
50
|
+
tenantId?: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Write a caller-supplied claim onto a payload unless the authorization server
|
|
54
|
+
* owns that claim name.
|
|
55
|
+
*
|
|
56
|
+
* @returns true when the claim was written, false when it was dropped.
|
|
57
|
+
*/
|
|
58
|
+
export declare function applyCustomClaim(payload: Record<string, unknown>, claim: string, value: unknown, options: ApplyCustomClaimOptions): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Bulk variant of `applyCustomClaim`. Returns a new object holding only the
|
|
61
|
+
* claims that are safe to merge, so callers can keep spreading them into a
|
|
62
|
+
* payload literal.
|
|
63
|
+
*/
|
|
64
|
+
export declare function applyCustomClaims(claims: Record<string, unknown> | undefined, options: ApplyCustomClaimOptions): Record<string, unknown> | undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CodeExecutor, DataAdapters } from "@authhero/adapter-interfaces";
|
|
2
2
|
import type { WebhookInvoker } from "../types/AuthHeroConfig";
|
|
3
|
+
import type { OutboxMetricsSink } from "../types/OutboxMetrics";
|
|
3
4
|
export interface RunOutboxRelayConfig {
|
|
4
5
|
/** Same `DataAdapters` passed to `init()`. Must include `outbox` to drain. */
|
|
5
6
|
dataAdapter: DataAdapters;
|
|
@@ -31,6 +32,13 @@ export interface RunOutboxRelayConfig {
|
|
|
31
32
|
* silently skipped.
|
|
32
33
|
*/
|
|
33
34
|
codeExecutor?: CodeExecutor;
|
|
35
|
+
/**
|
|
36
|
+
* Optional metrics sink — same shape as `init({ outbox: { metrics } })`.
|
|
37
|
+
* Receives `outbox_events_processed_total`,
|
|
38
|
+
* `outbox_events_dead_lettered_total` and `outbox_retry_delay_seconds` for
|
|
39
|
+
* the events this cron drain handles, tagged `source: "cron"`.
|
|
40
|
+
*/
|
|
41
|
+
metrics?: OutboxMetricsSink;
|
|
34
42
|
}
|
|
35
43
|
/**
|
|
36
44
|
* One-call outbox relay for cron / scheduled handlers.
|
|
@@ -8,6 +8,11 @@ export interface RunRetentionConfig {
|
|
|
8
8
|
outboxRetentionDays?: number;
|
|
9
9
|
/** Days of action execution history to keep. Default 30. */
|
|
10
10
|
actionExecutionsRetentionDays?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Hours to keep finished bulk user-import jobs and their staged rows.
|
|
13
|
+
* Default 24, matching Auth0's job-data retention.
|
|
14
|
+
*/
|
|
15
|
+
usersImportRetentionHours?: number;
|
|
11
16
|
/**
|
|
12
17
|
* Scope the session sweep to a single tenant. Codes and outbox events are
|
|
13
18
|
* always swept globally — an expired row is dead regardless of who owns it.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { PasswordInsert, UserInsert } from "@authhero/adapter-interfaces";
|
|
2
|
+
import { type UserImportEntry } from "../../types/auth0/UserImport";
|
|
3
|
+
/**
|
|
4
|
+
* Machine-readable per-row failure reasons, surfaced by the Auth0-compatible
|
|
5
|
+
* `GET /api/v2/jobs/{id}/errors` endpoint. Stable strings — clients branch on
|
|
6
|
+
* them, so treat these as API surface.
|
|
7
|
+
*/
|
|
8
|
+
export declare const IMPORT_ERROR_CODES: {
|
|
9
|
+
/** The entry did not match the import-file schema. */
|
|
10
|
+
readonly VALIDATION_ERROR: "VALIDATION_ERROR";
|
|
11
|
+
/** A well-formed hash in an algorithm AuthHero cannot verify. */
|
|
12
|
+
readonly UNSUPPORTED_HASH_ALGORITHM: "UNSUPPORTED_HASH_ALGORITHM";
|
|
13
|
+
/** bcrypt, but a variant or encoding that would never verify. */
|
|
14
|
+
readonly UNSUPPORTED_HASH_FORMAT: "UNSUPPORTED_HASH_FORMAT";
|
|
15
|
+
/** The same identity appears earlier in the same file. */
|
|
16
|
+
readonly DUPLICATE_ENTRY: "DUPLICATE_ENTRY";
|
|
17
|
+
/** The user already exists and `upsert` was not enabled. */
|
|
18
|
+
readonly USER_ALREADY_EXISTS: "USER_ALREADY_EXISTS";
|
|
19
|
+
/** The write itself failed. */
|
|
20
|
+
readonly INTERNAL_ERROR: "INTERNAL_ERROR";
|
|
21
|
+
};
|
|
22
|
+
export type ImportErrorCode = (typeof IMPORT_ERROR_CODES)[keyof typeof IMPORT_ERROR_CODES];
|
|
23
|
+
export interface ImportRowError {
|
|
24
|
+
code: ImportErrorCode;
|
|
25
|
+
message: string;
|
|
26
|
+
path?: string;
|
|
27
|
+
}
|
|
28
|
+
export type MappedPassword = Pick<PasswordInsert, "password" | "algorithm">;
|
|
29
|
+
export interface MappedEntry {
|
|
30
|
+
user: Omit<UserInsert, "connection"> & {
|
|
31
|
+
connection: string;
|
|
32
|
+
};
|
|
33
|
+
password?: MappedPassword;
|
|
34
|
+
}
|
|
35
|
+
export type MapResult = {
|
|
36
|
+
ok: true;
|
|
37
|
+
value: MappedEntry;
|
|
38
|
+
} | {
|
|
39
|
+
ok: false;
|
|
40
|
+
error: ImportRowError;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Resolve the entry's password into something AuthHero can actually verify.
|
|
44
|
+
*
|
|
45
|
+
* Returns `undefined` when the entry carries no credential at all — a valid
|
|
46
|
+
* case that produces a shell user who signs in via password reset or the
|
|
47
|
+
* upstream `import_mode` fallback.
|
|
48
|
+
*
|
|
49
|
+
* AuthHero verifies with `bcryptjs.compare`, so bcrypt is the only algorithm
|
|
50
|
+
* that can round-trip. Storing anything else would create a user who can
|
|
51
|
+
* never authenticate, so unsupported hashes fail the row instead.
|
|
52
|
+
*/
|
|
53
|
+
export declare function mapPassword(entry: UserImportEntry): {
|
|
54
|
+
ok: true;
|
|
55
|
+
value?: MappedPassword;
|
|
56
|
+
} | {
|
|
57
|
+
ok: false;
|
|
58
|
+
error: ImportRowError;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Build the stored `provider|id` identifier for an imported entry.
|
|
62
|
+
*
|
|
63
|
+
* Auth0 prefixes bare import ids with the connection's provider; AuthHero
|
|
64
|
+
* uses the tenant's resolved username-password provider (`auth0`, or `auth2`
|
|
65
|
+
* for tenants still pinned to the legacy value) so the password row lands on
|
|
66
|
+
* the identity the login path actually reads. An entry that already carries
|
|
67
|
+
* the prefix is not double-prefixed, and one with no id at all gets a
|
|
68
|
+
* generated one — the users table requires the column.
|
|
69
|
+
*
|
|
70
|
+
* Mirrors `POST /api/v2/users`, which derives the id exactly this way.
|
|
71
|
+
*/
|
|
72
|
+
export declare function buildUserId(userId: string | undefined, provider: string, fallbackId?: string): string;
|
|
73
|
+
/**
|
|
74
|
+
* Deterministic bare id for an import row that supplied no `user_id`.
|
|
75
|
+
*
|
|
76
|
+
* Retry safety depends on this. A driver can create a user and then die
|
|
77
|
+
* before committing that row's outcome, leaving the row `pending`; the next
|
|
78
|
+
* driver reprocesses it. With a random id the retry cannot tell its own
|
|
79
|
+
* half-finished write from a genuinely pre-existing user, and reports a
|
|
80
|
+
* successful import as `USER_ALREADY_EXISTS`. Deriving the id from
|
|
81
|
+
* `(operation_id, seq)` — both immutable for the life of the row — makes the
|
|
82
|
+
* retry regenerate the exact same id, so it can recognise its own work.
|
|
83
|
+
*
|
|
84
|
+
* Not a security boundary: the inputs are our own identifiers, so this only
|
|
85
|
+
* needs to be stable and collision-free, which a truncated SHA-256 is.
|
|
86
|
+
*/
|
|
87
|
+
export declare function deriveImportUserId(operationId: string, seq: number): Promise<string>;
|
|
88
|
+
export interface MapEntryParams {
|
|
89
|
+
entry: UserImportEntry;
|
|
90
|
+
/** Connection NAME (the users table stores the name, not the id). */
|
|
91
|
+
connection: string;
|
|
92
|
+
/** Resolved username-password provider for this tenant. */
|
|
93
|
+
provider: string;
|
|
94
|
+
/**
|
|
95
|
+
* Id to use when the entry supplies none. Pass the value from
|
|
96
|
+
* {@link deriveImportUserId} so a reprocessed row rebuilds the same id;
|
|
97
|
+
* omitting it falls back to a random one.
|
|
98
|
+
*/
|
|
99
|
+
fallbackUserId?: string;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Map one validated import entry onto the user and password rows to write.
|
|
103
|
+
* Pure — performs no I/O and makes no existence checks, so it is safe to call
|
|
104
|
+
* repeatedly when a chunk is retried.
|
|
105
|
+
*/
|
|
106
|
+
export declare function mapEntry({ entry, connection, provider, fallbackUserId, }: MapEntryParams): MapResult;
|
|
107
|
+
/**
|
|
108
|
+
* Strip credential material from a staged entry before it is returned by
|
|
109
|
+
* `GET /jobs/{id}/errors`.
|
|
110
|
+
*
|
|
111
|
+
* Redaction happens on the way OUT, not on the way in: the staged row is the
|
|
112
|
+
* work item, so it must keep the hash the job exists to import. What must
|
|
113
|
+
* never happen is echoing that hash back over the API, which is what this
|
|
114
|
+
* guards. The marker is left in place so an operator reading an error can
|
|
115
|
+
* still tell a credential was supplied.
|
|
116
|
+
*/
|
|
117
|
+
export declare function redactEntry(entry: unknown): Record<string, unknown>;
|
|
118
|
+
/**
|
|
119
|
+
* Normalize a raw file entry into the record shape a staged row stores,
|
|
120
|
+
* WITHOUT redacting: the row has to carry the credential it is going to
|
|
121
|
+
* import. {@link redactEntry} is applied when the row is read back out.
|
|
122
|
+
*/
|
|
123
|
+
export declare function toStagedPayload(entry: unknown): Record<string, unknown>;
|
|
124
|
+
/**
|
|
125
|
+
* Identity keys Auth0 dedupes an import file on: a repeat of any of these
|
|
126
|
+
* within one file is an error rather than a silent overwrite.
|
|
127
|
+
*/
|
|
128
|
+
export declare function entryIdentityKeys(entry: UserImportEntry): string[];
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { DataAdapters, TenantOperation } from "@authhero/adapter-interfaces";
|
|
2
|
+
/**
|
|
3
|
+
* How many staged rows one chunk processes.
|
|
4
|
+
*
|
|
5
|
+
* Sized against Cloudflare D1's per-invocation query cap (order of a
|
|
6
|
+
* thousand): a row costs up to four existence probes plus a user and a
|
|
7
|
+
* password write, so 50 rows leaves comfortable headroom for the
|
|
8
|
+
* surrounding reads and the outcome commit. Chunks are cheap — a smaller
|
|
9
|
+
* one only means more of them, while an oversized one fails the whole
|
|
10
|
+
* invocation.
|
|
11
|
+
*/
|
|
12
|
+
export declare const DEFAULT_CHUNK_SIZE = 50;
|
|
13
|
+
/** How long a driver's lease on an operation is valid. */
|
|
14
|
+
export declare const DEFAULT_LEASE_MS = 60000;
|
|
15
|
+
export interface UsersImportInput {
|
|
16
|
+
connection_id: string;
|
|
17
|
+
connection: string;
|
|
18
|
+
upsert: boolean;
|
|
19
|
+
external_id?: string;
|
|
20
|
+
send_completion_email?: boolean;
|
|
21
|
+
provider: string;
|
|
22
|
+
}
|
|
23
|
+
export interface AdvanceOptions {
|
|
24
|
+
/** Stop after this many rows, so a driver can bound its own runtime. */
|
|
25
|
+
maxRows?: number;
|
|
26
|
+
/** Rows per chunk; defaults to {@link DEFAULT_CHUNK_SIZE}. */
|
|
27
|
+
chunkSize?: number;
|
|
28
|
+
/** Lease duration for this driver's claim. */
|
|
29
|
+
leaseMs?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Wall-clock deadline (epoch ms). The driver stops cleanly at the next
|
|
32
|
+
* chunk boundary once passed, leaving the remainder `pending` for the
|
|
33
|
+
* next driver — never mid-chunk, so no work is half-committed.
|
|
34
|
+
*/
|
|
35
|
+
deadline?: number;
|
|
36
|
+
/** Identifies the lease holder; defaults to a random id. */
|
|
37
|
+
workerId?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface AdvanceResult {
|
|
40
|
+
/** True when no `pending` rows remain and the operation was finalized. */
|
|
41
|
+
done: boolean;
|
|
42
|
+
/** Rows this call committed an outcome for. */
|
|
43
|
+
processed: number;
|
|
44
|
+
/** Rows still `pending` after this call. */
|
|
45
|
+
remaining: number;
|
|
46
|
+
/** False when another live driver holds the lease. */
|
|
47
|
+
claimed: boolean;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Read the operation's `input` back into a typed shape. The row is written
|
|
51
|
+
* by the accept route, so a malformed one means the operation is
|
|
52
|
+
* unrunnable rather than that the caller made a mistake.
|
|
53
|
+
*/
|
|
54
|
+
export declare function parseImportInput(operation: TenantOperation): UsersImportInput | null;
|
|
55
|
+
/**
|
|
56
|
+
* Advance a `users_import` operation by processing staged rows until it is
|
|
57
|
+
* finished or the caller's budget runs out.
|
|
58
|
+
*
|
|
59
|
+
* This is the whole execution model. Every engine — an inline kick from the
|
|
60
|
+
* accepting request, a cron sweep, a Cloudflare Workflow step — calls this
|
|
61
|
+
* same function; they differ only in how much budget they pass and how often
|
|
62
|
+
* they call it. Durability comes from the database, not from the caller:
|
|
63
|
+
* outcomes are committed chunk by chunk, so a driver that dies loses at most
|
|
64
|
+
* the chunk in flight, and those rows stay `pending` for whoever runs next.
|
|
65
|
+
*/
|
|
66
|
+
export declare function advanceUsersImport(data: DataAdapters, operationId: string, options?: AdvanceOptions): Promise<AdvanceResult>;
|
|
67
|
+
/** Auth0's job summary shape, derived from the staged-row counts. */
|
|
68
|
+
export declare function buildSummary(counts: {
|
|
69
|
+
total: number;
|
|
70
|
+
inserted: number;
|
|
71
|
+
updated: number;
|
|
72
|
+
failed: number;
|
|
73
|
+
}): Record<string, number>;
|
|
74
|
+
export interface ResumeUsersImportsOptions extends Omit<AdvanceOptions, "workerId"> {
|
|
75
|
+
/** Maximum operations to advance in one sweep. */
|
|
76
|
+
maxOperations?: number;
|
|
77
|
+
}
|
|
78
|
+
export interface ResumeUsersImportsResult {
|
|
79
|
+
scanned: number;
|
|
80
|
+
advanced: number;
|
|
81
|
+
completed: number;
|
|
82
|
+
errors: number;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Resume every unfinished bulk import that no live driver is working on.
|
|
86
|
+
*
|
|
87
|
+
* This is what makes the feature durable regardless of deployment. Wire it
|
|
88
|
+
* to a scheduled handler alongside `runRetention`: whatever started an
|
|
89
|
+
* import — a request that timed out, a worker that was evicted, a process
|
|
90
|
+
* that was redeployed mid-run — the sweep picks the job back up and carries
|
|
91
|
+
* it to completion from the last committed chunk.
|
|
92
|
+
*
|
|
93
|
+
* One operation's failure never aborts the sweep.
|
|
94
|
+
*/
|
|
95
|
+
export declare function resumeUsersImports(data: DataAdapters, options?: ResumeUsersImportsOptions): Promise<ResumeUsersImportsResult>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { DataAdapters } from "@authhero/adapter-interfaces";
|
|
2
|
+
/**
|
|
3
|
+
* Auth0 deletes all job-related data 24 hours after the job is created.
|
|
4
|
+
*/
|
|
5
|
+
export declare const DEFAULT_IMPORT_JOB_RETENTION_HOURS = 24;
|
|
6
|
+
export interface UsersImportCleanupParams {
|
|
7
|
+
/** Hours to keep finished import jobs. Defaults to Auth0's 24. */
|
|
8
|
+
retentionHours?: number;
|
|
9
|
+
/** Maximum jobs to delete per sweep. */
|
|
10
|
+
limit?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Delete finished bulk-import jobs (and, by cascade, their staged rows)
|
|
14
|
+
* older than the retention window.
|
|
15
|
+
*
|
|
16
|
+
* Staged rows hold the submitted user entries, so this is a privacy control
|
|
17
|
+
* as much as a housekeeping one — though credential material is redacted
|
|
18
|
+
* before staging rather than relying on this sweep.
|
|
19
|
+
*
|
|
20
|
+
* Only terminal jobs are removed: an unfinished import may legitimately be
|
|
21
|
+
* older than the window (a very large file, or one that has been waiting on
|
|
22
|
+
* a resume sweep), and deleting it would strand the users it had not yet
|
|
23
|
+
* created. Returns the number of jobs deleted, or `null` when the
|
|
24
|
+
* deployment has no tenant-operations adapter.
|
|
25
|
+
*/
|
|
26
|
+
export declare function cleanupUsersImports(data: DataAdapters, params?: UsersImportCleanupParams): Promise<number | null>;
|