alchemy 0.93.4 → 0.93.7

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.
Files changed (73) hide show
  1. package/bin/alchemy.js +1966 -1825
  2. package/bin/alchemy.ts +2 -0
  3. package/bin/commands/state.ts +58 -0
  4. package/bin/services/execute-alchemy.ts +9 -1
  5. package/lib/alchemy.d.ts.map +1 -1
  6. package/lib/alchemy.js +7 -0
  7. package/lib/alchemy.js.map +1 -1
  8. package/lib/cloudflare/access-application.d.ts +204 -0
  9. package/lib/cloudflare/access-application.d.ts.map +1 -0
  10. package/lib/cloudflare/access-application.js +205 -0
  11. package/lib/cloudflare/access-application.js.map +1 -0
  12. package/lib/cloudflare/access-group.d.ts +115 -0
  13. package/lib/cloudflare/access-group.d.ts.map +1 -0
  14. package/lib/cloudflare/access-group.js +147 -0
  15. package/lib/cloudflare/access-group.js.map +1 -0
  16. package/lib/cloudflare/access-identity-provider.d.ts +261 -0
  17. package/lib/cloudflare/access-identity-provider.d.ts.map +1 -0
  18. package/lib/cloudflare/access-identity-provider.js +229 -0
  19. package/lib/cloudflare/access-identity-provider.js.map +1 -0
  20. package/lib/cloudflare/access-policy.d.ts +168 -0
  21. package/lib/cloudflare/access-policy.d.ts.map +1 -0
  22. package/lib/cloudflare/access-policy.js +176 -0
  23. package/lib/cloudflare/access-policy.js.map +1 -0
  24. package/lib/cloudflare/access-rule.d.ts +248 -0
  25. package/lib/cloudflare/access-rule.d.ts.map +1 -0
  26. package/lib/cloudflare/access-rule.js +37 -0
  27. package/lib/cloudflare/access-rule.js.map +1 -0
  28. package/lib/cloudflare/access-service-token.d.ts +86 -0
  29. package/lib/cloudflare/access-service-token.d.ts.map +1 -0
  30. package/lib/cloudflare/access-service-token.js +127 -0
  31. package/lib/cloudflare/access-service-token.js.map +1 -0
  32. package/lib/cloudflare/index.d.ts +6 -0
  33. package/lib/cloudflare/index.d.ts.map +1 -1
  34. package/lib/cloudflare/index.js +6 -0
  35. package/lib/cloudflare/index.js.map +1 -1
  36. package/lib/cloudflare/miniflare/build-worker-options.d.ts.map +1 -1
  37. package/lib/cloudflare/miniflare/build-worker-options.js +17 -3
  38. package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
  39. package/lib/state/file-system-state-store.d.ts +1 -0
  40. package/lib/state/file-system-state-store.d.ts.map +1 -1
  41. package/lib/state/file-system-state-store.js +16 -0
  42. package/lib/state/file-system-state-store.js.map +1 -1
  43. package/lib/state/operations.d.ts +1 -0
  44. package/lib/state/operations.d.ts.map +1 -1
  45. package/lib/state/operations.js +24 -0
  46. package/lib/state/operations.js.map +1 -1
  47. package/lib/state/proxy.d.ts +1 -0
  48. package/lib/state/proxy.d.ts.map +1 -1
  49. package/lib/state/proxy.js +3 -0
  50. package/lib/state/proxy.js.map +1 -1
  51. package/lib/state-cli.d.ts +9 -0
  52. package/lib/state-cli.d.ts.map +1 -0
  53. package/lib/state-cli.js +110 -0
  54. package/lib/state-cli.js.map +1 -0
  55. package/lib/state.d.ts +2 -0
  56. package/lib/state.d.ts.map +1 -1
  57. package/package.json +1 -1
  58. package/src/alchemy.ts +9 -0
  59. package/src/cloudflare/access-application.ts +489 -0
  60. package/src/cloudflare/access-group.ts +271 -0
  61. package/src/cloudflare/access-identity-provider.ts +584 -0
  62. package/src/cloudflare/access-policy.ts +392 -0
  63. package/src/cloudflare/access-rule.ts +307 -0
  64. package/src/cloudflare/access-service-token.ts +255 -0
  65. package/src/cloudflare/index.ts +6 -0
  66. package/src/cloudflare/miniflare/build-worker-options.ts +20 -3
  67. package/src/state/file-system-state-store.ts +16 -0
  68. package/src/state/operations.ts +22 -0
  69. package/src/state/proxy.ts +4 -0
  70. package/src/state-cli.ts +134 -0
  71. package/src/state.ts +2 -0
  72. package/workers/cloudflare-state-store.js +17 -0
  73. package/workers/tunnel-proxy.js +1 -1
@@ -0,0 +1,115 @@
1
+ import type { Context } from "../context.ts";
2
+ import { type AccessRule } from "./access-rule.ts";
3
+ import { type CloudflareApiOptions } from "./api.ts";
4
+ /**
5
+ * Properties for creating or updating an {@link AccessGroup}.
6
+ */
7
+ export interface AccessGroupProps extends CloudflareApiOptions {
8
+ /**
9
+ * Display name of the group.
10
+ *
11
+ * @default ${app}-${stage}-${id}
12
+ */
13
+ name?: string;
14
+ /**
15
+ * Rules that grant membership (OR logic — any match includes the user).
16
+ */
17
+ include?: AccessRule[];
18
+ /**
19
+ * Rules that revoke membership when matched.
20
+ */
21
+ exclude?: AccessRule[];
22
+ /**
23
+ * Rules that must additionally match for membership (AND logic).
24
+ */
25
+ require?: AccessRule[];
26
+ /**
27
+ * Mark this group as the account default. Default groups apply to every
28
+ * Access application unless explicitly overridden.
29
+ *
30
+ * @default false
31
+ */
32
+ isDefault?: boolean;
33
+ /**
34
+ * Adopt an existing group with the same name instead of failing.
35
+ *
36
+ * @default false
37
+ */
38
+ adopt?: boolean;
39
+ /**
40
+ * Whether to delete the group when removed from Alchemy.
41
+ *
42
+ * @default true
43
+ */
44
+ delete?: boolean;
45
+ }
46
+ /**
47
+ * Output for an {@link AccessGroup}.
48
+ */
49
+ export type AccessGroup = Omit<AccessGroupProps, "adopt" | "delete"> & {
50
+ /** Cloudflare-assigned group UUID. */
51
+ id: string;
52
+ /** Display name. */
53
+ name: string;
54
+ /** ISO 8601 creation timestamp. */
55
+ createdAt: string;
56
+ /** ISO 8601 last-update timestamp. */
57
+ updatedAt: string;
58
+ };
59
+ /**
60
+ * Type guard for {@link AccessGroup}.
61
+ */
62
+ export declare function isAccessGroup(resource: any): resource is AccessGroup;
63
+ /**
64
+ * Creates a Cloudflare Zero Trust [Access group](https://developers.cloudflare.com/cloudflare-one/identity/users/groups/),
65
+ * a reusable bundle of rules that can be referenced by Access policies.
66
+ *
67
+ * @example
68
+ * // Engineering team by email domain.
69
+ * const engineering = await AccessGroup("engineering", {
70
+ * name: "Engineering",
71
+ * include: [{ email_domain: { domain: "acme.com" } }],
72
+ * });
73
+ *
74
+ * @example
75
+ * // Allow a managed IP list, exclude one specific IP.
76
+ * const officeIps = await AccessGroup("office", {
77
+ * name: "Office IPs",
78
+ * include: [{ ip_list: { id: "<list-uuid>" } }],
79
+ * exclude: [{ ip: { ip: "203.0.113.99/32" } }],
80
+ * });
81
+ *
82
+ * @example
83
+ * // Compose groups: admins are engineers who are also on-call. Resources
84
+ * // can be passed directly — Alchemy lifts `.id` at the wire boundary.
85
+ * const onCall = await AccessGroup("on-call", {
86
+ * include: [{ email_domain: { domain: "acme.com" } }],
87
+ * });
88
+ * const admins = await AccessGroup("admins", {
89
+ * include: [{ group: { id: engineering } }],
90
+ * require: [{ group: { id: onCall } }],
91
+ * });
92
+ *
93
+ * @example
94
+ * // IdP-bound rules — match Okta groups via an AccessIdentityProvider.
95
+ * const okta = await AccessIdentityProvider("okta", {
96
+ * type: "okta",
97
+ * name: "Acme Okta",
98
+ * oktaAccount: "acme.okta.com",
99
+ * clientId: "...",
100
+ * clientSecret: alchemy.secret.env.OKTA_SECRET,
101
+ * });
102
+ * const sre = await AccessGroup("sre", {
103
+ * include: [{ okta: { name: "sre", identity_provider_id: okta } }],
104
+ * });
105
+ *
106
+ * @example
107
+ * // Account default — applied implicitly to every Access application.
108
+ * await AccessGroup("default-deny", {
109
+ * isDefault: true,
110
+ * include: [{ everyone: {} }],
111
+ * exclude: [{ email_domain: { domain: "acme.com" } }],
112
+ * });
113
+ */
114
+ export declare const AccessGroup: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<AccessGroup>, id: string, props: AccessGroupProps) => Promise<AccessGroup>);
115
+ //# sourceMappingURL=access-group.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-group.d.ts","sourceRoot":"","sources":["../../src/cloudflare/access-group.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG7C,OAAO,EAAuB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAMxE,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,UAAU,CAAC;AAElB;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB;IAC5D;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IAEvB;;OAEG;IACH,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IAEvB;;OAEG;IACH,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IAEvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,EAAE,OAAO,GAAG,QAAQ,CAAC,GAAG;IACrE,sCAAsC;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,GAAG,GAAG,QAAQ,IAAI,WAAW,CAEpE;AAaD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,eAAO,MAAM,WAAW,yFAGd,OAAO,CAAC,WAAW,CAAC,MACtB,MAAM,SACH,gBAAgB,KACtB,OAAO,CAAC,WAAW,CAAC,CAkExB,CAAC"}
@@ -0,0 +1,147 @@
1
+ import { Resource, ResourceKind } from "../resource.js";
2
+ import { logger } from "../util/logger.js";
3
+ import { serializeAccessRule } from "./access-rule.js";
4
+ import { isCloudflareApiError } from "./api-error.js";
5
+ import { extractCloudflareResult, } from "./api-response.js";
6
+ import { createCloudflareApi, } from "./api.js";
7
+ /**
8
+ * Type guard for {@link AccessGroup}.
9
+ */
10
+ export function isAccessGroup(resource) {
11
+ return resource?.[ResourceKind] === "cloudflare::AccessGroup";
12
+ }
13
+ /**
14
+ * Creates a Cloudflare Zero Trust [Access group](https://developers.cloudflare.com/cloudflare-one/identity/users/groups/),
15
+ * a reusable bundle of rules that can be referenced by Access policies.
16
+ *
17
+ * @example
18
+ * // Engineering team by email domain.
19
+ * const engineering = await AccessGroup("engineering", {
20
+ * name: "Engineering",
21
+ * include: [{ email_domain: { domain: "acme.com" } }],
22
+ * });
23
+ *
24
+ * @example
25
+ * // Allow a managed IP list, exclude one specific IP.
26
+ * const officeIps = await AccessGroup("office", {
27
+ * name: "Office IPs",
28
+ * include: [{ ip_list: { id: "<list-uuid>" } }],
29
+ * exclude: [{ ip: { ip: "203.0.113.99/32" } }],
30
+ * });
31
+ *
32
+ * @example
33
+ * // Compose groups: admins are engineers who are also on-call. Resources
34
+ * // can be passed directly — Alchemy lifts `.id` at the wire boundary.
35
+ * const onCall = await AccessGroup("on-call", {
36
+ * include: [{ email_domain: { domain: "acme.com" } }],
37
+ * });
38
+ * const admins = await AccessGroup("admins", {
39
+ * include: [{ group: { id: engineering } }],
40
+ * require: [{ group: { id: onCall } }],
41
+ * });
42
+ *
43
+ * @example
44
+ * // IdP-bound rules — match Okta groups via an AccessIdentityProvider.
45
+ * const okta = await AccessIdentityProvider("okta", {
46
+ * type: "okta",
47
+ * name: "Acme Okta",
48
+ * oktaAccount: "acme.okta.com",
49
+ * clientId: "...",
50
+ * clientSecret: alchemy.secret.env.OKTA_SECRET,
51
+ * });
52
+ * const sre = await AccessGroup("sre", {
53
+ * include: [{ okta: { name: "sre", identity_provider_id: okta } }],
54
+ * });
55
+ *
56
+ * @example
57
+ * // Account default — applied implicitly to every Access application.
58
+ * await AccessGroup("default-deny", {
59
+ * isDefault: true,
60
+ * include: [{ everyone: {} }],
61
+ * exclude: [{ email_domain: { domain: "acme.com" } }],
62
+ * });
63
+ */
64
+ export const AccessGroup = Resource("cloudflare::AccessGroup", async function (id, props) {
65
+ const api = await createCloudflareApi(props);
66
+ const name = props.name ?? this.scope.createPhysicalName(id);
67
+ const basePath = `/accounts/${api.accountId}/access/groups`;
68
+ if (this.phase === "delete") {
69
+ if (this.output?.id && props.delete !== false) {
70
+ await deleteAccessGroup(api, this.output.id);
71
+ }
72
+ return this.destroy();
73
+ }
74
+ const body = {
75
+ name,
76
+ include: (props.include ?? []).map(serializeAccessRule),
77
+ exclude: (props.exclude ?? []).map(serializeAccessRule),
78
+ require: (props.require ?? []).map(serializeAccessRule),
79
+ is_default: props.isDefault ?? false,
80
+ };
81
+ let group;
82
+ if (this.phase === "update" && this.output?.id) {
83
+ group = await extractCloudflareResult(`update access group "${name}"`, api.put(`${basePath}/${this.output.id}`, body));
84
+ }
85
+ else {
86
+ const adopt = props.adopt ?? this.scope.adopt;
87
+ try {
88
+ group = await extractCloudflareResult(`create access group "${name}"`, api.post(basePath, body));
89
+ }
90
+ catch (err) {
91
+ if (adopt && isAccessDuplicateNameError(err)) {
92
+ const existing = await findAccessGroupByName(api, name);
93
+ if (!existing) {
94
+ throw new Error(`Access group "${name}" already exists but could not be found for adoption.`, { cause: err });
95
+ }
96
+ logger.log(`Adopting existing access group "${name}" (${existing.id})`);
97
+ group = await extractCloudflareResult(`adopt access group "${name}"`, api.put(`${basePath}/${existing.id}`, body));
98
+ }
99
+ else {
100
+ throw err;
101
+ }
102
+ }
103
+ }
104
+ return {
105
+ id: group.id,
106
+ name: group.name,
107
+ include: props.include,
108
+ exclude: props.exclude,
109
+ require: props.require,
110
+ isDefault: group.is_default,
111
+ createdAt: group.created_at,
112
+ updatedAt: group.updated_at,
113
+ };
114
+ });
115
+ function isAccessDuplicateNameError(err) {
116
+ if (isCloudflareApiError(err, { status: 409 }) ||
117
+ isCloudflareApiError(err, { status: 400 })) {
118
+ const data = err.errorData;
119
+ return (Array.isArray(data) &&
120
+ data.some((e) => "message" in e && /already exists/i.test(String(e.message))));
121
+ }
122
+ return false;
123
+ }
124
+ async function findAccessGroupByName(api, name) {
125
+ let page = 1;
126
+ const perPage = 50;
127
+ while (true) {
128
+ const response = await api.get(`/accounts/${api.accountId}/access/groups?page=${page}&per_page=${perPage}`);
129
+ if (!response.ok)
130
+ return null;
131
+ const data = (await response.json());
132
+ const match = data.result.find((g) => g.name === name);
133
+ if (match)
134
+ return match;
135
+ const info = data.result_info;
136
+ if (!info || info.page * info.per_page >= info.total_count)
137
+ return null;
138
+ page++;
139
+ }
140
+ }
141
+ async function deleteAccessGroup(api, groupId) {
142
+ const response = await api.delete(`/accounts/${api.accountId}/access/groups/${groupId}`);
143
+ if (!response.ok && response.status !== 404) {
144
+ logger.error(`Error deleting access group ${groupId}: ${response.status} ${response.statusText}`);
145
+ }
146
+ }
147
+ //# sourceMappingURL=access-group.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-group.js","sourceRoot":"","sources":["../../src/cloudflare/access-group.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,261 @@
1
+ import type { Context } from "../context.ts";
2
+ import { Secret } from "../secret.ts";
3
+ import { type CloudflareApiOptions } from "./api.ts";
4
+ /**
5
+ * Supported Access identity provider types. The five most common providers
6
+ * have strict {@link AccessIdentityProviderProps} variants below; everything
7
+ * else falls back to {@link OtherIdentityProviderProps} with a permissive
8
+ * `config` shape.
9
+ */
10
+ export type AccessIdentityProviderType = "onetimepin" | "google" | "google-apps" | "github" | "okta" | "azureAD" | "oidc" | "saml" | "centrify" | "facebook" | "linkedin" | "onelogin" | "pingone" | "yandex" | (string & {});
11
+ interface BaseAccessIdpProps extends CloudflareApiOptions {
12
+ /**
13
+ * Display name shown on the Access login page.
14
+ *
15
+ * @default ${app}-${stage}-${id}
16
+ */
17
+ name?: string;
18
+ /**
19
+ * Adopt an existing IdP with the same name instead of failing.
20
+ *
21
+ * @default false
22
+ */
23
+ adopt?: boolean;
24
+ /**
25
+ * Whether to delete the IdP when removed from Alchemy.
26
+ *
27
+ * @default true
28
+ */
29
+ delete?: boolean;
30
+ }
31
+ /**
32
+ * One-Time PIN — Cloudflare emails a code to the user. No external IdP
33
+ * configuration required.
34
+ */
35
+ export interface OneTimePinIdentityProviderProps extends BaseAccessIdpProps {
36
+ type: "onetimepin";
37
+ }
38
+ /**
39
+ * Google OAuth identity provider.
40
+ */
41
+ export interface GoogleIdentityProviderProps extends BaseAccessIdpProps {
42
+ type: "google";
43
+ /**
44
+ * OAuth 2.0 client ID issued by Google for your application.
45
+ * This is a public identifier (not a secret).
46
+ */
47
+ clientId: string;
48
+ /**
49
+ * OAuth 2.0 client secret issued by Google. Use {@link alchemy.secret} so
50
+ * the value is encrypted at rest in the Alchemy state file.
51
+ */
52
+ clientSecret: string | Secret;
53
+ /**
54
+ * Custom claims to request from the IdP and forward into the Access JWT.
55
+ */
56
+ claims?: string[];
57
+ /**
58
+ * Override the OIDC claim Cloudflare reads as the user's email
59
+ * (defaults to `email`).
60
+ */
61
+ emailClaimName?: string;
62
+ }
63
+ /**
64
+ * Okta identity provider (OIDC under the hood).
65
+ */
66
+ export interface OktaIdentityProviderProps extends BaseAccessIdpProps {
67
+ type: "okta";
68
+ /**
69
+ * Your Okta tenant subdomain, e.g. `acme` for `acme.okta.com`.
70
+ */
71
+ oktaAccount: string;
72
+ /**
73
+ * Custom Okta authorization server ID. Omit to use Okta's default
74
+ * authorization server.
75
+ */
76
+ authorizationServerId?: string;
77
+ /**
78
+ * OAuth 2.0 client ID of the Okta app integration.
79
+ */
80
+ clientId: string;
81
+ /**
82
+ * OAuth 2.0 client secret of the Okta app integration. Use
83
+ * {@link alchemy.secret} for at-rest encryption.
84
+ */
85
+ clientSecret: string | Secret;
86
+ /**
87
+ * Custom claims to request from Okta and forward into the Access JWT.
88
+ */
89
+ claims?: string[];
90
+ /**
91
+ * Override the OIDC claim Cloudflare reads as the user's email
92
+ * (defaults to `email`).
93
+ */
94
+ emailClaimName?: string;
95
+ }
96
+ /**
97
+ * Generic OpenID Connect identity provider.
98
+ */
99
+ export interface OidcIdentityProviderProps extends BaseAccessIdpProps {
100
+ type: "oidc";
101
+ /**
102
+ * IdP authorization endpoint URL (the page users are redirected to to
103
+ * sign in).
104
+ */
105
+ authUrl: string;
106
+ /**
107
+ * IdP token endpoint URL (used by Cloudflare to exchange the auth code
108
+ * for tokens).
109
+ */
110
+ tokenUrl: string;
111
+ /**
112
+ * JWKS endpoint URL — public keys Cloudflare uses to verify ID-token
113
+ * signatures.
114
+ */
115
+ certsUrl: string;
116
+ /**
117
+ * OAuth 2.0 client ID registered with the IdP.
118
+ */
119
+ clientId: string;
120
+ /**
121
+ * OAuth 2.0 client secret registered with the IdP. Use
122
+ * {@link alchemy.secret} for at-rest encryption.
123
+ */
124
+ clientSecret: string | Secret;
125
+ /**
126
+ * OIDC scopes to request. Defaults to `["openid", "email", "profile"]`
127
+ * server-side if omitted.
128
+ */
129
+ scopes?: string[];
130
+ /**
131
+ * Custom claims to request from the IdP and forward into the Access JWT.
132
+ */
133
+ claims?: string[];
134
+ /**
135
+ * Override the OIDC claim Cloudflare reads as the user's email
136
+ * (defaults to `email`).
137
+ */
138
+ emailClaimName?: string;
139
+ /**
140
+ * Enable PKCE on the authorization code flow. Recommended for public
141
+ * clients and required by some IdPs.
142
+ */
143
+ pkceEnabled?: boolean;
144
+ }
145
+ /**
146
+ * Generic SAML 2.0 identity provider.
147
+ */
148
+ export interface SamlIdentityProviderProps extends BaseAccessIdpProps {
149
+ type: "saml";
150
+ /**
151
+ * SAML issuer (entity ID) of the IdP, used to validate the `Issuer`
152
+ * element of incoming assertions.
153
+ */
154
+ issuerUrl: string;
155
+ /**
156
+ * IdP single sign-on URL — Cloudflare redirects users here to start
157
+ * the SAML flow.
158
+ */
159
+ ssoTargetUrl: string;
160
+ /**
161
+ * PEM-encoded x509 certificates the IdP will use to sign assertions.
162
+ * Multiple entries support certificate rotation.
163
+ */
164
+ idpPublicCerts: string[];
165
+ /**
166
+ * SAML attributes to forward from the assertion into the Access JWT.
167
+ */
168
+ attributes?: string[];
169
+ /**
170
+ * Override the SAML attribute Cloudflare reads as the user's email
171
+ * (defaults to `email`).
172
+ */
173
+ emailAttributeName?: string;
174
+ /**
175
+ * Map SAML attributes to HTTP headers Cloudflare will inject when
176
+ * forwarding requests to the origin.
177
+ */
178
+ headerAttributes?: {
179
+ headerName: string;
180
+ attributeName: string;
181
+ }[];
182
+ /**
183
+ * Sign outgoing AuthnRequests with Cloudflare's signing key.
184
+ */
185
+ signRequest?: boolean;
186
+ }
187
+ /**
188
+ * Catch-all for IdP types not covered by a strict variant
189
+ * (`azureAD`, `github`, `google-apps`, `centrify`, `facebook`, `linkedin`,
190
+ * `onelogin`, `pingone`, `yandex`, or future providers).
191
+ *
192
+ * Pass a free-form camelCase `config` object — keys are converted to
193
+ * snake_case at the API boundary. This nested escape hatch is an
194
+ * intentional exception to the flat-props convention used by the strict
195
+ * variants above.
196
+ */
197
+ export interface OtherIdentityProviderProps extends BaseAccessIdpProps {
198
+ type: Exclude<AccessIdentityProviderType, "onetimepin" | "google" | "okta" | "oidc" | "saml">;
199
+ /**
200
+ * Free-form provider configuration. Use {@link alchemy.secret} for any
201
+ * sensitive values; they are unwrapped before sending to Cloudflare.
202
+ */
203
+ config: {
204
+ clientId?: string;
205
+ clientSecret?: string | Secret;
206
+ } & Record<string, unknown>;
207
+ }
208
+ /**
209
+ * Properties for creating or updating an {@link AccessIdentityProvider}.
210
+ */
211
+ export type AccessIdentityProviderProps = OneTimePinIdentityProviderProps | GoogleIdentityProviderProps | OktaIdentityProviderProps | OidcIdentityProviderProps | SamlIdentityProviderProps | OtherIdentityProviderProps;
212
+ /**
213
+ * Output for an {@link AccessIdentityProvider}.
214
+ */
215
+ export type AccessIdentityProvider = Omit<AccessIdentityProviderProps, "adopt" | "delete"> & {
216
+ /** Cloudflare-assigned IdP UUID. */
217
+ id: string;
218
+ /** Display name. */
219
+ name: string;
220
+ };
221
+ /**
222
+ * Type guard for {@link AccessIdentityProvider}.
223
+ */
224
+ export declare function isAccessIdentityProvider(resource: any): resource is AccessIdentityProvider;
225
+ /**
226
+ * Creates a Cloudflare Zero Trust [Access identity provider](https://developers.cloudflare.com/cloudflare-one/identity/idp-integration/)
227
+ * which lets users sign in to Access-protected applications.
228
+ *
229
+ * @example
230
+ * // Built-in One-Time PIN (no IdP setup required).
231
+ * const otp = await AccessIdentityProvider("otp", {
232
+ * type: "onetimepin",
233
+ * name: "Email OTP",
234
+ * });
235
+ *
236
+ * @example
237
+ * // Google OAuth. clientId is a public OAuth identifier (not a secret).
238
+ * const google = await AccessIdentityProvider("google", {
239
+ * type: "google",
240
+ * name: "Google",
241
+ * clientId: process.env.GOOGLE_CLIENT_ID!,
242
+ * clientSecret: alchemy.secret.env.GOOGLE_CLIENT_SECRET,
243
+ * });
244
+ *
245
+ * @example
246
+ * // Generic OIDC provider.
247
+ * const oidc = await AccessIdentityProvider("idp", {
248
+ * type: "oidc",
249
+ * name: "Corporate IdP",
250
+ * authUrl: "https://idp.example.com/oauth2/authorize",
251
+ * tokenUrl: "https://idp.example.com/oauth2/token",
252
+ * certsUrl: "https://idp.example.com/oauth2/certs",
253
+ * clientId: "my-app",
254
+ * clientSecret: alchemy.secret.env.IDP_CLIENT_SECRET,
255
+ * scopes: ["openid", "email", "profile"],
256
+ * pkceEnabled: true,
257
+ * });
258
+ */
259
+ export declare const AccessIdentityProvider: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<AccessIdentityProvider>, id: string, props: AccessIdentityProviderProps) => Promise<AccessIdentityProvider>);
260
+ export {};
261
+ //# sourceMappingURL=access-identity-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-identity-provider.d.ts","sourceRoot":"","sources":["../../src/cloudflare/access-identity-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAOtC,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,UAAU,CAAC;AAElB;;;;;GAKG;AACH,MAAM,MAAM,0BAA0B,GAClC,YAAY,GACZ,QAAQ,GACR,aAAa,GACb,QAAQ,GACR,MAAM,GACN,SAAS,GACT,MAAM,GACN,MAAM,GACN,UAAU,GACV,UAAU,GACV,UAAU,GACV,UAAU,GACV,SAAS,GACT,QAAQ,GACR,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB,UAAU,kBAAmB,SAAQ,oBAAoB;IACvD;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,+BAAgC,SAAQ,kBAAkB;IACzE,IAAI,EAAE,YAAY,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,2BAA4B,SAAQ,kBAAkB;IACrE,IAAI,EAAE,QAAQ,CAAC;IAEf;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC;IAE9B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,yBAA0B,SAAQ,kBAAkB;IACnE,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC;IAE9B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,yBAA0B,SAAQ,kBAAkB;IACnE,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC;IAE9B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,yBAA0B,SAAQ,kBAAkB;IACnE,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,cAAc,EAAE,MAAM,EAAE,CAAC;IAEzB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;;OAGG;IACH,gBAAgB,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAEnE;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,0BAA2B,SAAQ,kBAAkB;IACpE,IAAI,EAAE,OAAO,CACX,0BAA0B,EAC1B,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CACnD,CAAC;IAEF;;;OAGG;IACH,MAAM,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,GAAG,MAAM,CACpE,MAAM,EACN,OAAO,CACR,CAAC;CACH;AAED;;GAEG;AACH,MAAM,MAAM,2BAA2B,GACnC,+BAA+B,GAC/B,2BAA2B,GAC3B,yBAAyB,GACzB,yBAAyB,GACzB,yBAAyB,GACzB,0BAA0B,CAAC;AAE/B;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,IAAI,CACvC,2BAA2B,EAC3B,OAAO,GAAG,QAAQ,CACnB,GAAG;IACF,oCAAoC;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,GAAG,GACZ,QAAQ,IAAI,sBAAsB,CAEpC;AASD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,sBAAsB,yFAGzB,OAAO,CAAC,sBAAsB,CAAC,MACjC,MAAM,SACH,2BAA2B,KACjC,OAAO,CAAC,sBAAsB,CAAC,CA2FnC,CAAC"}