Build TypeScript apps with Microsoft Graph and app-only authentication
This tutorial teaches you how to build a TypeScript console app that uses the Microsoft Graph API to access data using app-only authentication. App-only authentication is a good choice for background services or applications that need to access data for all users in an organization.
Note
To learn how to use Microsoft Graph to access data on behalf of a user, see this user (delegated) authentication tutorial.
In this tutorial, you will:
Tip
As an alternative to following this tutorial, you can download or clone the GitHub repository and follow the instructions in the README to register an application and configure the project.
Prerequisites
Before you start this tutorial, you should have Node.js installed on your development machine.
You should also have a Microsoft work or school account with the Global administrator role. If you don't have a Microsoft 365 tenant, you might qualify for one through the Microsoft 365 Developer Program; for details, see the FAQ. Alternatively, you can sign up for a 1-month free trial or purchase a Microsoft 365 plan.
Note
This tutorial was written with Node.js version 16.14.2. The steps in this guide may work with other versions, but that has not been tested.
Register the app in the portal
In this exercise you will register a new application in Azure Active Directory to enable app-only authentication. You can register an application using the Microsoft Entra admin center, or by using the Microsoft Graph PowerShell SDK.
Register application for app-only authentication
In this section you will register an application that supports app-only authentication using client credentials flow.
Open a browser and navigate to the Microsoft Entra admin center and login using a Global administrator account.
Select Microsoft Entra ID in the left-hand navigation, expand Identity, expand Applications, then select App registrations.
Select New registration. Enter a name for your application, for example,
Graph App-Only Auth Tutorial
.Set Supported account types to Accounts in this organizational directory only.
Leave Redirect URI empty.
Select Register. On the application's Overview page, copy the value of the Application (client) ID and Directory (tenant) ID and save them, you will need these values in the next step.
Select API permissions under Manage.
Remove the default User.Read permission under Configured permissions by selecting the ellipses (...) in its row and selecting Remove permission.
Select Add a permission, then Microsoft Graph.
Select Application permissions.
Select User.Read.All, then select Add permissions.
Select Grant admin consent for..., then select Yes to provide admin consent for the selected permission.
Select Certificates and secrets under Manage, then select New client secret.
Enter a description, choose a duration, and select Add.
Copy the secret from the Value column, you will need it in the next steps.
Important
This client secret is never shown again, so make sure you copy it now.
Note
Notice that, unlike the steps when registering for user authentication, in this section you did configure Microsoft Graph permissions on the app registration. This is because app-only auth uses the client credentials flow, which requires that permissions be configured on the app registration. See The .default scope for details.
Create a TypeScript console app
Begin by creating a new Node.js project and configuring TypeScript.
Open your command-line interface (CLI) in a directory where you want to create the project. Run the following command.
npm init
Answer the prompts by either supplying your own values or accepting the defaults.
Run the following command to install TypeScript.
npm install -D typescript ts-node
Run the following command to initialize TypeScript.
npx tsc --init
Install dependencies
Before moving on, add some additional dependencies that you will use later.
- Azure Identity client library for JavaScript to authenticate the user and acquire access tokens.
- Microsoft Graph JavaScript client library to make calls to the Microsoft Graph.
- isomorphic-fetch to add
fetch
API to Node.js. This is a dependency for the Microsoft Graph JavaScript client library. - readline-sync for prompting the user for input.
Run the following commands in your CLI to install the dependencies.
npm install @azure/identity @microsoft/microsoft-graph-client isomorphic-fetch readline-sync
npm install -D @microsoft/microsoft-graph-types @types/node @types/readline-sync @types/isomorphic-fetch
Load application settings
In this section you'll add the details of your app registration to the project.
Create a file in the root of your project named appSettings.ts and add the following code.
const settings: AppSettings = { clientId: 'YOUR_CLIENT_ID_HERE', clientSecret: 'YOUR_CLIENT_SECRET_HERE', tenantId: 'YOUR_TENANT_ID_HERE', }; export interface AppSettings { clientId: string; clientSecret: string; tenantId: string; } export default settings;
Update the values in
settings
according to the following table.Setting Value clientId
The client ID of your app registration tenantId
The tenant ID of your organization. clientSecret
The client secret generated in the previous step
Design the app
In this section you will create a simple console-based menu.
Create a file in the root of your project named graphHelper.ts and add the following placeholder code. You'll add more code this file in later steps.
export {};
Create a file in the root of your project named index.ts and add the following code.
import * as readline from 'readline-sync'; import { User } from '@microsoft/microsoft-graph-types'; import settings, { AppSettings } from './appSettings'; import * as graphHelper from './graphHelper'; async function main() { console.log('TypeScript Graph Tutorial'); let choice = 0; // Initialize Graph initializeGraph(settings); const choices = ['Display access token', 'List users', 'Make a Graph call']; while (choice != -1) { choice = readline.keyInSelect(choices, 'Select an option', { cancel: 'Exit', }); switch (choice) { case -1: // Exit console.log('Goodbye...'); break; case 0: // Display access token await displayAccessTokenAsync(); break; case 1: // List users await listUsersAsync(); break; case 2: // Run any Graph code await makeGraphCallAsync(); break; default: console.log('Invalid choice! Please try again.'); } } } main();
Add the following placeholder methods at the end of the file. You'll implement them in later steps.
function initializeGraph(settings: AppSettings) { // TODO } async function displayAccessTokenAsync() { // TODO } async function listUsersAsync() { // TODO } async function makeGraphCallAsync() { // TODO }
This implements a basic menu and reads the user's choice from the command line.
Add app-only authentication
In this section you will add app-only authentication to the application. This is required to obtain the necessary OAuth access token to call the Microsoft Graph. In this step you will integrate the Azure Identity client library for JavaScript into the application and configure authentication for the Microsoft Graph JavaScript client library.
The Azure Identity library provides a number of TokenCredential
classes that implement OAuth2 token flows. The Microsoft Graph client library uses those classes to authenticate calls to Microsoft Graph.
Configure Graph client for app-only authentication
In this section you will use the ClientSecretCredential
class to request an access token by using the client credentials flow.
Open graphHelper.ts and add the following code.
import 'isomorphic-fetch'; import { ClientSecretCredential } from '@azure/identity'; import { Client, PageCollection } from '@microsoft/microsoft-graph-client'; // prettier-ignore import { TokenCredentialAuthenticationProvider } from '@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials'; import { AppSettings } from './appSettings'; let _settings: AppSettings | undefined = undefined; let _clientSecretCredential: ClientSecretCredential | undefined = undefined; let _appClient: Client | undefined = undefined; export function initializeGraphForAppOnlyAuth(settings: AppSettings) { // Ensure settings isn't null if (!settings) { throw new Error('Settings cannot be undefined'); } _settings = settings; if (!_clientSecretCredential) { _clientSecretCredential = new ClientSecretCredential( _settings.tenantId, _settings.clientId, _settings.clientSecret, ); } if (!_appClient) { const authProvider = new TokenCredentialAuthenticationProvider( _clientSecretCredential, { scopes: ['https://graph.microsoft.com/.default'], }, ); _appClient = Client.initWithMiddleware({ authProvider: authProvider, }); } }
Replace the empty
initializeGraph
function in index.ts with the following.function initializeGraph(settings: AppSettings) { graphHelper.initializeGraphForAppOnlyAuth(settings); }
This code declares two private properties, a ClientSecretCredential
object and a Client
object. The InitializeGraphForAppOnlyAuth
function creates a new instance of ClientSecretCredential
, then uses that instance to create a new instance of Client
. Every time an API call is made to Microsoft Graph through the _appClient
, it uses the provided credential to get an access token.
Test the ClientSecretCredential
Next, add code to get an access token from the ClientSecretCredential
.
Add the following function to graphHelper.ts.
export async function getAppOnlyTokenAsync(): Promise<string> { // Ensure credential isn't undefined if (!_clientSecretCredential) { throw new Error('Graph has not been initialized for app-only auth'); } // Request token with given scopes const response = await _clientSecretCredential.getToken([ 'https://graph.microsoft.com/.default', ]); return response.token; }
Replace the empty
displayAccessTokenAsync
function in index.ts with the following.async function displayAccessTokenAsync() { try { const userToken = await graphHelper.getAppOnlyTokenAsync(); console.log(`App-only token: ${userToken}`); } catch (err) { console.log(`Error getting app-only access token: ${err}`); } }
Run the following command in your CLI in the root of your project.
npx ts-node index.ts
Enter
1
when prompted for an option. The application displays an access token.TypeScript Graph App-Only Tutorial [1] Display access token [2] List users [3] Make a Graph call [0] Exit Select an option [1...3 / 0]: 1 App-only token: eyJ0eXAiOiJKV1QiLCJub25jZSI6IlVDTzRYOWtKYlNLVjVkRzJGenJqd2xvVUcwWS...
Tip
For validation and debugging purposes only, you can decode app-only access tokens using Microsoft's online token parser at https://jwt.ms. This can be useful if you encounter token errors when calling Microsoft Graph. For example, verifying that the
role
claim in the token contains the expected Microsoft Graph permission scopes.
List users
In this section you will add the ability to list all users in your Azure Active Directory using app-only authentication.
Open graphHelper.ts and add the following function.
export async function getUsersAsync(): Promise<PageCollection> { // Ensure client isn't undefined if (!_appClient) { throw new Error('Graph has not been initialized for app-only auth'); } return _appClient ?.api('/users') .select(['displayName', 'id', 'mail']) .top(25) .orderby('displayName') .get(); }
Replace the empty
listUsersAsync
function in index.ts with the following.async function listUsersAsync() { try { const userPage = await graphHelper.getUsersAsync(); const users: User[] = userPage.value; // Output each user's details for (const user of users) { console.log(`User: ${user.displayName ?? 'NO NAME'}`); console.log(` ID: ${user.id}`); console.log(` Email: ${user.mail ?? 'NO EMAIL'}`); } // If @odata.nextLink is not undefined, there are more users // available on the server const moreAvailable = userPage['@odata.nextLink'] != undefined; console.log(`\nMore users available? ${moreAvailable}`); } catch (err) { console.log(`Error getting users: ${err}`); } }
Run the app, sign in, and choose option 2 to list users.
[1] Display access token [2] List users [3] Make a Graph call [0] Exit Select an option [1...3 / 0]: 2 User: Adele Vance ID: 05fb57bf-2653-4396-846d-2f210a91d9cf Email: [email protected] User: Alex Wilber ID: a36fe267-a437-4d24-b39e-7344774d606c Email: [email protected] User: Allan Deyoung ID: 54cebbaa-2c56-47ec-b878-c8ff309746b0 Email: [email protected] User: Bianca Pisani ID: 9a7dcbd0-72f0-48a9-a9fa-03cd46641d49 Email: NO EMAIL User: Brian Johnson (TAILSPIN) ID: a8989e40-be57-4c2e-bf0b-7cdc471e9cc4 Email: [email protected] ... More users available? true
Code explained
Consider the code in the getUsersAsync
function. It is very similar to the code in getInboxAsync
:
- It gets a collection of users
- It uses
select
to request specific properties - It uses
top
to limit the number of users returned - It uses
orderBy
to sort the response
Optional: add your own code
In this section you will add your own Microsoft Graph capabilities to the application. This could be a code snippet from Microsoft Graph documentation or Graph Explorer, or code that you created. This section is optional.
Update the app
Open graphHelper.ts and add the following function.
// This function serves as a playground for testing Graph snippets // or other code export async function makeGraphCallAsync() { // INSERT YOUR CODE HERE }
Replace the empty
makeGraphCallAsync
function in index.ts with the following.async function makeGraphCallAsync() { try { await graphHelper.makeGraphCallAsync(); } catch (err) { console.log(`Error making Graph call: ${err}`); } }
Choose an API
Find an API in Microsoft Graph you'd like to try. For example, the Create event API. You can use one of the examples in the API documentation, or you can customize an API request in Graph Explorer and use the generated snippet.
Configure permissions
Check the Permissions section of the reference documentation for your chosen API to see which authentication methods are supported. Some APIs don't support app-only, or personal Microsoft accounts, for example.
- To call an API with user authentication (if the API supports user (delegated) authentication), see the user (delegated) authentication tutorial.
- To call an API with app-only authentication (if the API supports it), add the required permission scope in the Azure AD admin center.
Add your code
Copy your code into the makeGraphCallAsync
function in graphHelper.ts. If you're copying a snippet from documentation or Graph Explorer, be sure to rename the client
to _appClient
.
Congratulations!
You've completed the TypeScript Microsoft Graph tutorial. Now that you have a working app that calls Microsoft Graph, you can experiment and add new features.
- Learn how to use user (delegated) authentication with the Microsoft Graph JavaScript SDK.
- Visit the Overview of Microsoft Graph to see all of the data you can access with Microsoft Graph.
Microsoft Graph Toolkit
If you are building TypeScript apps with UI, the Microsoft Graph Toolkit offers a collection of components that can simplify development.
TypeScript/JavaScript samples
Have an issue with this section? If so, please give us some feedback so we can improve this section.