I have an Angular SPA and an ASP.NET Core Web API backend. I have an Azure application registration which the SPA is configured to use, and I can make Graph API calls from the SPA directly using @azure/msal-angular
and @microsoft/microsoft-graph-client
.
What I want to do now is have the SPA call the Web API, and for the Web API to make the Graph API calls on behalf of the logged in user.
I've waded through reams of documentation but I've not been able to find a complete tutorial for this scenario or find a definitive answer to a key question.
Which is, do I need a separate app registration for the SPA and the ASP.NET Core Web API, or do I need a single application registration with two platforms configured?
UPDATE
With the help of the responses in this thread I have this working.
One difference from the configuration proposed by @Rukmini is that in the configuration of the SPA app registration I do not have the web api added to API Permissions (it is not listed under My APIs
). Instead in the configuration of the web API app registration, under Expose an API
-> Authorized client applications
I have the SPA app registration selected there. (I don't know what the difference is between these two approaches but it is working).
In the ASP web api my appsettings.json
is trivial
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"TenantId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"ClientSecret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"Scopes": "Api.Access"
}
No section is required to configure the Graph API, the defaults work.
Program.cs
is also straightforward
// Using Microsoft.Identity.Web.MicrosoftGraph 3.5.0
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddMicrosoftGraph()
.AddDistributedTokenCaches();
Default configuration all works.
Controller is as follows.
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class GraphTestController : ControllerBase
{
public readonly GraphServiceClient _graphClient;
public GraphTestController(GraphServiceClient graphClient)
{
_graphClient = graphClient;
}
[HttpGet("invite-user")]
public async Task<ActionResult> InviteUser(string email, string displayName)
{
var invite = new Invitation()
{
InvitedUserDisplayName = displayName,
InvitedUserEmailAddress = email,
InviteRedirectUrl = "https://www.google.com",
SendInvitationMessage = true
};
Invitation response = await _graphClient.Invitations.PostAsync(invite);
return Ok(response);
}
}
To send invitations the User.Invite.All
delegated scope is added to both SPA and Web API app registrations, and the user must also have sufficient permissions.
Thanks everyone for the help!
I agree with @Jack A, You need two separate app registrations in Microsoft Entra ID, one for the Angular SPA (client) and one for the ASP.NET Core Web API (backend).
User Authentication in SPA -> SPA Calls the Web API -> Web API Validates the Token -> Web API Calls Graph API
Create a ASP.NET Core Web API application and expose an API add scope:
And add Microsoft Graph API permissions in ASP.NET Core Web API application:
In Angular SPA Microsoft Entra ID application, grant API permission of the ASP.NET Core Web API:
For sample, I generated access token using Angular SPA:
https://login.microsoftonline.com/tenantId/oauth2/v2.0/token
grant_type:authorization_code
client_id:ClientSPAappId
scope: api://xxxxxxxx/webapi.access
code: xxx
code_verifier:S256
redirect_uri: https://jwt.ms
The above generated access token can be used to call web API.
Now generate on-behalf-of flow access token to call Microsoft Graph:
https://login.microsoftonline.com/tenantID/oauth2/v2.0/token
client_id:WebAPIappID
client_secret:WebAPIappSecret
scope: https://graph.microsoft.com/.default
grant_type: urn:ietf:params:oauth:grant-type:jwt-bearer
assertion:<paste_token_from_above_step>
requested_token_use:on_behalf_of
You can use this access token to call Microsoft Graph API:
https://graph.microsoft.com/v1.0/users
For sample, I generated tokens via Rest API this can be done via code too.
Reference:
Microsoft identity platform and OAuth2.0 On-Behalf-Of flow - | Microsoft