Micronaut — Login with Microsoft

Rafael Gutierrez
11 min readFeb 4, 2021

At work, I am creating a simple application for internal usage and as with almost all kind of applications I need to authenticate the users. Originally I implemented the classic “Login with Google” following this tutorial: https://guides.micronaut.io/micronaut-oauth2-oidc-google/guide/index.html. Now I also need to implement a “Login with Microsoft”.

Fortunately, Microsoft already offers OAuth 2.0 or OpenId Connect services to use as an identity platform for different types of applications.

This tutorial is to show you how I implemented the “Login with Microsoft”.

Let's start.

Generate the project

Let’s start by creating the project. For this I’m going to the Micronaut CLI tool.

NOTE: I recommend to you the usage of sdkman to install the micronaut CLI tool.

To generate the basic project you can use:

$ mn create-app <app-package-name>.<folder-name/app-name> --build=gradle --java-version=11

Yes!, we are going to use Java 11, so be sure to have that version installed. Tip, you can also use SDKMAN to install Java 11.

As an example, I used the following command:

mn create-app com.jabaddon.micronaut.tutorials.loginwithmicrosoft.micronaut-loginwithmicrosoft --build=gradle --java-version=11

Where com.jabaddon.micronaut.tutorials.loginwithmicrosoft is the application package and micronaut-loginwithmicrosoft is the folder name and also the rootProject.name in gradle.

In your terminal go inside the project folder and verify the basic project is working running:

gradle run

Then you will see in your console:

23:03:21.134 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 858ms. Server Running: http://localhost:8080

Dependencies

To avoid any kind of issue let's specify the dependencies we are going to need since the beginning.

Add the following into the build.gradle file in the dependencies section:

annotationProcessor("io.micronaut.security:micronaut-security-annotations")
implementation("io.micronaut.security:micronaut-security-oauth2")
implementation("io.micronaut.security:micronaut-security-jwt")
implementation("io.micronaut.views:micronaut-views-thymeleaf")

As you can see we are going to use the Micronaut security dependencies using OAuth 2.0 and JWT and Thymeleaf for the HTML views.

Basic entry point — Home page

Just as the Micronaut tutorials that you can find online easily, we are going to start creating a Home Controller that will display a home page where we are going to show a link to log in the user when the user is using the page anonymously or show basic information about the logged user when she has been authenticated.

NOTE: For my pet projects I normally use package that starts with com.jabaddon. You don't have to use the same package, use the package you want but use it consistently across all java files we are going to create.

Let’s code the HomeController class in file src/main/java/com/jabaddon/tutorials/micronaut/loginwithmicrosoft/HomeController.java which will render the home page.

So, the HomeController is the entry point for this little web application. It will render the Thymeleaft view named “home” and it is accessible for anonymous users.

Now, let’s code the home view. Create file src/main/resources/views/home.html and type the following:

This page will render different messages depending if the user is authenticated or not. When the user is not authenticated it will provide a link to log in to the application or when the user is authenticated it will provide a logout link. The view knows if the user is authenticated or not because when authenticated Micronaut injects a security object into the model that the view can access with the name security . For more information, see https://micronaut-projects.github.io/micronaut-views/latest/guide/#views-security.

If you run the application you will see a page like this:

Home Page when anonymous user.

The link “Login with Microsoft” is still not functional.

User Details Mapper

The Microunaut Security documentation here describes the Authorization Code Grant flow for OAuth 2.0 and how Micronaut implements that.

In that flow they are 3 key players:

  • The Browser.
  • You Micronaut Application.
  • The OAuth 2.0 Provider, in this case, Microsoft identity platform.

You can look at the image they used in the documentation to understand the flow but I will try to explain it also here in terms of how this web application is using it:

  1. When a users opens the application in a web browser the HomeController shows the Home page.
  2. The user then clicks on “Login with Microsoft”. That link is basically a GET to /oauth/login/{provider} where {provider} in our case is microsoft .
  3. The Micronaut Security framework then calls the Authorization endpoint. This Authorization Endpoint shows the user a page from the OAuth provider where the user have to provide her credentials.
  4. Once the user provide her credentials and the OAuth provider validates them, then the OAuth provider calls back our application sending a GET or POST to /oauth/callback/{provider} , again, where {provider} is microsoft . When calling the “callback” URL the OAuth provider includes a “code” that is used for the Token Endpoint.
  5. The Micronaut Security framework then calls the Token endpoint sending the code returned by the OAuth provider when it called back to our application. The Token endpoint then returns the “authorization token”.
  6. The Micronaut Security framework then calls the “User Details Mapper”, sending the “authorization token” returned by the OAuth provider as a parameter. The “User Details Mapper” is a component that we have to implement and then make a good usage of the “authorization token” to get the user information we need for our application.

Ok, we need to implement the “User Details Mapper”, but before that we need to implement the service client to get the User Information.

We are going to use the Microsoft Graph API. The Microsfot Graph API is a a RESTful web API that enables you to access a variety of Microsoft cloud services. Once our application gets the authorization token we can use that token to make request, on behalf of the user, to the Microsfot Graph API.

The service we are going to use to get the user information is the service to get the profile of the user:

https://graph.microsoft.com/v1.0/me

This service returns information about the user in the following format:

{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
"displayName": null,
"surname": null,
"givenName": null,
"id": null,
"userPrincipalName": null,
"businessPhones": [],
"jobTitle": null,
"mail": null,
"mobilePhone": null,
"officeLocation": null,
"preferredLanguage": null
}

You can explore the Microsoft Graph API using this link: https://developer.microsoft.com/en-us/graph/graph-explorer.

Ok, let’s code our simple service client to get the user profile information. First let’s code the class where we are going to store the response, let’s call this class MicrosoftUser. Create file src/main/java/com/jabaddon/tutorials/micronaut/loginwithmicrosoft/MicrosoftUser.java and type the following:

Now, let’s code the class MicrosoftGraphApiClient where we are going to call the endpoint to get information about the User’s profile. Create file src/main/java/com/jabaddon/tutorials/micronaut/loginwithmicrosoft/MicrosoftUser.java and type the following:

With the MicrosoftGraphApiClient in place now we are ready to code the “User Details Mapper”. Create file src/main/java/com/jabaddon/tutorials/micronaut/loginwithmicrosoft/UserDetailsMapper.java and type the following:

Notice how the UserDetailsMapper class is using @Named("microsoft") annotation. This is very important since the oauth provider name that we are using is microsoft . This is the provider name that is used in the URL to start the OAuth authentication flow (/oauth/login/microsoft ) also in the callback URL ( /oath/callback/microsoft). We also use the provider name microsoft in the configuration (next section).

The “User Details Mapper”, after a successful identification of the user, receives in the method createAuthenticationResponse the authorization token in the TokenResponse object. In this implementation, we used that token to get the user profile information from the Microsfot Graph API.

Security Configuration

The following gist is the template for the security configuration we are going to use. Right now, some of the important values are missing and some of them will depend on your setup.

Microsoft identity platform endpoints are what we are going to use to login our user using their Microsoft account. They are based on OAuth 2.0 and OpendID Connect protocols.

In this documentation https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-app-types you will see that Microsoft has 2 endpoints setup for us to start the authorization process and to get the OAuth token:

https://login.microsoftonline.com/common/oauth2/v2.0/authorize
https://login.microsoftonline.com/common/oauth2/v2.0/token

these are the values that we need to set up in our following configurations:

micronaut.security.oauth2.clients.microsoft.authorization.url
micronaut.security.oauth2.clients.microsoft.token.url

If you want to understand more how these 2 endpoints are used in the OAuth flow in Micronuat you can read: https://micronaut-projects.github.io/micronaut-security/latest/guide/#oauth. Also, here you can learn more about the basics of OAuth 2.0 and OpenID Connect protocols in the Microsoft identity platform: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols.

Register the Application

In order to use the Microsoft identity platform with our application, we need to register our application in the Azure portal. Once the application is registered you will get the values for client-id and client-secret configurations.

One thing that I didn't like when registering the application with the Microsoft identity platform is that you need to create a “free” account in Azure portal but you need specify a Credit Card. With this “free” account you have access to several services within the next 12 months and they say you won’t be charged until you actually choose to upgrade your account. Hopefully that is right.

Once you have your Azure portal account go to https://portal.azure.com/#home and find the Azure Active Directory service.

Then in the menu at the left find the option “App Registrations” and register your app.

For “Supported account types” I chose accounts in any organization and personal accounts.

In the Redirect URI, it says it is optional, but here we have to specify the Web redirect URL with the following value:

http://localhost:8080/oauth/callback/microsoft

This is basically the URL that the Microsoft identity platform will callback once the user is successfully authenticated.

Clink on the “Register” button to save the form.

Client ID

Once you save the form when registering an application, it will take you to the “Overview” page where you can see the following:

From that “Overview” page we need to get the value of “Application (client) ID” because this our client-id in our security configuration:

micronaut.security.oauth2.clients.microsoft.client-id

Client Secret

To create the client secret, we need to go to “Certificates & secrets” option under “Manage”:

In that page, you can create the client secret we will need for our application:

This is important, after creating this new secret you have to copy the secret value before moving away from this page because next time you visit this page the secret value will be masked.

If for some reason you forgot to copy the value, don’t worry you can always delete the client secret and create a new one.

So please, copy the secret value immediately and use that value for our security configuration:

micronaut.security.oauth2.clients.microsoft.secret-client

Scopes

When getting the “authorization token” from the OAuth provider we need to specify the list of “permissions” that the token should have so we can use it to call different services according to our needs.

In OAuth 2.0, this kind of permissions are called “scopes”. Generally, these scopes are represented as string values. As an example, for the Microsfot Graph API, these are some of the “scopes” we can use:

  • To be able to read the user’s calendar: Calendars.Read
  • To be able to send mails as a user: Mail.Send
  • To read the user profile: User.Read

Scope User.Read is the one we are going to use in our security configuration:

micronaut.security.oauth2.clients.microsoft.scopes

In fact, the permission User.Read is added automatically when we register our application. You can verify this by going to “API Permissions”:

And the looking at the “Configured Permissions”:

As you can imagine, here is where you can add more permissions to you application, like be able to access other kind of APIs from Microsoft. Just remember to grab the scope name and add it to the list of scopes in the security configuration of your application.

Application.yml — Final look

So, this should be the final look of your application.yml file in src/main/resources/application.yml. after you have all the values needed from the registration of the application the Azure platform:

Don’t forget to change the value:

micronaut.security.token.jwt.signatures.generator.secret

since pleaseChangeThisSecretForANewOne is not an strong secret.

Run the application

Now that we have all the code and configuration setup, just like when we validated that the generated project was working, in your terminal go inside the project folder and verify project is working running:

gradle run

Then you will see in your console:

23:03:21.134 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 858ms. Server Running: http://localhost:8080

then you application should be ready to login users with their Microsoft accounts.

Conclusion

I hope this tutorial can help you. I decided to try and write this tutorial since it was hard to find something useful for Micronaut applications.

Microsoft documentions is good but you need to have patience to read it. I will leave some of the links I found useful when writing this tutorial in the Resources section.

Please comment any kind of suggestion or you have. Thank you.

UPDATE (30.3.2021):

If you deploy your application using Heroku since Heroku is somehow routing and forwarding to your application, probably you must set up configuration: micronaut.server.host-resolution in your application.yml file like the following code:

micronaut:
server:
host-resolution:
protocol-header: 'X-Forwarded-Proto'
host-header: 'Host'

This is because Micronaut when calling the OAuth provider it builds and sends the callback URL in the redirect_uri parameter. By default, Micronaut looks at the header Forwarded trying to get the scheme, host, and port; and if Forwarded is not there, it tries with X-Forwarded-Host , X-Forwarded-Proto , and X-Forwarded-Port , but the problem I faced is that in my case Heroku was not sending the header Forwarded but also not sending the header X-Forwarded-Host so I had to force Micronaut to use the header Host to get the hostname and the header X-Forwared-Proto to get the protocol (http/https).

Resources

--

--