import * as NodeServices from "@effect/platform-node/NodeServices";import { describe, expect, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as PlatformError from "effect/PlatformError";import * as HttpClient from "effect/unstable/http/HttpClient"; import * as ServerSecretStore from "../auth/ServerSecretStore.ts";import * as CliTokenManager from "./CliTokenManager.ts"; const unusedSecretStoreOperation = () => Effect.die("unused secret-store operation"); function makeSecretStore( overrides: Partial<ServerSecretStore.ServerSecretStore["Service"]>,): ServerSecretStore.ServerSecretStore["Service"] { return { get: unusedSecretStoreOperation, set: unusedSecretStoreOperation, create: unusedSecretStoreOperation, getOrCreateRandom: unusedSecretStoreOperation, remove: unusedSecretStoreOperation, ...overrides, };} function makeTokenManager(secretStore: ServerSecretStore.ServerSecretStore["Service"]) { return CliTokenManager.make.pipe( Effect.provide( Layer.mergeAll( NodeServices.layer, Layer.succeed(ServerSecretStore.ServerSecretStore, secretStore), Layer.succeed( HttpClient.HttpClient, HttpClient.make(() => Effect.die("unused HTTP client")), ), ), ), );} describe("CloudCliTokenManager", () => { it("redacts OAuth endpoint credentials while retaining exact causes", () => { const tokenEndpoint = "https://user:password@auth.example.test/private/token?client_secret=secret#fragment"; const redirectUri = "https://callback-user:callback-password@localhost/private/callback?code=secret#fragment"; const cause = new Error("exchange failed"); const refreshError = CliTokenManager.CloudCliCredentialRefreshError.fromStage({ stage: "exchange-token", tokenEndpoint, cause, }); const authorizationError = CliTokenManager.CloudCliAuthorizationError.fromStage({ stage: "exchange-token", tokenEndpoint, redirectUri, cause, }); const timeoutError = CliTokenManager.CloudCliAuthorizationTimeoutError.fromRedirectUri({ redirectUri, timeoutMillis: 1000, cause, }); expect(refreshError).toMatchObject({ tokenEndpointInputLength: tokenEndpoint.length, tokenEndpointProtocol: "https:", tokenEndpointHostname: "auth.example.test", cause, }); expect(authorizationError).toMatchObject({ tokenEndpointInputLength: tokenEndpoint.length, tokenEndpointHostname: "auth.example.test", redirectUriInputLength: redirectUri.length, redirectUriHostname: "localhost", cause, }); expect(timeoutError).toMatchObject({ redirectUriInputLength: redirectUri.length, redirectUriHostname: "localhost", cause, }); expect(refreshError.cause).toBe(cause); expect(authorizationError.cause).toBe(cause); expect(timeoutError.cause).toBe(cause); for (const error of [refreshError, authorizationError, timeoutError]) { expect(error).not.toHaveProperty("tokenEndpoint"); expect(error).not.toHaveProperty("redirectUri"); const serialized = JSON.stringify(error); for (const secret of [ "user:password", "callback-user:callback-password", "/private/", "client_secret=secret", "code=secret", "#fragment", ]) { expect(error.message).not.toContain(secret); expect(serialized).not.toContain(secret); } } }); it.effect("retains secret context and cause when credential removal fails", () => { const failure = new ServerSecretStore.SecretStoreRemoveError({ secretName: "cloud-cli-oauth-token", secretPath: "/tmp/secrets/cloud-cli-oauth-token.bin", cause: PlatformError.systemError({ _tag: "PermissionDenied", module: "FileSystem", method: "remove", pathOrDescriptor: "/tmp/secrets/cloud-cli-oauth-token.bin", }), }); return Effect.gen(function* () { const tokens = yield* makeTokenManager( makeSecretStore({ remove: () => Effect.fail(failure) }), ); const error = yield* Effect.flip(tokens.clear); expect(error).toMatchObject({ _tag: "CloudCliCredentialRemovalError", secretName: "cloud-cli-oauth-token", cause: failure, }); expect(error.message).toBe( "Could not remove the stored T3 Connect CLI credential cloud-cli-oauth-token.", ); }); }); it.effect("classifies credential read failures without replacing the cause", () => { const failure = new ServerSecretStore.SecretStoreReadError({ secretName: "cloud-cli-oauth-token", secretPath: "/tmp/secrets/cloud-cli-oauth-token.bin", cause: PlatformError.systemError({ _tag: "PermissionDenied", module: "FileSystem", method: "readFile", pathOrDescriptor: "/tmp/secrets/cloud-cli-oauth-token.bin", }), }); return Effect.gen(function* () { const tokens = yield* makeTokenManager(makeSecretStore({ get: () => Effect.fail(failure) })); const error = yield* Effect.flip(tokens.hasCredential); expect(error).toMatchObject({ _tag: "CloudCliCredentialReadError", stage: "read-credential", secretName: "cloud-cli-oauth-token", cause: failure, }); expect(error.message).toBe( "Could not inspect the stored T3 Connect CLI credential cloud-cli-oauth-token during read-credential.", ); }); }); it.effect("classifies malformed persisted credentials as refresh decode failures", () => Effect.gen(function* () { const tokens = yield* makeTokenManager( makeSecretStore({ get: () => Effect.succeed(Option.some(new TextEncoder().encode("not valid credential JSON"))), }), ); const error = yield* Effect.flip(tokens.getExisting); expect(error).toMatchObject({ _tag: "CloudCliCredentialRefreshError", stage: "decode-credential", secretName: "cloud-cli-oauth-token", cause: { _tag: "SchemaError" }, }); expect(error.message).toBe( "Could not refresh the T3 Connect CLI credential cloud-cli-oauth-token during decode-credential.", ); }), );});
import * as NodeServices from "@effect/platform-node/NodeServices";
import { describe, expect, it } from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Option from "effect/Option";
import * as PlatformError from "effect/PlatformError";
import * as HttpClient from "effect/unstable/http/HttpClient";
import * as ServerSecretStore from "../auth/ServerSecretStore.ts";
import * as CliTokenManager from "./CliTokenManager.ts";
const unusedSecretStoreOperation = () => Effect.die("unused secret-store operation");
function makeSecretStore(
overrides: Partial<ServerSecretStore.ServerSecretStore["Service"]>,
): ServerSecretStore.ServerSecretStore["Service"] {
return {
get: unusedSecretStoreOperation,
set: unusedSecretStoreOperation,
create: unusedSecretStoreOperation,
getOrCreateRandom: unusedSecretStoreOperation,
remove: unusedSecretStoreOperation,
...overrides,
};
}
function makeTokenManager(secretStore: ServerSecretStore.ServerSecretStore["Service"]) {
return CliTokenManager.make.pipe(
Effect.provide(
Layer.mergeAll(
NodeServices.layer,
Layer.succeed(ServerSecretStore.ServerSecretStore, secretStore),
Layer.succeed(
HttpClient.HttpClient,
HttpClient.make(() => Effect.die("unused HTTP client")),
),
),
),
);
}
describe("CloudCliTokenManager", () => {
it("redacts OAuth endpoint credentials while retaining exact causes", () => {
const tokenEndpoint =
"https://user:password@auth.example.test/private/token?client_secret=secret#fragment";
const redirectUri =
"https://callback-user:callback-password@localhost/private/callback?code=secret#fragment";
const cause = new Error("exchange failed");
const refreshError = CliTokenManager.CloudCliCredentialRefreshError.fromStage({
stage: "exchange-token",
tokenEndpoint,
cause,
});
const authorizationError = CliTokenManager.CloudCliAuthorizationError.fromStage({
stage: "exchange-token",
tokenEndpoint,
redirectUri,
cause,
});
const timeoutError = CliTokenManager.CloudCliAuthorizationTimeoutError.fromRedirectUri({
redirectUri,
timeoutMillis: 1000,
cause,
});
expect(refreshError).toMatchObject({
tokenEndpointInputLength: tokenEndpoint.length,
tokenEndpointProtocol: "https:",
tokenEndpointHostname: "auth.example.test",
cause,
});
expect(authorizationError).toMatchObject({
tokenEndpointInputLength: tokenEndpoint.length,
tokenEndpointHostname: "auth.example.test",
redirectUriInputLength: redirectUri.length,
redirectUriHostname: "localhost",
cause,
});
expect(timeoutError).toMatchObject({
redirectUriInputLength: redirectUri.length,
redirectUriHostname: "localhost",
cause,
});
expect(refreshError.cause).toBe(cause);
expect(authorizationError.cause).toBe(cause);
expect(timeoutError.cause).toBe(cause);
for (const error of [refreshError, authorizationError, timeoutError]) {
expect(error).not.toHaveProperty("tokenEndpoint");
expect(error).not.toHaveProperty("redirectUri");
const serialized = JSON.stringify(error);
for (const secret of [
"user:password",
"callback-user:callback-password",
"/private/",
"client_secret=secret",
"code=secret",
"#fragment",
]) {
expect(error.message).not.toContain(secret);
expect(serialized).not.toContain(secret);
}
}
});
it.effect("retains secret context and cause when credential removal fails", () => {
const failure = new ServerSecretStore.SecretStoreRemoveError({
secretName: "cloud-cli-oauth-token",
secretPath: "/tmp/secrets/cloud-cli-oauth-token.bin",
cause: PlatformError.systemError({
_tag: "PermissionDenied",
module: "FileSystem",
method: "remove",
pathOrDescriptor: "/tmp/secrets/cloud-cli-oauth-token.bin",
}),
});
return Effect.gen(function* () {
const tokens = yield* makeTokenManager(
makeSecretStore({ remove: () => Effect.fail(failure) }),
);
const error = yield* Effect.flip(tokens.clear);
expect(error).toMatchObject({
_tag: "CloudCliCredentialRemovalError",
secretName: "cloud-cli-oauth-token",
cause: failure,
});
expect(error.message).toBe(
"Could not remove the stored T3 Connect CLI credential cloud-cli-oauth-token.",
);
});
});
it.effect("classifies credential read failures without replacing the cause", () => {
const failure = new ServerSecretStore.SecretStoreReadError({
secretName: "cloud-cli-oauth-token",
secretPath: "/tmp/secrets/cloud-cli-oauth-token.bin",
cause: PlatformError.systemError({
_tag: "PermissionDenied",
module: "FileSystem",
method: "readFile",
pathOrDescriptor: "/tmp/secrets/cloud-cli-oauth-token.bin",
}),
});
return Effect.gen(function* () {
const tokens = yield* makeTokenManager(makeSecretStore({ get: () => Effect.fail(failure) }));
const error = yield* Effect.flip(tokens.hasCredential);
expect(error).toMatchObject({
_tag: "CloudCliCredentialReadError",
stage: "read-credential",
secretName: "cloud-cli-oauth-token",
cause: failure,
});
expect(error.message).toBe(
"Could not inspect the stored T3 Connect CLI credential cloud-cli-oauth-token during read-credential.",
);
});
});
it.effect("classifies malformed persisted credentials as refresh decode failures", () =>
Effect.gen(function* () {
const tokens = yield* makeTokenManager(
makeSecretStore({
get: () =>
Effect.succeed(Option.some(new TextEncoder().encode("not valid credential JSON"))),
}),
);
const error = yield* Effect.flip(tokens.getExisting);
expect(error).toMatchObject({
_tag: "CloudCliCredentialRefreshError",
stage: "decode-credential",
secretName: "cloud-cli-oauth-token",
cause: { _tag: "SchemaError" },
});
expect(error.message).toBe(
"Could not refresh the T3 Connect CLI credential cloud-cli-oauth-token during decode-credential.",
);
}),
);
});