Refactor getCredential and loginInteractive functions for improved error handling and browser specification
This commit is contained in:
63
src/azure.js
63
src/azure.js
@@ -1,13 +1,20 @@
|
|||||||
// azure.js
|
// azure.js
|
||||||
import http from "node:http";
|
import http from "node:http";
|
||||||
import { URL } from "node:url";
|
import { URL } from "node:url";
|
||||||
import open from "open";
|
import open, { apps } from "open";
|
||||||
import crypto from "node:crypto";
|
import crypto from "node:crypto";
|
||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
import { PublicClientApplication, ConfidentialClientApplication } from "@azure/msal-node";
|
import {
|
||||||
import { DefaultAzureCredential, ClientSecretCredential, DeviceCodeCredential } from "@azure/identity";
|
PublicClientApplication,
|
||||||
|
ConfidentialClientApplication,
|
||||||
|
} from "@azure/msal-node";
|
||||||
|
import {
|
||||||
|
DefaultAzureCredential,
|
||||||
|
ClientSecretCredential,
|
||||||
|
DeviceCodeCredential,
|
||||||
|
} from "@azure/identity";
|
||||||
|
|
||||||
export async function getCredential(credentialType, options) {
|
export async function getCredential(credentialType, options) {
|
||||||
switch (credentialType) {
|
switch (credentialType) {
|
||||||
@@ -17,17 +24,21 @@ export async function getCredential(credentialType, options) {
|
|||||||
case "cs":
|
case "cs":
|
||||||
case "clientSecret":
|
case "clientSecret":
|
||||||
if (!options.tenantId || !options.clientId || !options.clientSecret) {
|
if (!options.tenantId || !options.clientId || !options.clientSecret) {
|
||||||
throw new Error("tenantId, clientId, and clientSecret are required for ClientSecretCredential");
|
throw new Error(
|
||||||
|
"tenantId, clientId, and clientSecret are required for ClientSecretCredential",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return new ClientSecretCredential(
|
return new ClientSecretCredential(
|
||||||
options.tenantId,
|
options.tenantId,
|
||||||
options.clientId,
|
options.clientId,
|
||||||
options.clientSecret
|
options.clientSecret,
|
||||||
);
|
);
|
||||||
case "dc":
|
case "dc":
|
||||||
case "deviceCode":
|
case "deviceCode":
|
||||||
if (!options.tenantId || !options.clientId) {
|
if (!options.tenantId || !options.clientId) {
|
||||||
throw new Error("tenantId and clientId are required for DeviceCodeCredential");
|
throw new Error(
|
||||||
|
"tenantId and clientId are required for DeviceCodeCredential",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return new DeviceCodeCredential({
|
return new DeviceCodeCredential({
|
||||||
tenantId: options.tenantId,
|
tenantId: options.tenantId,
|
||||||
@@ -45,18 +56,13 @@ function fileCachePlugin(cachePath) {
|
|||||||
return {
|
return {
|
||||||
beforeCacheAccess: async (ctx) => {
|
beforeCacheAccess: async (ctx) => {
|
||||||
if (fs.existsSync(cachePath)) {
|
if (fs.existsSync(cachePath)) {
|
||||||
ctx.tokenCache.deserialize(
|
ctx.tokenCache.deserialize(fs.readFileSync(cachePath, "utf8"));
|
||||||
fs.readFileSync(cachePath, "utf8")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
afterCacheAccess: async (ctx) => {
|
afterCacheAccess: async (ctx) => {
|
||||||
if (ctx.cacheHasChanged) {
|
if (ctx.cacheHasChanged) {
|
||||||
fs.mkdirSync(path.dirname(cachePath), { recursive: true });
|
fs.mkdirSync(path.dirname(cachePath), { recursive: true });
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(cachePath, ctx.tokenCache.serialize());
|
||||||
cachePath,
|
|
||||||
ctx.tokenCache.serialize()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -72,16 +78,22 @@ function generatePkce() {
|
|||||||
return { verifier, challenge, challengeMethod: "S256" };
|
return { verifier, challenge, challengeMethod: "S256" };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loginInteractive({ tenantId, clientId, scopes }) {
|
export async function loginInteractive({ appName, tenantId, clientId, scopes }) {
|
||||||
if (!tenantId) throw new Error("tenantId is required");
|
if (!tenantId) throw new Error("tenantId is required");
|
||||||
if (!clientId) throw new Error("clientId is required");
|
if (!clientId) throw new Error("clientId is required");
|
||||||
if (!Array.isArray(scopes) || scopes.length === 0)
|
if (!Array.isArray(scopes) || scopes.length === 0)
|
||||||
throw new Error("scopes[] is required");
|
throw new Error("scopes[] is required");
|
||||||
|
|
||||||
|
// Make app name lowercase with all non-alphanumeric characters removed
|
||||||
|
// spaces replaced with dashes and all letters converted to lowercase
|
||||||
|
const sanitizedAppName = (appName || "Azure Node Login")
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[^a-z0-9]+/g, "-");
|
||||||
|
|
||||||
const cachePath = path.join(
|
const cachePath = path.join(
|
||||||
os.homedir(),
|
os.homedir(),
|
||||||
".config/azure-node-playground",
|
`.config/${sanitizedAppName}`,
|
||||||
`${clientId}-token-cache.json`
|
`${clientId}-token-cache.json`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const pca = new PublicClientApplication({
|
const pca = new PublicClientApplication({
|
||||||
@@ -142,7 +154,9 @@ export async function loginInteractive({ tenantId, clientId, scopes }) {
|
|||||||
|
|
||||||
resolve(token);
|
resolve(token);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
try { server.close(); } catch {}
|
try {
|
||||||
|
server.close();
|
||||||
|
} catch {}
|
||||||
reject(e);
|
reject(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -160,10 +174,19 @@ export async function loginInteractive({ tenantId, clientId, scopes }) {
|
|||||||
codeChallengeMethod: pkce.challengeMethod,
|
codeChallengeMethod: pkce.challengeMethod,
|
||||||
});
|
});
|
||||||
|
|
||||||
try { await open(authUrl, { wait: false }); } catch {}
|
try {
|
||||||
console.log("If the browser didn't open, visit:\n" + authUrl);
|
await open(authUrl, {
|
||||||
|
wait: false,
|
||||||
|
app: {
|
||||||
|
name: apps.edge, // Enforce using Microsoft Edge browser
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch {}
|
||||||
|
console.log("Visit:\n" + authUrl);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
try { server.close(); } catch {}
|
try {
|
||||||
|
server.close();
|
||||||
|
} catch {}
|
||||||
reject(e);
|
reject(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user