From e9f88169fb5419f327eba2ebdb22a1b673242962 Mon Sep 17 00:00:00 2001 From: Jason Johnston Date: Fri, 20 Mar 2020 13:03:00 -0400 Subject: [PATCH 01/20] Recreated project with latest JDK/Maven --- demo/graphtutorial/pom.xml | 75 +++++++++++++++++++ .../src/main/java/com/contoso/App.java | 13 ++++ .../src/test/java/com/contoso/AppTest.java | 20 +++++ tutorial/01-intro.md | 2 +- 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 demo/graphtutorial/pom.xml create mode 100644 demo/graphtutorial/src/main/java/com/contoso/App.java create mode 100644 demo/graphtutorial/src/test/java/com/contoso/AppTest.java diff --git a/demo/graphtutorial/pom.xml b/demo/graphtutorial/pom.xml new file mode 100644 index 0000000..d78062a --- /dev/null +++ b/demo/graphtutorial/pom.xml @@ -0,0 +1,75 @@ + + + + 4.0.0 + + com.contoso + graphtutorial + 1.0-SNAPSHOT + + graphtutorial + + http://www.example.com + + + UTF-8 + 1.7 + 1.7 + + + + + junit + junit + 4.11 + test + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + diff --git a/demo/graphtutorial/src/main/java/com/contoso/App.java b/demo/graphtutorial/src/main/java/com/contoso/App.java new file mode 100644 index 0000000..7875c95 --- /dev/null +++ b/demo/graphtutorial/src/main/java/com/contoso/App.java @@ -0,0 +1,13 @@ +package com.contoso; + +/** + * Hello world! + * + */ +public class App +{ + public static void main( String[] args ) + { + System.out.println( "Hello World!" ); + } +} diff --git a/demo/graphtutorial/src/test/java/com/contoso/AppTest.java b/demo/graphtutorial/src/test/java/com/contoso/AppTest.java new file mode 100644 index 0000000..6a9717e --- /dev/null +++ b/demo/graphtutorial/src/test/java/com/contoso/AppTest.java @@ -0,0 +1,20 @@ +package com.contoso; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +} diff --git a/tutorial/01-intro.md b/tutorial/01-intro.md index 16b9a7d..9223f27 100644 --- a/tutorial/01-intro.md +++ b/tutorial/01-intro.md @@ -10,7 +10,7 @@ This tutorial teaches you how to build a Java console app that uses the Microsof Before you start this tutorial, you should have the [Java SE Development Kit (JDK)](https://java.com/en/download/faq/develop.xml) and [Maven](https://maven.apache.org/) installed on your development machine. If you do not have the JDK or Maven, visit the previous links for download options. > [!NOTE] -> This tutorial was written with OpenJDK version 12.0.1 and Maven 3.6.1. The steps in this guide may work with other versions, but that has not been tested. +> This tutorial was written with OpenJDK version 14.0.0.36 and Maven 3.6.3. The steps in this guide may work with other versions, but that has not been tested. ## Feedback From bd3970d8f5f51c3511079c19152f257a0804ca42 Mon Sep 17 00:00:00 2001 From: Jason Johnston Date: Fri, 20 Mar 2020 13:10:54 -0400 Subject: [PATCH 02/20] Completed create the app step --- demo/graphtutorial/pom.xml | 20 +++ .../src/main/java/com/contoso/App.java | 50 +++++- tutorial/02-create-app.md | 158 ++++++++---------- 3 files changed, 138 insertions(+), 90 deletions(-) diff --git a/demo/graphtutorial/pom.xml b/demo/graphtutorial/pom.xml index d78062a..c99e3b1 100644 --- a/demo/graphtutorial/pom.xml +++ b/demo/graphtutorial/pom.xml @@ -25,6 +25,26 @@ 4.11 test + + + + org.slf4j + slf4j-nop + 1.8.0-beta4 + + + + com.microsoft.graph + microsoft-graph + 1.6.0 + + + + com.microsoft.azure + msal4j + 1.4.0 + + diff --git a/demo/graphtutorial/src/main/java/com/contoso/App.java b/demo/graphtutorial/src/main/java/com/contoso/App.java index 7875c95..7efcd82 100644 --- a/demo/graphtutorial/src/main/java/com/contoso/App.java +++ b/demo/graphtutorial/src/main/java/com/contoso/App.java @@ -1,13 +1,51 @@ package com.contoso; +import java.util.InputMismatchException; +import java.util.Scanner; + /** - * Hello world! + * Graph Tutorial * */ -public class App -{ - public static void main( String[] args ) - { - System.out.println( "Hello World!" ); +public class App { + public static void main(String[] args) { + System.out.println("Java Graph Tutorial"); + System.out.println(); + + 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 calendar events"); + + 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 + break; + case 2: + // List the calendar + break; + default: + System.out.println("Invalid choice"); + } + } + + input.close(); } } diff --git a/tutorial/02-create-app.md b/tutorial/02-create-app.md index 8182302..65a111e 100644 --- a/tutorial/02-create-app.md +++ b/tutorial/02-create-app.md @@ -1,107 +1,97 @@ -Open your command-line interface (CLI) in a directory where you want to create the project. Run the following command to create a new Maven project. +In this section you'll create a basic Java console app. -```Shell -mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=com.contoso -DartifactId=graphtutorial -Dversion=1.0-SNAPSHOT -``` +1. Open your command-line interface (CLI) in a directory where you want to create the project. Run the following command to create a new Maven project. -> [!IMPORTANT] -> You can enter different values for the group ID (`DgroupId` parameter) and artifact ID (`DartifactId` parameter) than the values specified above. The sample code in this tutorial assumes that the group ID `com.contoso` was used. If you use a different value, be sure to replace `com.contoso` in any sample code with your group ID. + ```Shell + mvn archetype:generate "-DarchetypeArtifactId=maven-archetype-quickstart" "-DarchetypeGroupId=org.apache.maven.archetypes" "-DgroupId=com.contoso" "-DartifactId=graphtutorial" "-Dversion=1.0-SNAPSHOT" + ``` -When prompted, confirm the configuration, then wait for the project to be created. Once the project is created, verify that it works by running the following commands to package and run the app in your CLI. + > [!IMPORTANT] + > You can enter different values for the group ID (`DgroupId` parameter) and artifact ID (`DartifactId` parameter) than the values specified above. The sample code in this tutorial assumes that the group ID `com.contoso` was used. If you use a different value, be sure to replace `com.contoso` in any sample code with your group ID. -```Shell -mvn package -java -cp target/graphtutorial-1.0-SNAPSHOT.jar com.contoso.App -``` + When prompted, confirm the configuration, then wait for the project to be created. -If it works, the app should output `Hello World!`. Before moving on, add some additional dependencies that you will use later. +1. Once the project is created, verify that it works by changing to the `graphtutorial` directory and running the following commands to package and run the app in your CLI. + + ```Shell + mvn package + java -cp target/graphtutorial-1.0-SNAPSHOT.jar com.contoso.App + ``` + + If it works, the app should output `Hello World!`. + +## Install dependencies + +Before moving on, add some additional dependencies that you will use later. - [Microsoft Authentication Library (MSAL) for Java](https://github.com/AzureAD/microsoft-authentication-library-for-java) to authenticate the user and acquire access tokens. - [Microsoft Graph SDK for Java](https://github.com/microsoftgraph/msgraph-sdk-java) to make calls to the Microsoft Graph. - [SLF4J NOP Binding](https://mvnrepository.com/artifact/org.slf4j/slf4j-nop) to suppress logging from MSAL. -Open **./graphtutorial/pom.xml**. Add the following inside the `` element. +1. Open **./graphtutorial/pom.xml**. Add the following inside the `` element. -```xml - - org.slf4j - slf4j-nop - 1.8.0-beta4 - - - - com.microsoft.graph - microsoft-graph - 1.6.0 - - - - com.microsoft.azure - msal4j - 1.1.0 - -``` + :::code language="xml" source="../demo/graphtutorial/pom.xml" id="PomDependenciesSnippet"::: The next time you build the project, Maven will download those dependencies. ## Design the app -Open the **./graphtutorial/src/main/java/com/contoso/App.java** file and replace its contents with the following. - -```java -package com.contoso; - -import java.util.InputMismatchException; -import java.util.Scanner; - -/** - * Graph Tutorial - * - */ -public class App { - public static void main(String[] args) { - System.out.println("Java Graph Tutorial"); - System.out.println(); - - 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 calendar events"); - - try { - choice = input.nextInt(); - } catch (InputMismatchException ex) { - // Skip over non-integer input - input.nextLine(); +1. Open the **./graphtutorial/src/main/java/com/contoso/App.java** file and replace its contents with the following. + + ```java + package com.contoso; + + import java.util.InputMismatchException; + import java.util.Scanner; + + /** + * Graph Tutorial + * + */ + public class App { + public static void main(String[] args) { + System.out.println("Java Graph Tutorial"); + System.out.println(); + + 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 calendar events"); + + 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 + break; + case 2: + // List the calendar + break; + default: + System.out.println("Invalid choice"); + } } - // Process user choice - switch(choice) { - case 0: - // Exit the program - System.out.println("Goodbye..."); - break; - case 1: - // Display access token - break; - case 2: - // List the calendar - break; - default: - System.out.println("Invalid choice"); - } + input.close(); } - - input.close(); } -} -``` + ``` -This implements a basic menu and reads the user's choice from the command line. + This implements a basic menu and reads the user's choice from the command line. From 5836ee4b6a5584abb364890a825c81b30abd5206 Mon Sep 17 00:00:00 2001 From: Jason Johnston Date: Fri, 20 Mar 2020 13:17:54 -0400 Subject: [PATCH 03/20] Updated app registration steps --- tutorial/03-register-app.md | 8 ++------ tutorial/images/aad-register-an-app.png | Bin 35083 -> 16133 bytes 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/tutorial/03-register-app.md b/tutorial/03-register-app.md index 7bec243..3b3c087 100644 --- a/tutorial/03-register-app.md +++ b/tutorial/03-register-app.md @@ -12,7 +12,7 @@ In this exercise you will create a new Azure AD application using the Azure Acti - Set **Name** to `Java Graph Tutorial`. - Set **Supported account types** to **Accounts in any organizational directory and personal Microsoft accounts**. - - Leave **Redirect URI** empty. + - Under **Redirect URI**, change the dropdown to **Public client (mobile & desktop)**, and set the value to `https://login.microsoftonline.com/common/oauth2/nativeclient`. ![A screenshot of the Register an application page](./images/aad-register-an-app.png) @@ -20,10 +20,6 @@ In this exercise you will create a new Azure AD application using the Azure Acti ![A screenshot of the application ID of the new app registration](./images/aad-application-id.png) -1. Select the **Add a Redirect URI** link. On the **Redirect URIs** page, locate the **Suggested Redirect URIs for public clients (mobile, desktop)** section. Select the `https://login.microsoftonline.com/common/oauth2/nativeclient` URI. - - ![A screenshot of the Redirect URIs page](./images/aad-redirect-uris.png) - -1. Locate the **Default client type** section and change the **Treat application as a public client** toggle to **Yes**, then choose **Save**. +1. Select **Authentication** under **Manage**. Locate the **Advanced settings** section and change the **Treat application as a public client** toggle to **Yes**, then choose **Save**. ![A screenshot of the Default client type section](./images/aad-default-client-type.png) diff --git a/tutorial/images/aad-register-an-app.png b/tutorial/images/aad-register-an-app.png index 9c49ee662eb259f0c3d6628f6e6ae64a02840106..45e26e9ec7f3b68c03bdd4683e32d8d9ddcfc14e 100644 GIT binary patch literal 16133 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 ycwzB>XB6S$Z-mU#f7_azf7{SM{<<84y_ From b2d6855088ad704159b4b30c0bcd569466249b45 Mon Sep 17 00:00:00 2001 From: Jason Johnston Date: Fri, 20 Mar 2020 15:40:21 -0400 Subject: [PATCH 04/20] Completed add AAD auth step --- demo/graphtutorial/pom.xml | 6 +- .../src/main/java/com/contoso/App.java | 27 +++ .../main/java/com/contoso/Authentication.java | 74 ++++++++ .../com/contoso/oAuth.properties.example | 2 + tutorial/02-create-app.md | 6 +- tutorial/04-add-aad-auth.md | 166 +++++------------- 6 files changed, 156 insertions(+), 125 deletions(-) create mode 100644 demo/graphtutorial/src/main/java/com/contoso/Authentication.java create mode 100644 demo/graphtutorial/src/main/resources/com/contoso/oAuth.properties.example diff --git a/demo/graphtutorial/pom.xml b/demo/graphtutorial/pom.xml index c99e3b1..5555d52 100644 --- a/demo/graphtutorial/pom.xml +++ b/demo/graphtutorial/pom.xml @@ -12,11 +12,13 @@ http://www.example.com + UTF-8 - 1.7 - 1.7 + 14 + 14 + diff --git a/demo/graphtutorial/src/main/java/com/contoso/App.java b/demo/graphtutorial/src/main/java/com/contoso/App.java index 7efcd82..7465af4 100644 --- a/demo/graphtutorial/src/main/java/com/contoso/App.java +++ b/demo/graphtutorial/src/main/java/com/contoso/App.java @@ -1,6 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + package com.contoso; +import java.io.InputStream; +import java.io.IOException; import java.util.InputMismatchException; +import java.util.Properties; import java.util.Scanner; /** @@ -12,6 +18,26 @@ public static void main(String[] args) { System.out.println("Java Graph Tutorial"); System.out.println(); + // + // Load OAuth settings + 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; + } + + + final String appId = oAuthProperties.getProperty("app.id"); + final String[] appScopes = oAuthProperties.getProperty("app.scopes").split(","); + // + + // Get an access token + Authentication.initialize(appId); + final String accessToken = Authentication.getUserAccessToken(appScopes); + Scanner input = new Scanner(System.in); int choice = -1; @@ -37,6 +63,7 @@ public static void main(String[] args) { break; case 1: // Display access token + System.out.println("Access token: " + accessToken); break; case 2: // List the calendar diff --git a/demo/graphtutorial/src/main/java/com/contoso/Authentication.java b/demo/graphtutorial/src/main/java/com/contoso/Authentication.java new file mode 100644 index 0000000..ff3c445 --- /dev/null +++ b/demo/graphtutorial/src/main/java/com/contoso/Authentication.java @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +// +package com.contoso; + +import java.net.MalformedURLException; +import java.util.Set; +import java.util.function.Consumer; + +import com.microsoft.aad.msal4j.DeviceCode; +import com.microsoft.aad.msal4j.DeviceCodeFlowParameters; +import com.microsoft.aad.msal4j.IAuthenticationResult; +import com.microsoft.aad.msal4j.PublicClientApplication; + +/** + * Authentication + */ +public class Authentication { + + private static String applicationId; + // Set authority to allow only organizational accounts + // Device code flow only supports organizational accounts + private final static String authority = "https://login.microsoftonline.com/common/"; + + public static void initialize(String applicationId) { + Authentication.applicationId = applicationId; + } + + public static String getUserAccessToken(String[] scopes) { + if (applicationId == null) { + System.out.println("You must initialize Authentication before calling getUserAccessToken"); + return null; + } + + Set scopeSet = Set.of(scopes); + + PublicClientApplication app; + try { + // Build the MSAL application object with + // app ID and authority + app = PublicClientApplication.builder(applicationId) + .authority(authority) + .build(); + } catch (MalformedURLException e) { + return null; + } + + // Create consumer to receive the DeviceCode object + // This method gets executed during the flow and provides + // the URL the user logs into and the device code to enter + Consumer deviceCodeConsumer = (DeviceCode deviceCode) -> { + // Print the login information to the console + System.out.println(deviceCode.message()); + }; + + // Request a token, passing the requested permission scopes + IAuthenticationResult result = app.acquireToken( + DeviceCodeFlowParameters + .builder(scopeSet, deviceCodeConsumer) + .build() + ).exceptionally(ex -> { + System.out.println("Unable to authenticate - " + ex.getMessage()); + return null; + }).join(); + + if (result != null) { + return result.accessToken(); + } + + return null; + } +} +// diff --git a/demo/graphtutorial/src/main/resources/com/contoso/oAuth.properties.example b/demo/graphtutorial/src/main/resources/com/contoso/oAuth.properties.example new file mode 100644 index 0000000..a839af4 --- /dev/null +++ b/demo/graphtutorial/src/main/resources/com/contoso/oAuth.properties.example @@ -0,0 +1,2 @@ +app.id=YOUR_APP_ID_HERE +app.scopes=User.Read,Calendars.Read diff --git a/tutorial/02-create-app.md b/tutorial/02-create-app.md index 65a111e..dc1998c 100644 --- a/tutorial/02-create-app.md +++ b/tutorial/02-create-app.md @@ -13,11 +13,15 @@ In this section you'll create a basic Java console app. When prompted, confirm the configuration, then wait for the project to be created. +1. Open **./graphtutorial/pom.xml** and update the `maven.compiler.source` and `maven.compiler.target` values to match your version of Java. + + :::code language="xml" source="../demo/graphtutorial/pom.xml" id="JavaVersionSnippet" highlight="3-4"::: + 1. Once the project is created, verify that it works by changing to the `graphtutorial` directory and running the following commands to package and run the app in your CLI. ```Shell mvn package - java -cp target/graphtutorial-1.0-SNAPSHOT.jar com.contoso.App + mvn exec:java -D exec.mainClass="com.contoso.App" ``` If it works, the app should output `Hello World!`. diff --git a/tutorial/04-add-aad-auth.md b/tutorial/04-add-aad-auth.md index 07c23e9..ed3c565 100644 --- a/tutorial/04-add-aad-auth.md +++ b/tutorial/04-add-aad-auth.md @@ -2,136 +2,58 @@ In this exercise you will extend the application from the previous exercise to support authentication with Azure AD. This is required to obtain the necessary OAuth access token to call the Microsoft Graph. In this step you will integrate the [Microsoft Authentication Library (MSAL) for Java](https://github.com/AzureAD/microsoft-authentication-library-for-java) into the application. -Create a new directory named **resources** in the **./graphtutorial/src/main** directory. Then create a new directory named **com** in the **resources** directory, then a new directory named **contoso** in the **com** directory. Finally, create a new file in the **./graphtutorial/src/main/resources/com/contoso** directory named **oAuth.properties**, and add the following text in that file. +1. Create a new directory named **resources** in the **./graphtutorial/src/main** directory. -```INI -app.id=YOUR_APP_ID_HERE -app.scopes=User.Read,Calendars.Read -``` +1. Create a new directory named **com** in the **resources** directory. -Replace `YOUR_APP_ID_HERE` with the application ID you created in the Azure portal. +1. Create a new directory named **contoso** in the **com** directory. -> [!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. +1. Create a new file in the **./graphtutorial/src/main/resources/com/contoso** directory named **oAuth.properties**, and add the following text in that file. -Open **App.java** and add the following `import` statements. + :::code language="ini" source="../demo/graphtutorial/src/main/resources/com/contoso/oAuth.properties.example"::: -```java -import java.io.IOException; -import java.util.Properties; -``` + Replace `YOUR_APP_ID_HERE` with the application ID you created in the Azure portal. -Then add the following code just before the `Scanner input = new Scanner(System.in);` line to load the **oAuth.properties** file. + > [!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. -```java -// Load OAuth settings -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; -} +1. Open **App.java** and add the following `import` statements. -final String appId = oAuthProperties.getProperty("app.id"); -final String[] appScopes = oAuthProperties.getProperty("app.scopes").split(","); -``` + ```java + import java.io.IOException; + import java.util.Properties; + ``` + +1. Add the following code just before the `Scanner input = new Scanner(System.in);` line to load the **oAuth.properties** file. + + :::code language="java" source="../demo/graphtutorial/src/main/java/com/contoso/App.java" id="LoadSettingsSnippet"::: ## Implement sign-in -Create a new file in the **./graphtutorial/src/main/java/com/contoso** directory named **Authentication.java** and add the following code. - -```java -package com.contoso; -import java.net.MalformedURLException; -import java.util.Set; -import java.util.function.Consumer; - -import com.microsoft.aad.msal4j.DeviceCode; -import com.microsoft.aad.msal4j.DeviceCodeFlowParameters; -import com.microsoft.aad.msal4j.IAuthenticationResult; -import com.microsoft.aad.msal4j.PublicClientApplication; - -/** - * Authentication - */ -public class Authentication { - - private static String applicationId; - // Set authority to allow only organizational accounts - // Device code flow only supports organizational accounts - private final static String authority = "https://login.microsoftonline.com/common/"; - - public static void initialize(String applicationId) { - Authentication.applicationId = applicationId; - } - - public static String getUserAccessToken(String[] scopes) { - if (applicationId == null) { - System.out.println("You must initialize Authentication before calling getUserAccessToken"); - return null; - } - - Set scopeSet = Set.of(scopes); - - PublicClientApplication app; - try { - // Build the MSAL application object with - // app ID and authority - app = PublicClientApplication.builder(applicationId) - .authority(authority) - .build(); - } catch (MalformedURLException e) { - return null; - } - - // Create consumer to receive the DeviceCode object - // This method gets executed during the flow and provides - // the URL the user logs into and the device code to enter - Consumer deviceCodeConsumer = (DeviceCode deviceCode) -> { - // Print the login information to the console - System.out.println(deviceCode.message()); - }; - - // Request a token, passing the requested permission scopes - IAuthenticationResult result = app.acquireToken( - DeviceCodeFlowParameters - .builder(scopeSet, deviceCodeConsumer) - .build() - ).exceptionally(ex -> { - System.out.println("Unable to authenticate - " + ex.getMessage()); - return null; - }).join(); - - if (result != null) { - return result.accessToken(); - } - - return null; - } -} -``` - -In **App.java**, add the following code just before the `Scanner input = new Scanner(System.in);` line to get an access token. - -```java -// Get an access token -Authentication.initialize(appId); -final String accessToken = Authentication.getUserAccessToken(appScopes); -``` - -Then add the following line after the `// Display access token` comment. - -```java -System.out.println("Access token: " + accessToken); -``` - -Build and run the app. The application displays a URL and device code. - -```Shell -Java Graph Tutorial - -To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code F7CG945YZ to authenticate. -``` - -Open a browser and browse to the URL displayed. Enter the provided code and sign in. Once completed, return to the application and choose the **1. Display access token** option to display the access token. +1. Create a new file in the **./graphtutorial/src/main/java/com/contoso** directory named **Authentication.java** and add the following code. + + :::code language="java" source="../demo/graphtutorial/src/main/java/com/contoso/Authentication.java" id="AuthenticationSnippet"::: + +1. In **App.java**, add the following code just before the `Scanner input = new Scanner(System.in);` line to get an access token. + + ```java + // Get an access token + Authentication.initialize(appId); + final String accessToken = Authentication.getUserAccessToken(appScopes); + ``` + +1. Add the following line after the `// Display access token` comment. + + ```java + System.out.println("Access token: " + accessToken); + ``` + +1. Build and run the app. The application displays a URL and device code. + + ```Shell + Java Graph Tutorial + + To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code F7CG945YZ to authenticate. + ``` + +1. Open a browser and browse to the URL displayed. Enter the provided code and sign in. Once completed, return to the application and choose the **1. Display access token** option to display the access token. From 57fecd433e7b77e1079fb65013d511fc11432832 Mon Sep 17 00:00:00 2001 From: Jason Johnston Date: Fri, 20 Mar 2020 15:46:14 -0400 Subject: [PATCH 05/20] Fix for java.lang.IllegalThreadStateException on exit --- .../src/main/java/com/contoso/Authentication.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/demo/graphtutorial/src/main/java/com/contoso/Authentication.java b/demo/graphtutorial/src/main/java/com/contoso/Authentication.java index ff3c445..2cd27c2 100644 --- a/demo/graphtutorial/src/main/java/com/contoso/Authentication.java +++ b/demo/graphtutorial/src/main/java/com/contoso/Authentication.java @@ -6,6 +6,8 @@ import java.net.MalformedURLException; import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.function.Consumer; import com.microsoft.aad.msal4j.DeviceCode; @@ -35,12 +37,14 @@ public static String getUserAccessToken(String[] scopes) { Set scopeSet = Set.of(scopes); + ExecutorService pool = Executors.newFixedThreadPool(1); PublicClientApplication app; try { // Build the MSAL application object with // app ID and authority app = PublicClientApplication.builder(applicationId) .authority(authority) + .executorService(pool) .build(); } catch (MalformedURLException e) { return null; @@ -64,6 +68,8 @@ public static String getUserAccessToken(String[] scopes) { return null; }).join(); + pool.shutdown(); + if (result != null) { return result.accessToken(); } From d3bbac13783b58ce00dc538c39118a02e973d54e Mon Sep 17 00:00:00 2001 From: Jason Johnston Date: Fri, 20 Mar 2020 16:57:35 -0400 Subject: [PATCH 06/20] Completed add MS Graph step --- .../src/main/java/com/contoso/App.java | 43 +++ .../src/main/java/com/contoso/Graph.java | 82 +++++ .../java/com/contoso/SimpleAuthProvider.java | 27 ++ tutorial/05-add-ms-graph.md | 296 +++++++----------- 4 files changed, 270 insertions(+), 178 deletions(-) create mode 100644 demo/graphtutorial/src/main/java/com/contoso/Graph.java create mode 100644 demo/graphtutorial/src/main/java/com/contoso/SimpleAuthProvider.java diff --git a/demo/graphtutorial/src/main/java/com/contoso/App.java b/demo/graphtutorial/src/main/java/com/contoso/App.java index 7465af4..11ffa31 100644 --- a/demo/graphtutorial/src/main/java/com/contoso/App.java +++ b/demo/graphtutorial/src/main/java/com/contoso/App.java @@ -5,10 +5,18 @@ import java.io.InputStream; import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; import java.util.InputMismatchException; +import java.util.List; import java.util.Properties; import java.util.Scanner; +import com.microsoft.graph.models.extensions.DateTimeTimeZone; +import com.microsoft.graph.models.extensions.Event; +import com.microsoft.graph.models.extensions.User; + /** * Graph Tutorial * @@ -38,6 +46,11 @@ public static void main(String[] args) { Authentication.initialize(appId); final String accessToken = Authentication.getUserAccessToken(appScopes); + // Greet the user + User user = Graph.getUser(accessToken); + System.out.println("Welcome " + user.displayName); + System.out.println(); + Scanner input = new Scanner(System.in); int choice = -1; @@ -67,12 +80,42 @@ public static void main(String[] args) { break; case 2: // List the calendar + listCalendarEvents(accessToken); break; default: System.out.println("Invalid choice"); } } + Graph.shutdown(); input.close(); } + + // + private static String formatDateTimeTimeZone(DateTimeTimeZone date) { + LocalDateTime dateTime = LocalDateTime.parse(date.dateTime); + + return dateTime.format( + DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)) + + " (" + date.timeZone + ")"; + } + // + + // + private static void listCalendarEvents(String accessToken) { + // Get the user's events + List events = Graph.getEvents(accessToken); + + System.out.println("Events:"); + + for (Event event : events) { + System.out.println("Subject: " + event.subject); + System.out.println(" Organizer: " + event.organizer.emailAddress.name); + System.out.println(" Start: " + formatDateTimeTimeZone(event.start)); + System.out.println(" End: " + formatDateTimeTimeZone(event.end)); + } + + System.out.println(); + } + // } diff --git a/demo/graphtutorial/src/main/java/com/contoso/Graph.java b/demo/graphtutorial/src/main/java/com/contoso/Graph.java new file mode 100644 index 0000000..2cbc0d0 --- /dev/null +++ b/demo/graphtutorial/src/main/java/com/contoso/Graph.java @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.contoso; + +import java.util.LinkedList; +import java.util.List; + +import com.microsoft.graph.logger.DefaultLogger; +import com.microsoft.graph.logger.LoggerLevel; +import com.microsoft.graph.models.extensions.Event; +import com.microsoft.graph.models.extensions.IGraphServiceClient; +import com.microsoft.graph.models.extensions.User; +import com.microsoft.graph.options.Option; +import com.microsoft.graph.options.QueryOption; +import com.microsoft.graph.requests.extensions.GraphServiceClient; +import com.microsoft.graph.requests.extensions.IEventCollectionPage; + +/** + * Graph + */ +public class Graph { + + private static IGraphServiceClient graphClient = null; + private static SimpleAuthProvider authProvider = null; + + private static void ensureGraphClient(String accessToken) { + if (graphClient == null) { + // Create the auth provider + authProvider = new SimpleAuthProvider(accessToken); + + // Create default logger to only log errors + DefaultLogger logger = new DefaultLogger(); + logger.setLoggingLevel(LoggerLevel.DEBUG); + + // Build a Graph client + graphClient = GraphServiceClient.builder() + .authenticationProvider(authProvider) + .logger(logger) + .buildClient(); + } + } + + public static void shutdown() { + if (graphClient != null) { + graphClient.shutdown(); + System.out.println("Shut down client"); + } + } + + public static User getUser(String accessToken) { + ensureGraphClient(accessToken); + + // GET /me to get authenticated user + User me = graphClient + .me() + .buildRequest() + .get(); + + return me; + } + + // + public static List getEvents(String accessToken) { + ensureGraphClient(accessToken); + + // Use QueryOption to specify the $orderby query parameter + final List +} diff --git a/demo/graphtutorial/src/main/java/com/contoso/SimpleAuthProvider.java b/demo/graphtutorial/src/main/java/com/contoso/SimpleAuthProvider.java new file mode 100644 index 0000000..2cdb7bb --- /dev/null +++ b/demo/graphtutorial/src/main/java/com/contoso/SimpleAuthProvider.java @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +// +package com.contoso; + +import com.microsoft.graph.authentication.IAuthenticationProvider; +import com.microsoft.graph.http.IHttpRequest; + +/** + * SimpleAuthProvider + */ +public class SimpleAuthProvider implements IAuthenticationProvider { + + private String accessToken = null; + + public SimpleAuthProvider(String accessToken) { + this.accessToken = accessToken; + } + + @Override + public void authenticateRequest(IHttpRequest request) { + // Add the access token in the Authorization header + request.addHeader("Authorization", "Bearer " + accessToken); + } +} +// diff --git a/tutorial/05-add-ms-graph.md b/tutorial/05-add-ms-graph.md index 08eca23..63b7d19 100644 --- a/tutorial/05-add-ms-graph.md +++ b/tutorial/05-add-ms-graph.md @@ -4,133 +4,93 @@ In this exercise you will incorporate the Microsoft Graph into the application. ## Implement an authentication provider -The Microsoft Graph SDK for Java requires an implementation of the `IAuthenticationProvider` interface to instantiate its `GraphServiceClient` object. Start by creating a simple class to add the access token to outgoing requests. Create a new file in the **./graphtutorial/src/main/java/com/contoso** directory named **SimpleAuthProvider.java** and add the following code. +The Microsoft Graph SDK for Java requires an implementation of the `IAuthenticationProvider` interface to instantiate its `GraphServiceClient` object. -```java -package com.contoso; - -import com.microsoft.graph.authentication.IAuthenticationProvider; -import com.microsoft.graph.http.IHttpRequest; - -/** - * SimpleAuthProvider - */ -public class SimpleAuthProvider implements IAuthenticationProvider { +1. Create a new file in the **./graphtutorial/src/main/java/com/contoso** directory named **SimpleAuthProvider.java** and add the following code. - private String accessToken = null; - - public SimpleAuthProvider(String accessToken) { - this.accessToken = accessToken; - } - - @Override - public void authenticateRequest(IHttpRequest request) { - // Add the access token in the Authorization header - request.addHeader("Authorization", "Bearer " + accessToken); - } -} -``` + :::code language="java" source="../demo/graphtutorial/src/main/java/com/contoso/SimpleAuthProvider.java" id="AuthProviderSnippet"::: ## Get user details -First, add a new class to contain all of the Graph functionality. Create a new file in the **./graphtutorial/src/main/java/com/contoso** directory named **Graph.java** and add the following code. - -```java -package com.contoso; - -import com.microsoft.graph.logger.DefaultLogger; -import com.microsoft.graph.logger.LoggerLevel; -import com.microsoft.graph.models.extensions.IGraphServiceClient; -import com.microsoft.graph.models.extensions.User; -import com.microsoft.graph.requests.extensions.GraphServiceClient; -import java.util.LinkedList; -import java.util.List;import com.microsoft.graph.models.extensions.Event;import com.microsoft.graph.options.Option; -import com.microsoft.graph.options.QueryOption; -/** - * Graph - */ -public class Graph { - - private static IGraphServiceClient graphClient = null; - private static SimpleAuthProvider authProvider = null; - - private static void ensureGraphClient(String accessToken) { - if (graphClient == null) { - // Create the auth provider - authProvider = new SimpleAuthProvider(accessToken); - - // Create default logger to only log errors - DefaultLogger logger = new DefaultLogger(); - logger.setLoggingLevel(LoggerLevel.ERROR); - - // Build a Graph client - graphClient = GraphServiceClient.builder() - .authenticationProvider(authProvider) - .logger(logger) - .buildClient(); +1. Create a new file in the **./graphtutorial/src/main/java/com/contoso** directory named **Graph.java** and add the following code. + + ```java + package com.contoso; + + import java.util.LinkedList; + import java.util.List; + + import com.microsoft.graph.logger.DefaultLogger; + import com.microsoft.graph.logger.LoggerLevel; + import com.microsoft.graph.models.extensions.Event; + import com.microsoft.graph.models.extensions.IGraphServiceClient; + import com.microsoft.graph.models.extensions.User; + import com.microsoft.graph.options.Option; + import com.microsoft.graph.options.QueryOption; + import com.microsoft.graph.requests.extensions.GraphServiceClient; + import com.microsoft.graph.requests.extensions.IEventCollectionPage; + + /** + * Graph + */ + public class Graph { + + private static IGraphServiceClient graphClient = null; + private static SimpleAuthProvider authProvider = null; + + private static void ensureGraphClient(String accessToken) { + if (graphClient == null) { + // Create the auth provider + authProvider = new SimpleAuthProvider(accessToken); + + // Create default logger to only log errors + DefaultLogger logger = new DefaultLogger(); + logger.setLoggingLevel(LoggerLevel.ERROR); + + // Build a Graph client + graphClient = GraphServiceClient.builder() + .authenticationProvider(authProvider) + .logger(logger) + .buildClient(); + } } - } - public static User getUser(String accessToken) { - ensureGraphClient(accessToken); + public static User getUser(String accessToken) { + ensureGraphClient(accessToken); - // GET /me to get authenticated user - User me = graphClient - .me() - .buildRequest() - .get(); + // GET /me to get authenticated user + User me = graphClient + .me() + .buildRequest() + .get(); - return me; + return me; + } } -} -``` + ``` -Add the following code in **App.java** just before the `Scanner input = new Scanner(System.in);` line to get the user and output the user's display name. +1. Add the following `import` statement at the top of **App.java**. -```java -// Greet the user -User user = Graph.getUser(accessToken); -System.out.println("Welcome " + user.displayName); -System.out.println(); -``` + ```java + import com.microsoft.graph.models.extensions.User; + ``` -If you run the app now, after you log in the app welcomes you by name. +1. Add the following code in **App.java** just before the `Scanner input = new Scanner(System.in);` line to get the user and output the user's display name. -## Get calendar events from Outlook + ```java + // Greet the user + User user = Graph.getUser(accessToken); + System.out.println("Welcome " + user.displayName); + System.out.println(); + ``` -Add the following `import` statements to **Graph.java**. +1. Build and run the app. After you log in the app welcomes you by name. -```java -import java.util.LinkedList; -import java.util.List; -import com.microsoft.graph.models.extensions.Event; -import com.microsoft.graph.options.Option; -import com.microsoft.graph.options.QueryOption; -import com.microsoft.graph.requests.extensions.IEventCollectionPage; -``` +## Get calendar events from Outlook -Add the following function to the `Graph` class in **Graph.java** to get events from the user's calendar. +1. Add the following function to the `Graph` class in **Graph.java** to get events from the user's calendar. -```java -public static List getEvents(String accessToken) { - ensureGraphClient(accessToken); - - // Use QueryOption to specify the $orderby query parameter - final List