Build Java apps with Microsoft Graph and app-only authentication
This tutorial teaches you how to build a Java 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 the Java SE Development Kit (JDK) and Gradle 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 OpenJDK version 17.0.2 and Gradle 7.4.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 Java console app
In this section you'll create a basic Java console app.
Open your command-line interface (CLI) in a directory where you want to create the project. Run the following command to create a new Gradle project.
gradle init --dsl groovy --test-framework junit --type java-application --project-name graphapponlytutorial --package graphapponlytutorial
Once the project is created, verify that it works by running the following command to run the app in your CLI.
./gradlew --console plain run
If it works, the app should output
Hello World.
.
Install dependencies
Before moving on, add some additional dependencies that you will use later.
- Azure Identity client library for Java to authenticate the user and acquire access tokens.
- Microsoft Graph SDK for Java to make calls to the Microsoft Graph.
Open ./app/build.gradle. Update the
dependencies
section to add those dependencies.dependencies { // Use JUnit test framework. testImplementation 'junit:junit:4.13.2' // This dependency is used by the application. implementation 'com.google.guava:guava:33.2.1-jre' implementation 'com.azure:azure-identity:1.13.0' implementation 'com.microsoft.graph:microsoft-graph:6.13.0' }
Add the following to the end of ./app/build.gradle.
run { standardInput = System.in }
The next time you build the project, Gradle will download those dependencies.
Load application settings
In this section you'll add the details of your app registration to the project.
Create a new directory named graphapponlytutorial in the ./app/src/main/resources directory.
Create a new file in the ./app/src/main/resources/graphapponlytutorial directory named oAuth.properties, and add the following text in that file.
app.clientId=YOUR_CLIENT_ID_HERE app.clientSecret=YOUR_CLIENT_SECRET_HERE app.tenantId=YOUR_TENANT_ID_HERE
Update the values according to the following table.
Setting Value app.clientId
The client ID of your app registration app.tenantId
The tenant ID of your organization app.clientSecret
The client secret Important
If you're using source control such as git, now would be a good time to exclude the oAuth.properties file from source control to avoid inadvertently leaking your app ID.
Design the app
In this section you will create a simple console-based menu.
Open ./app/src/main/java/graphapponlytutorial/App.java and add the following
import
statements.package graphapponlytutorial; import java.io.IOException; import java.util.InputMismatchException; import java.util.Properties; import java.util.Scanner; import com.microsoft.graph.models.User;
Replace the existing
main
function with the following.public static void main(String[] args) { System.out.println("Java App-Only Graph Tutorial"); System.out.println(); final Properties oAuthProperties = new Properties(); try { oAuthProperties.load(App.class.getResourceAsStream("oAuth.properties")); } catch (IOException e) { System.out.println("Unable to read OAuth configuration. Make sure you have a properly formatted oAuth.properties file. See README for details."); return; } initializeGraph(oAuthProperties); Scanner input = new Scanner(System.in); int choice = -1; while (choice != 0) { System.out.println("Please choose one of the following options:"); System.out.println("0. Exit"); System.out.println("1. Display access token"); System.out.println("2. List users"); System.out.println("3. Make a Graph call"); try { choice = input.nextInt(); } catch (InputMismatchException ex) { // Skip over non-integer input } input.nextLine(); // Process user choice switch(choice) { case 0: // Exit the program System.out.println("Goodbye..."); break; case 1: // Display access token displayAccessToken(); break; case 2: // List users listUsers(); break; case 3: // Run any Graph code makeGraphCall(); break; default: System.out.println("Invalid choice"); } } input.close(); }
Add the following placeholder methods at the end of the file. You'll implement them in later steps.
private static void initializeGraph(Properties properties) { // TODO } private static void displayAccessToken() { // TODO } private static void listUsers() { // TODO } private static void makeGraphCall() { // TODO }
This implements a basic menu and reads the user's choice from the command line.
- Delete ./app/src/test/java/graphapponlytutorial/AppTest.java.
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 Java into the application and configure authentication for the Microsoft Graph SDK for Java.
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.
Create a new file in the ./app/src/main/java/graphapponlytutorial directory named Graph.java and add the following code to that file.
package graphapponlytutorial; import java.util.Properties; import com.azure.core.credential.AccessToken; import com.azure.core.credential.TokenRequestContext; import com.azure.identity.ClientSecretCredential; import com.azure.identity.ClientSecretCredentialBuilder; import com.microsoft.graph.models.UserCollectionResponse; import com.microsoft.graph.serviceclient.GraphServiceClient;
Add an empty Graph class definition.
public class Graph { }
Add the following code to the Graph class.
private static Properties _properties; private static ClientSecretCredential _clientSecretCredential; private static GraphServiceClient _appClient; public static void initializeGraphForAppOnlyAuth(Properties properties) throws Exception { // Ensure properties isn't null if (properties == null) { throw new Exception("Properties cannot be null"); } _properties = properties; if (_clientSecretCredential == null) { final String clientId = _properties.getProperty("app.clientId"); final String tenantId = _properties.getProperty("app.tenantId"); final String clientSecret = _properties.getProperty("app.clientSecret"); _clientSecretCredential = new ClientSecretCredentialBuilder() .clientId(clientId) .tenantId(tenantId) .clientSecret(clientSecret) .build(); } if (_appClient == null) { _appClient = new GraphServiceClient(_clientSecretCredential, new String[] { "https://graph.microsoft.com/.default" }); } }
Replace the empty
initializeGraph
function in App.java with the following.private static void initializeGraph(Properties properties) { try { Graph.initializeGraphForAppOnlyAuth(properties); } catch (Exception e) { System.out.println("Error initializing Graph for user auth"); System.out.println(e.getMessage()); } }
This code declares two private properties, a ClientSecretCredential
object and a GraphServiceClient
object. The initializeGraphForAppOnlyAuth
function creates a new instance of ClientSecretCredential
, then uses that instance to create a new instance of GraphServiceClient
. Every time an API call is made to Microsoft Graph through the _appClient
, it will use 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 the
Graph
class.public static String getAppOnlyToken() throws Exception { // Ensure credential isn't null if (_clientSecretCredential == null) { throw new Exception("Graph has not been initialized for app-only auth"); } // Request the .default scope as required by app-only auth final String[] graphScopes = new String[] {"https://graph.microsoft.com/.default"}; final TokenRequestContext context = new TokenRequestContext(); context.addScopes(graphScopes); final AccessToken token = _clientSecretCredential.getToken(context).block(); return token.getToken(); }
Replace the empty
displayAccessToken
function in App.java with the following.private static void displayAccessToken() { try { final String accessToken = Graph.getAppOnlyToken(); System.out.println("Access token: " + accessToken); } catch (Exception e) { System.out.println("Error getting access token"); System.out.println(e.getMessage()); } }
Build and run the app. Enter
1
when prompted for an option. The application displays an access token.Java App-Only Graph Tutorial Please choose one of the following options: 0. Exit 1. Display access token 2. List users 3. Make a Graph call 1 App-only token: eyJ0eXAiOiJKV1QiLCJub25jZSI6IlVDTzRYOWtKYlNLVjVkRzJGenJqd2xvVUcwWS...
Tip
For validation and debugging purposes only, you can decode user access tokens (for work or school accounts only) 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
scp
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 Graph.java and add the following function to the Graph class.
public static UserCollectionResponse getUsers() throws Exception { // Ensure client isn't null if (_appClient == null) { throw new Exception("Graph has not been initialized for app-only auth"); } return _appClient.users().get(requestConfig -> { requestConfig.queryParameters.select = new String[] { "displayName", "id", "mail" }; requestConfig.queryParameters.top = 25; requestConfig.queryParameters.orderby = new String[] { "displayName" }; }); }
Replace the empty
listUsers
function in App.java with the following.private static void listUsers() { try { final UserCollectionResponse users = Graph.getUsers(); // Output each user's details for (User user: users.getValue()) { System.out.println("User: " + user.getDisplayName()); System.out.println(" ID: " + user.getId()); System.out.println(" Email: " + user.getMail()); } final Boolean moreUsersAvailable = users.getOdataNextLink() != null; System.out.println("\nMore users available? " + moreUsersAvailable); } catch (Exception e) { System.out.println("Error getting users"); System.out.println(e.getMessage()); } }
Run the app, sign in, and choose option 4 to list users.
Please choose one of the following options: 0. Exit 1. Display access token 2. List users 3. Make a Graph call 2 User: Adele Vance ID: 05fb57bf-2653-4396-846d-2f210a91d9cf Email: AdeleV@contoso.com User: Alex Wilber ID: a36fe267-a437-4d24-b39e-7344774d606c Email: AlexW@contoso.com User: Allan Deyoung ID: 54cebbaa-2c56-47ec-b878-c8ff309746b0 Email: AllanD@contoso.com User: Bianca Pisani ID: 9a7dcbd0-72f0-48a9-a9fa-03cd46641d49 Email: NO EMAIL User: Brian Johnson (TAILSPIN) ID: a8989e40-be57-4c2e-bf0b-7cdc471e9cc4 Email: BrianJ@contoso.com ... More users available? True
Code explained
Consider the code in the getUsers
function.
Accessing a collection
This method returns a collection of users. Most APIs in Microsoft Graph that return a collection do not return all available results in a single response. Instead, they use paging to return a portion of the results while providing a method for clients to request the next "page".
Default page sizes
APIs that use paging implement a default page size. For users, the default value is 10. Clients can request more (or less) by using the $top query parameter. In getUsers
, this is accomplished with the top
property in the request configuration.
Note
The value set in top
is an upper-bound, not an explicit number. The API returns a number of users up to the specified value.
Getting subsequent pages
If there are more results available on the server, collection responses include an @odata.nextLink
property with an API URL to access the next page. The Java client library exposes this as the getOdataNextLink
method on collection response objects. If this method returns non-null, there are more results available.
Sorting collections
The function uses the orderBy
property on the request configuration to request results sorted by the users' display names. This adds the $orderby query parameter to the API call.
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 Graph.java and add the following function to the Graph class.
public static void makeGraphCall() { // INSERT YOUR CODE HERE }
Replace the empty
MakeGraphCallAsync
function in App.java with the following.private static void makeGraphCall() { try { Graph.makeGraphCall(); } catch (Exception e) { System.out.println("Error making Graph call"); System.out.println(e.getMessage()); } }
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 Graph.java. If you're copying a snippet from documentation or Graph Explorer, be sure to rename the GraphServiceClient
to _appClient
.
Congratulations!
You've completed the Java 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 Java SDK.
- Visit the Overview of Microsoft Graph to see all of the data you can access with Microsoft Graph.
Java samples
Have an issue with this section? If so, please give us some feedback so we can improve this section.