在 MSAL 节点中获取令牌

由于 MSAL Node 支持各种授权代码授予,因此支持每个授予的不同公共 API 和相应的请求。 本文将带你了解每种流程可用的不同公共 API 及其对应的请求类型。 强烈建议为应用程序实现授权代码流。

授权代码流程

公共 API

  • getAuthCodeUrl():该 API 是 MSAL Node 的 authorization code grant 的第一步。 请求的类型为 AuthorizationUrlRequest。 应用程序发送的 URL 可用于生成 authorization code。 此 URL 可在所选的浏览器中打开,用户可以在其中输入凭据,并使用authorization code将其重定向回 redirectUri(在应用注册期间注册)。 authorization code 现在可以通过以下步骤兑换为 token。 请注意,如果正在为公共客户端应用程序执行授权代码流,建议 使用 PKCE

  • acquireTokenByCode():此 API 是 MSAL Node 的 authorization code grant 的第二部分。 此处构造的请求应为 AuthorizationCodeRequest 类型。 应用程序传递了在上一步骤中接收的 authorization code,并将其替换为 token。 请注意,如果为公共客户端应用程序执行授权代码流程,建议使用 PKCE


    const authCodeUrlParameters = {
        scopes: ["sample_scope"],
        redirectUri: "your_redirect_uri",
    };

    // get url to sign user in and consent to scopes needed for application
    cca.getAuthCodeUrl(authCodeUrlParameters).then((response) => {
        console.log(response);
    }).catch((error) => console.log(JSON.stringify(error)));

    const tokenRequest = {
        code: "authorization_code",
        redirectUri: "your_redirect_uri",
        scopes: ["sample_scope"],
    };

    // acquire a token by exchanging the code
    cca.acquireTokenByCode(tokenRequest).then((response) => {
        console.log("\nResponse: \n:", response);
    }).catch((error) => {
        console.log(error);
    });

设备代码流

公共 API

  • acquireTokenByDeviceCode():此 API 允许应用程序使用设备代码授予获取令牌。 请求的类型为 DeviceCodeRequest。 此 API 通过 OAuth2.0 设备代码流从授权机构获取 token。 此流专为无权访问浏览器或具有输入约束的设备而设计。 授权服务器颁发一个 DeviceCode 对象,其中包含验证代码、最终用户代码以及最终用户验证 URI。 DeviceCode 对象通过回调提供,应指示最终用户使用其他设备导航到验证 URI 以输入凭据。 由于客户端无法接收传入请求,因此它会重复轮询授权服务器,直到最终用户完成凭据输入。
const msalConfig = {
    auth: {
        clientId: "your_client_id_here",
        authority: "your_authority_here",
    }
};

const pca = new msal.PublicClientApplication(msalConfig);

const deviceCodeRequest = {
    deviceCodeCallback: (response) => (console.log(response.message)),
    scopes: ["user.read"],
};

pca.acquireTokenByDeviceCode(deviceCodeRequest).then((response) => {
    console.log(JSON.stringify(response));
}).catch((error) => {
    console.log(JSON.stringify(error));
});

刷新令牌流

公共 API

  • acquireTokenByRefreshToken:此 API 通过交换为新令牌集提供的刷新令牌来获取令牌。 请求的类型为 RefreshTokenRequestrefresh token 永远不会在响应中返回给用户,但可以从用户缓存中访问。 建议您在非交互式场景中使用 acquireTokenSilent()。 使用 acquireTokenSilent()时,MSAL 将自动处理令牌的缓存和刷新。
const config = {
    auth: {
        clientId: "your_client_id_here",
        authority: "your_authority_here",
    }
};

const pca = new msal.PublicClientApplication(config);

const refreshTokenRequest = {
    refreshToken: "",
    scopes: ["user.read"],
};

pca.acquireTokenByRefreshToken(refreshTokenRequest).then((response) => {
    console.log(JSON.stringify(response));
}).catch((error) => {
    console.log(JSON.stringify(error));
});

静默流

公共 API

  • acquireTokenSilent:此 API 以静默方式获取令牌,适用于用户提供了缓存的情况,或者在此次调用之前先执行了任何其他交互式流程(例如:授权码流)并已创建缓存的情况。 请求的类型为 SilentFlowRequest。 当用户指定请求令牌所对应的帐户时,系统会自动获取 token
/**
 * Cache Plugin configuration
 */
const cachePath = "path_to_your_cache_file/msal_cache.json"; // Replace this string with the path to your valid cache file.

const readFromStorage = () => {
    return fs.readFile(cachePath, "utf-8");
};

const writeToStorage = (getMergedState) => {
    return readFromStorage().then(oldFile =>{
        const mergedState = getMergedState(oldFile);
        return fs.writeFile(cachePath, mergedState);
    })
};

const cachePlugin = {
    readFromStorage,
    writeToStorage
};

/**
 * Public Client Application Configuration
 */
const publicClientConfig = {
    auth: {
        clientId: "your_client_id_here",
        authority: "your_authority_here",
        redirectUri: "your_redirectUri_here",
    },
    cache: {
        cachePlugin
    },
};

/** Request Configuration */

const scopes = ["your_scopes"];

const authCodeUrlParameters = {
    scopes: scopes,
    redirectUri: "your_redirectUri_here",
};

const pca = new msal.PublicClientApplication(publicClientConfig);
const msalCacheManager = pca.getCacheManager();
let accounts;

pca.getAuthCodeUrl(authCodeUrlParameters)
    .then((response) => {
        console.log(response);
    }).catch((error) => console.log(JSON.stringify(error)));

const tokenRequest = {
    code: req.query.code,
    redirectUri: "http://localhost:3000/redirect",
    scopes: scopes,
};

pca.acquireTokenByCode(tokenRequest).then((response) => {
    console.log("\nResponse: \n:", response);
    return msalCacheManager.writeToPersistence();
}).catch((error) => {
    console.log(error);
});

// get Accounts
accounts = msalCacheManager.getAllAccounts();

// Build silent request
const silentRequest = {
    account: accounts[0], // You would filter accounts to get the account you want to get tokens for
    scopes: scopes,
};

// Acquire Token Silently to be used in MS Graph call
pca.acquireTokenSilent(silentRequest).then((response) => {
    console.log("\nSuccessful silent token acquisition:\nResponse: \n:", response);
    return msalCacheManager.writeToPersistence();
}).catch((error) => {
        console.log(error);
});

客户端凭据流程

公共 API

  • acquireTokenByClientCredential:此 API 使用机密客户端应用程序的凭据获取令牌,以便在调用另一个 Web 服务时进行身份验证(而不是模拟用户)。 在此方案中,客户端通常是中间层 Web 服务、守护程序服务或后端 Web 应用程序。 为了进行更高级别的保证,Microsoft 标识平台还允许调用服务将证书(而不是共享机密)用作凭据。 请求的类型为 ClientCredentialRequest

安全地使用机密

不应对机密进行硬编码。 dotenv npm 包可用于将机密存储在 .env 文件(位于项目的根目录中)中,该文件应包含在 .gitignore 中,以防止意外上传机密。

import "dotenv/config"; // process.env now has the values defined in a .env file

const config = {
    auth: {
        clientId: "your_client_id_here",
        authority: "your_authority_here",
        clientSecret: process.env.clientSecret
    }
};

// Create msal application object
const cca = new msal.ConfidentialClientApplication(config);

// With client credentials flows permissions need to be granted in the portal by a tenant administrator.
// The scope is always in the format "<resource>/.default"
const clientCredentialRequest = {
    scopes: ["https://graph.microsoft.com/.default"], // replace with your resource
};

cca.acquireTokenByClientCredential(clientCredentialRequest).then((response) => {
    console.log("Response: ", response);
}).catch((error) => {
    console.log(JSON.stringify(error));
});

代理流

  • acquireTokenOnBehalfOf:此 API 实现 On-Behalf-Of 流程,当应用程序调用某个服务/Web API,而该服务/Web API 又需要调用另一个采用其他身份验证流(设备代码、用户名/密码等)的服务/Web API 时,会使用此流程。 访问令牌最初由 Web API 通过任一 Web API 流程获取,随后 Web API 可以通过 OBO 将此令牌交换为另一个令牌。 请求的类型为 OnBehalfOfRequest

有关使用说明,请查看代理流示例