1import * as AWS from "@/AWS";
2import { IdentityProvider, UserPool } from "@/AWS/Cognito";
3import * as Test from "@/Test/Alchemy";
4import * as cip from "@distilled.cloud/aws/cognito-identity-provider";
5import { expect } from "alchemy-test";
6import * as Effect from "effect/Effect";
7import * as Redacted from "effect/Redacted";
8
9const { test } = Test.make({ providers: AWS.providers() });
10
11// OIDC provider validation is lazy for issuer reachability of well-known
12// issuers; a dummy client against accounts.google.com creates fine. The
13// client_secret is Redacted to exercise the provider's unwrapping.
14const providerDetails = {
15 client_id: "alchemy-test-client",
16 client_secret: Redacted.make("alchemy-test-secret"),
17 authorize_scopes: "openid email",
18 oidc_issuer: "https://accounts.google.com",
19 attributes_request_method: "GET",
20};
21
22test.provider(
23 "create, update mapping, delete OIDC identity provider",
24 (stack) =>
25 Effect.gen(function* () {
26 yield* stack.destroy();
27
28 const outputs = yield* stack.deploy(
29 Effect.gen(function* () {
30 const pool = yield* UserPool("IdPTestPool", {});
31 const idp = yield* IdentityProvider("Corporate", {
32 userPoolId: pool.userPoolId,
33 providerType: "OIDC",
34 providerName: "corporate-oidc",
35 providerDetails,
36 attributeMapping: { email: "email" },
37 });
38 return { pool, idp };
39 }),
40 );
41
42 expect(outputs.idp.providerName).toBe("corporate-oidc");
43 expect(outputs.idp.providerType).toBe("OIDC");
44
45 // out-of-band verification via distilled
46 const created = yield* cip.describeIdentityProvider({
47 UserPoolId: outputs.pool.userPoolId,
48 ProviderName: "corporate-oidc",
49 });
50 expect(created.IdentityProvider?.ProviderType).toBe("OIDC");
51 expect(created.IdentityProvider?.ProviderDetails?.oidc_issuer).toBe(
52 "https://accounts.google.com",
53 );
54
55 // attribute mapping mutates in place
56 yield* stack.deploy(
57 Effect.gen(function* () {
58 const pool = yield* UserPool("IdPTestPool", {});
59 const idp = yield* IdentityProvider("Corporate", {
60 userPoolId: pool.userPoolId,
61 providerType: "OIDC",
62 providerName: "corporate-oidc",
63 providerDetails,
64 attributeMapping: { email: "email", username: "sub" },
65 });
66 return { pool, idp };
67 }),
68 );
69 const afterUpdate = yield* cip.describeIdentityProvider({
70 UserPoolId: outputs.pool.userPoolId,
71 ProviderName: "corporate-oidc",
72 });
73 expect(afterUpdate.IdentityProvider?.AttributeMapping?.username).toBe(
74 "sub",
75 );
76
77 yield* stack.destroy();
78 const gone = yield* cip
79 .describeIdentityProvider({
80 UserPoolId: outputs.pool.userPoolId,
81 ProviderName: "corporate-oidc",
82 })
83 .pipe(
84 Effect.map(() => false),
85 Effect.catchTag("ResourceNotFoundException", () =>
86 Effect.succeed(true),
87 ),
88 );
89 expect(gone).toBe(true);
90 }),
91 { tags: ["provider:aws", "provider:aws:cognito", "live"], timeout: 120_000 },
92);