fix: removed unefficient AI generated call pattern using one-use object types created for 2-3 variable passing.
Some checks failed
build / build (push) Failing after 13s

This commit is contained in:
2026-03-07 16:33:13 +01:00
parent 63029d1119
commit 059590fde4
16 changed files with 149 additions and 230 deletions

View File

@@ -1,18 +1,16 @@
// SPDX-License-Identifier: MIT
import { DefaultAzureCredential, ClientSecretCredential, DeviceCodeCredential } from "@azure/identity";
import type { AuthenticationResult } from "@azure/msal-node";
import { acquireResourceToken as acquireResourceTokenPca } from "./pca-auth.ts";
type CredentialType = "d" | "default" | "cs" | "clientSecret" | "dc" | "deviceCode";
type CredentialOptions = {
tenantId?: string;
clientId?: string;
clientSecret?: string;
};
export async function getCredential(
credentialType: CredentialType,
options: CredentialOptions,
tenantId?: string,
clientId?: string,
clientSecret?: string,
): Promise<DefaultAzureCredential | ClientSecretCredential | DeviceCodeCredential> {
switch (credentialType) {
case "d":
@@ -20,26 +18,26 @@ export async function getCredential(
return new DefaultAzureCredential();
case "cs":
case "clientSecret":
if (!options.tenantId || !options.clientId || !options.clientSecret) {
if (!tenantId || !clientId || !clientSecret) {
throw new Error(
"tenantId, clientId, and clientSecret are required for ClientSecretCredential",
);
}
return new ClientSecretCredential(
options.tenantId,
options.clientId,
options.clientSecret,
tenantId,
clientId,
clientSecret,
);
case "dc":
case "deviceCode":
if (!options.tenantId || !options.clientId) {
if (!tenantId || !clientId) {
throw new Error(
"tenantId and clientId are required for DeviceCodeCredential",
);
}
return new DeviceCodeCredential({
tenantId: options.tenantId,
clientId: options.clientId,
tenantId,
clientId,
userPromptCallback: (info) => {
console.log(info.message);
},
@@ -48,3 +46,11 @@ export async function getCredential(
throw new Error(`Unsupported credential type: ${credentialType}`);
}
}
export async function acquireResourceToken(
tenantId: string,
clientId: string,
resource: string,
): Promise<AuthenticationResult | null> {
return acquireResourceTokenPca(tenantId, clientId, resource);
}

View File

@@ -7,11 +7,19 @@
*/
export { getCredential } from "./client-auth.ts";
import { acquireResourceToken as acquireResourceTokenPca } from "./pca-auth.ts";
export {
loginInteractive,
loginDeviceCode,
login,
logout,
parseResources,
acquireResourceTokenFromLogin,
} from "./pca-auth.ts";
export async function acquireResourceToken(
tenantId: string,
clientId: string,
resource: string,
) {
return acquireResourceTokenPca(tenantId, clientId, resource);
}

View File

@@ -13,7 +13,6 @@ import type {
ICachePlugin,
TokenCacheContext,
} from "@azure/msal-node";
import os from "node:os";
const RESOURCE_SCOPE_BY_NAME = {
graph: "https://graph.microsoft.com/.default",
@@ -34,60 +33,6 @@ type SessionState = {
activeAccountUpn: string | null;
};
type BrowserOptions = {
browser?: string;
browserProfile?: string;
};
type LoginInteractiveOptions = {
tenantId?: string;
clientId?: string;
scopes: string[];
showAuthUrlOnly?: boolean;
browser?: string;
browserProfile?: string;
};
type LoginDeviceCodeOptions = {
tenantId?: string;
clientId?: string;
scopes: string[];
};
type LoginOptions = {
tenantId?: string;
clientId?: string;
resourcesCsv?: string;
useDeviceCode?: boolean;
noBrowser?: boolean;
browser?: string;
browserProfile?: string;
};
type AcquireResourceTokenOptions = {
tenantId?: string;
clientId?: string;
resource?: string;
};
type LogoutOptions = {
tenantId?: string;
clientId?: string;
clearAll?: boolean;
userPrincipalName?: string;
};
function getCacheRoot(): string {
const isWindows = process.platform === "win32";
const userRoot = isWindows
? process.env.LOCALAPPDATA || os.homedir()
: os.homedir();
return isWindows
? path.join(userRoot, "sk-az-tools")
: path.join(userRoot, ".config", "sk-az-tools");
}
async function readSessionState(): Promise<SessionState> {
const parsed = (await getConfig("sk-az-tools", CONFIG_FILE_NAME)) as { activeAccountUpn?: unknown };
return {
@@ -115,10 +60,6 @@ async function clearSessionState(): Promise<void> {
}
}
function normalizeUpn(upn: unknown): string {
return typeof upn === "string" ? upn.trim().toLowerCase() : "";
}
function writeStderr(message: string): void {
process.stderr.write(`${message}\n`);
}
@@ -156,7 +97,7 @@ function getBrowserKeyword(browser?: string): string {
return keyword.toLowerCase();
}
function getBrowserOpenOptions({ browser, browserProfile }: BrowserOptions): Parameters<typeof open>[1] {
function getBrowserOpenOptions(browser?: string, browserProfile?: string): Parameters<typeof open>[1] {
const browserName = getBrowserAppName(browser);
if (!browserProfile || browserProfile.trim() === "") {
@@ -185,7 +126,7 @@ function getBrowserOpenOptions({ browser, browserProfile }: BrowserOptions): Par
};
}
function validateBrowserOptions({ browser, browserProfile }: BrowserOptions): void {
function validateBrowserOptions(browser?: string, browserProfile?: string): void {
if (browser && browser.trim() !== "") {
getBrowserAppName(browser);
}
@@ -237,8 +178,8 @@ function fileCachePlugin(cachePath: string): ICachePlugin {
};
}
async function createPca({ tenantId, clientId }: { tenantId: string; clientId: string }): Promise<PublicClientApplication> {
const cacheRoot = getCacheRoot();
async function createPca(tenantId: string, clientId: string): Promise<PublicClientApplication> {
const cacheRoot = getConfigDir("sk-az-tools");
const cachePath = path.join(cacheRoot, `${clientId}-msal.cache`);
let cachePlugin: ICachePlugin;
try {
@@ -272,15 +213,11 @@ async function createPca({ tenantId, clientId }: { tenantId: string; clientId: s
});
}
async function acquireTokenWithCache({
pca,
scopes,
account,
}: {
pca: PublicClientApplication;
scopes: string[];
account?: AccountInfo | null;
}): Promise<AuthenticationResult | null> {
async function acquireTokenWithCache(
pca: PublicClientApplication,
scopes: string[],
account?: AccountInfo | null,
): Promise<AuthenticationResult | null> {
if (account) {
try {
return await pca.acquireTokenSilent({
@@ -307,43 +244,40 @@ async function acquireTokenWithCache({
return null;
}
async function findAccountByUpn({
pca,
upn,
}: {
pca: PublicClientApplication;
upn: string | null;
}): Promise<AccountInfo | null> {
const normalized = normalizeUpn(upn);
async function findAccountByUpn(
pca: PublicClientApplication,
upn: string,
): Promise<AccountInfo | null> {
const normalized = upn.trim().toLowerCase();
if (!normalized) {
return null;
}
const accounts = await pca.getTokenCache().getAllAccounts();
return (
accounts.find((account) => normalizeUpn(account?.username) === normalized) ??
accounts.find((account) => account.username.trim().toLowerCase() === normalized) ??
null
);
}
export async function loginInteractive({
tenantId,
clientId,
scopes,
export async function loginInteractive(
tenantId: string | undefined,
clientId: string | undefined,
scopes: string[],
showAuthUrlOnly = false,
browser,
browserProfile,
}: LoginInteractiveOptions): Promise<AuthenticationResult | null> {
browser?: string,
browserProfile?: string,
): Promise<AuthenticationResult | null> {
if (!tenantId) throw new Error("tenantId is required");
if (!clientId) throw new Error("clientId is required");
if (!Array.isArray(scopes) || scopes.length === 0) {
throw new Error("scopes[] is required");
}
validateBrowserOptions({ browser, browserProfile });
validateBrowserOptions(browser, browserProfile);
const pca = await createPca({ tenantId, clientId });
const pca = await createPca(tenantId, clientId);
const cached = await acquireTokenWithCache({ pca, scopes });
const cached = await acquireTokenWithCache(pca, scopes);
if (cached) return cached;
return pca.acquireTokenInteractive({
@@ -353,7 +287,7 @@ export async function loginInteractive({
writeStderr(`Visit:\n${url}`);
return;
}
const options = getBrowserOpenOptions({ browser, browserProfile });
const options = getBrowserOpenOptions(browser, browserProfile);
await open(url, options).catch(() => {
writeStderr(`Visit:\n${url}`);
});
@@ -361,16 +295,20 @@ export async function loginInteractive({
});
}
export async function loginDeviceCode({ tenantId, clientId, scopes }: LoginDeviceCodeOptions): Promise<AuthenticationResult | null> {
export async function loginDeviceCode(
tenantId: string | undefined,
clientId: string | undefined,
scopes: string[],
): Promise<AuthenticationResult | null> {
if (!tenantId) throw new Error("tenantId is required");
if (!clientId) throw new Error("clientId is required");
if (!Array.isArray(scopes) || scopes.length === 0) {
throw new Error("scopes[] is required");
}
const pca = await createPca({ tenantId, clientId });
const pca = await createPca(tenantId, clientId);
const cached = await acquireTokenWithCache({ pca, scopes });
const cached = await acquireTokenWithCache(pca, scopes);
if (cached) return cached;
return pca.acquireTokenByDeviceCode({
@@ -381,15 +319,15 @@ export async function loginDeviceCode({ tenantId, clientId, scopes }: LoginDevic
});
}
export async function login({
tenantId,
clientId,
resourcesCsv,
export async function login(
tenantId: string | undefined,
clientId: string | undefined,
resourcesCsv?: string,
useDeviceCode = false,
noBrowser = false,
browser,
browserProfile,
}: LoginOptions): Promise<{
browser?: string,
browserProfile?: string,
): Promise<{
accountUpn: string | null;
resources: Array<{ resource: string; expiresOn: string | null }>;
flow: "device-code" | "interactive";
@@ -397,27 +335,22 @@ export async function login({
}> {
if (!tenantId) throw new Error("tenantId is required");
if (!clientId) throw new Error("clientId is required");
validateBrowserOptions({ browser, browserProfile });
validateBrowserOptions(browser, browserProfile);
const resources = parseResources(resourcesCsv);
const scopes = resources.map((resourceName) => RESOURCE_SCOPE_BY_NAME[resourceName]);
const pca = await createPca({ tenantId, clientId });
const pca = await createPca(tenantId, clientId);
const session = await readSessionState();
const preferredAccount = await findAccountByUpn({
pca,
upn: session.activeAccountUpn,
});
const preferredAccount = session.activeAccountUpn
? await findAccountByUpn(pca, session.activeAccountUpn)
: null;
const results: Array<{ resource: string; expiresOn: string | null }> = [];
let selectedAccount: AccountInfo | null = preferredAccount;
for (let index = 0; index < resources.length; index += 1) {
const resource = resources[index];
const scope = [scopes[index]];
let token = await acquireTokenWithCache({
pca,
scopes: scope,
account: selectedAccount,
});
let token = await acquireTokenWithCache(pca, scope, selectedAccount);
if (!token) {
if (useDeviceCode) {
@@ -435,7 +368,7 @@ export async function login({
writeStderr(`Visit:\n${url}`);
return;
}
const options = getBrowserOpenOptions({ browser, browserProfile });
const options = getBrowserOpenOptions(browser, browserProfile);
await open(url, options).catch(() => {
writeStderr(`Visit:\n${url}`);
});
@@ -467,11 +400,11 @@ export async function login({
};
}
export async function acquireResourceTokenFromLogin({
tenantId,
clientId,
resource,
}: AcquireResourceTokenOptions): Promise<AuthenticationResult | null> {
export async function acquireResourceToken(
tenantId: string,
clientId: string,
resource: string,
): Promise<AuthenticationResult | null> {
if (!tenantId) throw new Error("tenantId is required");
if (!clientId) throw new Error("clientId is required");
if (!resource) throw new Error("resource is required");
@@ -487,11 +420,8 @@ export async function acquireResourceTokenFromLogin({
throw new Error(LOGIN_REQUIRED_MESSAGE);
}
const pca = await createPca({ tenantId, clientId });
const account = await findAccountByUpn({
pca,
upn: session.activeAccountUpn,
});
const pca = await createPca(tenantId, clientId);
const account = await findAccountByUpn(pca, session.activeAccountUpn);
if (!account) {
throw new Error(LOGIN_REQUIRED_MESSAGE);
}
@@ -506,16 +436,16 @@ export async function acquireResourceTokenFromLogin({
}
}
export async function logout({
tenantId,
clientId,
export async function logout(
tenantId: string,
clientId: string,
clearAll = false,
userPrincipalName,
}: LogoutOptions): Promise<{ clearedAll: boolean; signedOut: string[] }> {
userPrincipalName?: string,
): Promise<{ clearedAll: boolean; signedOut: string[] }> {
if (!tenantId) throw new Error("tenantId is required");
if (!clientId) throw new Error("clientId is required");
const pca = await createPca({ tenantId, clientId });
const pca = await createPca(tenantId, clientId);
const tokenCache = pca.getTokenCache();
const accounts = await tokenCache.getAllAccounts();
const session = await readSessionState();
@@ -531,9 +461,10 @@ export async function logout({
};
}
const targetUpn = normalizeUpn(userPrincipalName) || normalizeUpn(session.activeAccountUpn);
const targetUpn = (typeof userPrincipalName === "string" ? userPrincipalName.trim().toLowerCase() : "")
|| (typeof session.activeAccountUpn === "string" ? session.activeAccountUpn.trim().toLowerCase() : "");
const accountToSignOut = accounts.find(
(account) => normalizeUpn(account.username) === targetUpn,
(account) => account.username.trim().toLowerCase() === targetUpn,
);
if (!accountToSignOut) {