Since MSAL Node supports various authorization code grants, there is support for different public APIs per grant and the corresponding request.
acquireTokenInteractive(): This API handles both legs of the authorization code flow. It is available for PublicClientApplication only. The request type is documented here: InteractiveRequest. The only required parameter is a openBrowser callback which should accept a url parameter and open the browser of choice to complete the sign-in. A sample demonstrating its usage can be found here
import { PublicClientApplication } from "@azure/msal-node";
import open from "open";
// Open browser to sign user in and consent to scopes needed for application
const openBrowser = async (url) => {
// You can open a browser window with any library or method you wish to use - the 'open' npm package is used here for demonstration purposes.
open(url);
};
const loginRequest = {
scopes: ["User.Read"],
openBrowser,
successTemplate: "Successfully signed in! You can close this window now." // Will be shown in the browser window after authentication is complete
};
// Create msal application object
const pca = new PublicClientApplication({
auth: {
clientId: "<your clientId here>"
}
});
pca.acquireTokenInteractive(loginRequest).then((response) => {
// Do something with the token e.g. call an API
callAPI(response.accessToken)
})
.catch((error) => {
console.log(error);
});getAuthCodeUrl(): This API performs the first leg of the authorization code flow. The request is of the type AuthorizationUrlRequest.
getAuthCodeUrl returns a url that can be used to generate an authorization code. This URL can be opened in a browser of choice, where the user can input their credentials, and will be redirected back to the redirectUri (registered during the app registration) with an authorization code. The authorization code can now be redeemed for a token using acquireTokenByCode, documented below. Note that if authorization code flow is being done for a public client application, we recommend using acquireTokenInteractive documented above, otherwise the use of PKCE is recommended.
const authCodeUrlParameters = {
scopes: ["sample_scope"],
redirectUri: "your_redirect_uri",
};
const cca = new ConfidentialClientApplication({
auth: {
clientId: "<your clientId here>"
}
});
// get url to sign user in and consent to scopes needed for application
cca.getAuthCodeUrl(authCodeUrlParameters)
.then((url) => {
// redirect to url
})
.catch((error) => console.log(JSON.stringify(error)));acquireTokenByCode(): This API is the second leg of the authorization code flow. The request is of the type AuthorizationCodeRequest. The application should have received an authorization code as a part of the above step and can now exchange it for a token. Note that if authorization code flow is being done for a public client application, we recommend using acquireTokenInteractive documented above, otherwise the use of PKCE is recommended.
const tokenRequest = {
code: "authorization_code",
redirectUri: "your_redirect_uri",
scopes: ["sample_scope"],
};
const cca = new ConfidentialClientApplication({
auth: {
clientId: "<your clientId here>"
}
});
// acquire a token by exchanging the code
cca.acquireTokenByCode(tokenRequest)
.then((response) => {
// Do something with the token e.g. call an API
callAPI(response.accessToken)
})
.catch((error) => {
console.log(error);
});- acquireTokenByDeviceCode(): This API lets the application acquire a token with Device Code grant. The request is of the type DeviceCodeRequest. This API acquires a
tokenfrom the authority using OAuth2.0 device code flow. This flow is designed for devices that do not have access to a browser or have input constraints. The authorization server issues a DeviceCode object with a verification code, an end-user code, and the end-user verification URI. The DeviceCode object is provided through a callback, and the end-user should be instructed to use another device to navigate to the verification URI to input credentials. Since the client cannot receive incoming requests, it polls the authorization server repeatedly until the end-user completes input of credentials.
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));
});- acquireTokenByRefreshToken: This API acquires a token by exchanging the refresh token provided for a new set of tokens. The request is of the type RefreshTokenRequest. The
refresh tokenis never returned to the user in a response, but can be accessed from the user cache. It is recommended that you useacquireTokenSilent()for non-interactive scenarios. When usingacquireTokenSilent(), MSAL will handle the caching and refreshing of tokens automatically.
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));
});- acquireTokenSilent: This API acquires a token silently, in case cache is provided by the user, or when cache is created by preceding this call with any other interactive flow (eg: authorization code flow). The request is of the type SilentFlowRequest. The
tokenis acquired silently when a user specifies the account the token is requested for.
/**
* 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);
});- acquireTokenByClientCredential: This API acquires a token using the confidential client application's credentials to authenticate (instead of impersonating a user) when calling another web service. In this scenario, the client is typically a middle-tier web service, a daemon service, or a back-end web application. For a higher level of assurance, the Microsoft identity platform also allows the calling service to use a certificate (instead of a shared secret) as a credential. The request is of the type ClientCredentialRequest.
Secrets should never be hardcoded. The dotenv npm package can be used to store secrets in a .env file (located in project's root directory) that should be included in .gitignore to prevent accidental uploads of the secrets.
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: This API implements the On Behalf Of Flow, which is used when an application invokes a service/web API, which in turn needs to call another service/web API that uses any other authentication flow (device code, username/password, etc). The access token is acquired by the web API initially (by any of the web API flows), and the web API can then exchange this token for another token via OBO. The request is of the type OnBehalfOfRequest
Please look at the On Behalf Of flow sample for usage instructions:
Proceed to understand the public APIs provided by msal-node for acquiring tokens here