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!






