Skip to content

Commit 4dc2bfa

Browse files
authored
Merge pull request #17 from lucianfialho/fix/16-toclirc-auth-not-applied
fix: apply .toclirc and saved profile auth to API requests
2 parents b88c110 + 3fdcf7b commit 4dc2bfa

5 files changed

Lines changed: 105 additions & 13 deletions

File tree

src/auth/auth.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,40 @@ describe("resolveAuth", () => {
8080
expect(auth.type).toBe("bearer");
8181
expect(auth.value).toBe("env-token");
8282
});
83+
84+
it("uses .toclirc auth token", async () => {
85+
const auth = await resolveAuth({ rcAuthType: "bearer", rcAuthToken: "rc-token-123" }, minimalSpec, {});
86+
expect(auth.type).toBe("bearer");
87+
expect(auth.value).toBe("rc-token-123");
88+
});
89+
90+
it("uses .toclirc auth envVar", async () => {
91+
const auth = await resolveAuth({ rcAuthType: "bearer", rcAuthEnvVar: "MY_API_TOKEN" }, minimalSpec, { MY_API_TOKEN: "env-resolved" });
92+
expect(auth.type).toBe("bearer");
93+
expect(auth.value).toBe("env-resolved");
94+
});
95+
96+
it("defaults rcAuthType to bearer when not specified", async () => {
97+
const auth = await resolveAuth({ rcAuthToken: "tok" }, minimalSpec, {});
98+
expect(auth.type).toBe("bearer");
99+
expect(auth.value).toBe("tok");
100+
});
101+
102+
it("inline --token takes priority over .toclirc auth", async () => {
103+
const auth = await resolveAuth({ token: "inline-tok", rcAuthToken: "rc-tok" }, minimalSpec, {});
104+
expect(auth.value).toBe("inline-tok");
105+
});
106+
107+
it(".toclirc auth takes priority over saved profile", async () => {
108+
await saveProfile("default", { type: "bearer", value: "profile-tok" });
109+
const auth = await resolveAuth({ rcAuthToken: "rc-tok" }, minimalSpec);
110+
expect(auth.value).toBe("rc-tok");
111+
});
112+
113+
it("resolves env vars in .toclirc auth token", async () => {
114+
const auth = await resolveAuth({ rcAuthToken: "$SECRET_TOK" }, minimalSpec, { SECRET_TOK: "resolved-secret" });
115+
expect(auth.value).toBe("resolved-secret");
116+
});
83117
});
84118

85119
describe("auth config (profile persistence)", () => {

src/auth/flags.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ export interface AuthFlags {
77
apiKey?: string;
88
authHeader?: string;
99
profile?: string;
10+
rcAuthType?: string;
11+
rcAuthToken?: string;
12+
rcAuthEnvVar?: string;
1013
}
1114

1215
export async function resolveAuth(
@@ -26,7 +29,20 @@ export async function resolveAuth(
2629
return { type: "bearer", value: resolveEnvVar(flags.authHeader, env) };
2730
}
2831

29-
// Priority 2: Environment variables from spec
32+
// Priority 2: .toclirc auth config
33+
if (flags.rcAuthToken) {
34+
const type = (flags.rcAuthType as AuthConfig["type"]) ?? "bearer";
35+
return { type, value: resolveEnvVar(flags.rcAuthToken, env) };
36+
}
37+
if (flags.rcAuthEnvVar) {
38+
const envVal = env[flags.rcAuthEnvVar];
39+
if (envVal) {
40+
const type = (flags.rcAuthType as AuthConfig["type"]) ?? "bearer";
41+
return { type, value: envVal };
42+
}
43+
}
44+
45+
// Priority 3: Environment variables from spec
3046
const specAuth = detectAuthFromSpec(spec);
3147
if (specAuth) {
3248
// Check common env var names
@@ -38,7 +54,7 @@ export async function resolveAuth(
3854
}
3955
}
4056

41-
// Priority 3: Saved profile
57+
// Priority 4: Saved profile
4258
const profileName = flags.profile ?? "default";
4359
const profile = await getProfile(profileName);
4460
if (profile) {

src/config/config.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,30 @@ describe("resolveConfig", () => {
7777
expect(resolved.authEnvVar).toBe("STAGING_TOKEN");
7878
});
7979

80+
it("returns auth token from config", () => {
81+
const config = {
82+
spec: "./api.yaml",
83+
auth: { type: "bearer", token: "direct-token" },
84+
};
85+
const resolved = resolveConfig(config);
86+
expect(resolved.authType).toBe("bearer");
87+
expect(resolved.authToken).toBe("direct-token");
88+
});
89+
90+
it("overrides auth token with environment config", () => {
91+
const config = {
92+
spec: "./api.yaml",
93+
auth: { type: "bearer", token: "prod-token" },
94+
environments: {
95+
staging: {
96+
auth: { token: "staging-token" },
97+
},
98+
},
99+
};
100+
const resolved = resolveConfig(config, "staging");
101+
expect(resolved.authToken).toBe("staging-token");
102+
});
103+
80104
it("keeps base config for unknown env", () => {
81105
const config = {
82106
spec: "./api.yaml",

src/config/rc.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ export interface RcConfig {
77
baseUrl?: string;
88
auth?: {
99
type?: string;
10+
token?: string;
1011
envVar?: string;
1112
};
1213
environments?: Record<string, {
1314
baseUrl?: string;
1415
auth?: {
1516
type?: string;
17+
token?: string;
1618
envVar?: string;
1719
};
1820
}>;
@@ -34,22 +36,27 @@ export async function loadConfig(startDir?: string): Promise<RcConfig | null> {
3436
return config;
3537
}
3638

37-
export function resolveConfig(config: RcConfig, envName?: string): { spec: string; baseUrl?: string; authEnvVar?: string } {
39+
export function resolveConfig(config: RcConfig, envName?: string): { spec: string; baseUrl?: string; authType?: string; authToken?: string; authEnvVar?: string } {
3840
let spec = config.spec;
3941
let baseUrl = config.baseUrl;
42+
let authType = config.auth?.type;
43+
let authToken = config.auth?.token;
4044
let authEnvVar = config.auth?.envVar;
4145

4246
if (envName && config.environments?.[envName]) {
4347
const env = config.environments[envName];
4448
if (env.baseUrl) baseUrl = env.baseUrl;
49+
if (env.auth?.type) authType = env.auth.type;
50+
if (env.auth?.token) authToken = env.auth.token;
4551
if (env.auth?.envVar) authEnvVar = env.auth.envVar;
4652
}
4753

4854
// Resolve env vars in values
4955
spec = resolveEnvVars(spec);
5056
if (baseUrl) baseUrl = resolveEnvVars(baseUrl);
57+
if (authToken) authToken = resolveEnvVars(authToken);
5158

52-
return { spec, baseUrl, authEnvVar };
59+
return { spec, baseUrl, authType, authToken, authEnvVar };
5360
}
5461

5562
async function findRcFile(dir: string): Promise<string | null> {

src/index.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { extractOperations } from "./parser/extractor.js";
66
import { executeRequest } from "./executor/http.js";
77
import { formatOutput } from "./output/formatters.js";
88
import { registerAuthCommands } from "./auth/commands.js";
9+
import { resolveAuth as resolveAuthFromFlags } from "./auth/flags.js";
910
import { registerInitCommand } from "./config/init.js";
1011
import { registerUseCommand } from "./templates/commands.js";
1112
import { loadConfig, resolveConfig } from "./config/rc.js";
@@ -59,13 +60,19 @@ async function main() {
5960
// Resolve spec: --spec flag > .toclirc config
6061
let specPath = getFlagValue(rawArgs, "--spec");
6162
let configBaseUrl: string | undefined;
63+
let rcAuthType: string | undefined;
64+
let rcAuthToken: string | undefined;
65+
let rcAuthEnvVar: string | undefined;
6266

6367
if (!specPath) {
6468
const rc = await loadConfig();
6569
if (rc) {
6670
const resolved = resolveConfig(rc, envName);
6771
specPath = resolved.spec;
6872
configBaseUrl = resolved.baseUrl;
73+
rcAuthType = resolved.authType;
74+
rcAuthToken = resolved.authToken;
75+
rcAuthEnvVar = resolved.authEnvVar;
6976
}
7077
}
7178

@@ -93,10 +100,22 @@ async function main() {
93100
return;
94101
}
95102

103+
const auth = await resolveAuthFromFlags(
104+
{
105+
token: getFlagValue(rawArgs, "--token"),
106+
apiKey: getFlagValue(rawArgs, "--api-key"),
107+
profile: getFlagValue(rawArgs, "--profile"),
108+
rcAuthType,
109+
rcAuthToken,
110+
rcAuthEnvVar,
111+
},
112+
spec
113+
);
114+
96115
const config: RuntimeConfig = {
97116
specPath,
98117
baseUrl: getFlagValue(rawArgs, "--base-url") ?? configBaseUrl ?? resolveBaseUrl(spec, specPath),
99-
auth: resolveAuth(rawArgs),
118+
auth,
100119
output: getFlagValue(rawArgs, "--output") ?? (process.stdout.isTTY ? "pretty" : "json"),
101120
maxItems: getFlagValue(rawArgs, "--max-items") ? parseInt(getFlagValue(rawArgs, "--max-items")!) : undefined,
102121
verbose: rawArgs.includes("--verbose"),
@@ -360,14 +379,6 @@ function simplifyName(operationId: string, tag: string): string {
360379
return operationId.toLowerCase();
361380
}
362381
363-
function resolveAuth(args: string[]): RuntimeConfig["auth"] {
364-
const token = getFlagValue(args, "--token");
365-
if (token) return { type: "bearer", value: token };
366-
const apiKey = getFlagValue(args, "--api-key");
367-
if (apiKey) return { type: "apiKey", value: apiKey, headerName: "X-API-Key" };
368-
return { type: "none", value: "" };
369-
}
370-
371382
function getFlagValue(args: string[], flag: string): string | undefined {
372383
const idx = args.indexOf(flag);
373384
return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : undefined;

0 commit comments

Comments
 (0)