Call your Azure AD B2C protected API with authenticated HTTP requests from your JetBrains IDE
Automatically acquire OAuth 2.0 tokens
I have written several blog posts about HTTP clients in the past. I am a big fan of using HTTP text files versioned in a git repository alongside API code and executed by an IDE tooling. However, there was one use case where a GUI tool like Postman or a swagger page was more convenient: retrieving OAuth 2.0 users' tokens. Thanks to the latest OAuth 2.0 feature in JetBrains' IDE built-in HTTP client, this is no longer an issue.
Context
I am developing a web application composed of a Vue.js frontend and an ASP.NET Core backend (just describing my use case, technologies don't matter). The end users of this application are authenticated using Azure AD B2C, which is a customer identity access management solution like Auth0 or other competitors.
I often need to manually call the endpoints of the API to verify the code is working properly and that an endpoint is returning the expected result. HTTP files are a convenient way of writing and executing the HTTP requests. Once committed in the Git repository, they can easily be shared with other developers of the team who may not have worked on some endpoints and want to have proper examples with the query parameters and payloads.
As the API is protected by Azure AD B2C, I need to retrieve a valid access token and pass it to my requests.
Previous solutions
Passing a valid access token to my HTTP requests is something I was previously doing by:
signing in my frontend
grabbing the token in the web browser dev tools
copying the token to my HTTP environment variables (preferably the private environment file to avoid committing a secret in your repository)
That works but:
it's cumbersome
you have to do it each time your access token expires
Another solution is to use a tool that generates app-specific local JWTs and configure your local dev environment to authenticate with these tokens instead of using the Azure AD B2C configuration. In .NET, you can use the dotnet user-jwts
to do exactly that. It allows you to generate a JWT token with the scopes, roles, and claims you want. So it's a good solution to debug your API locally without having to bypass the authentication and authorization mechanisms.
However, it has some downsides:
the tokens are only valid in your local machine so it only works for your local environment
the Azure AD B2C authentication is replaced by this "local JWT authentication" so you are not testing your API in real conditions
With the new HTTP Client OAuth 2.0 feature
Starting version 2024.1, HTTP Client in the JetBrains IDEs (in my case Rider 2024.1) support automatically authenticating HTTP requests, provided that you properly configured it.
OAuth 2.0 authorization code flow with PKCE
The OAuth 2.0 flow involved in retrieving a valid access token to make requests to an Azure AD B2C protected API is the authorization code flow with PKCE. There are 2 steps in the OAuth 2.0 authorization code flow:
Get an authorization code
Exchange the authorization code for an access token
Step 1 involves the user entering their credentials in the login form (Azure AD B2C login form in this case). At first sight, it might appear not very suitable for using HTTP files but the JetBrains HTTP Client handled it by opening the login form in the IDE embedded browser.
For Azure AD B2C,
the authorize endpoint is
https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{policy}/oauth2/v2.0/authorize
the token endpoint is
https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{policy}/oauth2/v2.0/token
where:
tenant
is the name of the Azure AD B2C tenantclientId
is the application ID of the application registered in Azure AD the B2C tenantpolicy
is the name of the policy created in the Azure AD B2C tenant
{tenant}.b2clogin.com
part is replaced by the custom domain.If you want to better understand how this flow works, there is a nice diagram in Auth0 documentation.
Configuration in the JetBrains HTTP Client
To make the authorization code flow work in the HTTP Client, all I have to do is provide the configuration for the Azure AD B2C tenant in the HTTP environment file.
Here is an example of such configuration:
{
"apiUrl": "https://localhost:5001/api",
"Security": {
"Auth": {
"CIAM": {
"Type": "OAuth2",
"Grant Type": "Authorization Code",
"PKCE": true,
"Client ID": "3a53c90d-20c4-40e9-b440-4825b70374d7",
"Scope": "openid offline_access profile https://mytenant.onmicrosoft.com/security/user.read",
"Auth URL": "https://mytenant.b2clogin.com/mytenant.onmicrosoft.com/b2c_1_sign_in/oauth2/v2.0/authorize",
"Token URL": "https://mytenant.b2clogin.com/mytenant.onmicrosoft.com/b2c_1_sign_in/oauth2/v2.0/token",
"Redirect URL": "https://localhost:8080/oidc-callback",
"Acquire Automatically": true
}
}
}
}
You can check the JetBrains documentation to have more information about the HTTP Client support for OAuth 2.0 authorization.
Authenticated HTTP Requests in the HTTP file
Once the configuration is set, retrieving an access token can be done with a simple click in the configuration file.
The authentication process is logged so we can check the requests made and identify any mistakes made in the configuration.
Hopefully, we don't have to manually retrieve an access token each time we need to execute an HTTP request in an HTTP file of our IDE. We can just use the {{$auth.token()}}
variable in the Authorization header of our requests, like this:
GET {{apiUrl}}/products
Authorization: Bearer {{$auth.token("CIAM")}}
The IDE will handle the rest for us.
Wrapping up
The HTTP Client OAuth 2.0 feature in JetBrains IDEs has greatly simplified making authenticated HTTP requests to secure APIs. While this article focused on Azure AD B2C, the same principles apply to other Authorization Servers, with only the authorize and token endpoints differing.
I hope other IDEs will adopt this feature, using the same convention for the $auth.token()
variable and its configuration. The only drawback is for developers not using JetBrains IDEs, who will need to adjust requests containing the $auth.token()
variable to run them in their IDEs.