Update: implement token caching in loginInteractive and enhance error handling

This commit is contained in:
2026-01-27 08:06:32 +01:00
parent 6dc72ab8d2
commit f0a16144bd
2 changed files with 62 additions and 6 deletions

View File

@@ -1,12 +1,21 @@
import { loginInteractive } from "../src/auth.js"; import { loginInteractive } from "../src/auth.js";
import { config } from "../public-config.js"; import { config } from "../public-config.js";
const scopes = ["https://management.azure.com/.default"]; const scopes = ["https://management.azure.com/.default"];
const token = await loginInteractive({ let token;
tenantId: config.tenantId,
clientId: config.clientId, try {
scopes, token = await loginInteractive({
}); tenantId: config.tenantId,
clientId: config.clientId,
scopes,
});
} catch (e) {
console.error("Login failed:", e);
console.error(e.stack);
process.exit(1);
}
console.log("Access token acquired:"); console.log("Access token acquired:");
console.log(token.accessToken); //console.log(token.accessToken);

View File

@@ -4,6 +4,30 @@ import { URL } from "node:url";
import open from "open"; import open from "open";
import crypto from "node:crypto"; import crypto from "node:crypto";
import { PublicClientApplication } from "@azure/msal-node"; import { PublicClientApplication } from "@azure/msal-node";
import fs from "node:fs";
import path from "node:path";
import os from "node:os";
function fileCachePlugin(cachePath) {
return {
beforeCacheAccess: async (ctx) => {
if (fs.existsSync(cachePath)) {
ctx.tokenCache.deserialize(
fs.readFileSync(cachePath, "utf8")
);
}
},
afterCacheAccess: async (ctx) => {
if (ctx.cacheHasChanged) {
fs.mkdirSync(path.dirname(cachePath), { recursive: true });
fs.writeFileSync(
cachePath,
ctx.tokenCache.serialize()
);
}
},
};
}
function generatePkce() { function generatePkce() {
const verifier = crypto.randomBytes(32).toString("base64url"); // 43 chars, valid const verifier = crypto.randomBytes(32).toString("base64url"); // 43 chars, valid
@@ -21,13 +45,36 @@ export async function loginInteractive({ tenantId, clientId, scopes }) {
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");
const cachePath = path.join(
os.homedir(),
".config/azure-node-playground",
`${clientId}-token-cache.json`
);
const pca = new PublicClientApplication({ const pca = new PublicClientApplication({
auth: { auth: {
clientId, clientId,
authority: `https://login.microsoftonline.com/${tenantId}`, authority: `https://login.microsoftonline.com/${tenantId}`,
}, },
cache: {
cachePlugin: fileCachePlugin(cachePath),
},
}); });
const accounts = await pca.getTokenCache().getAllAccounts();
if (accounts.length > 0) {
try {
const silentResult = await pca.acquireTokenSilent({
account: accounts[0],
scopes,
});
return silentResult;
} catch (e) {
// proceed to interactive login
}
}
const pkce = generatePkce(); const pkce = generatePkce();
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {