I think my main question is which Authentication flow should I be using - On-Behalf-Of or Implicit Grant Flow? However my Authentication and Authorization set up in Startup.cs could be wrong and I am way off, so apologies if so.
I have an existing Angular SPA in Azure and a Net Framework API also running in Azure.
The web API has been ported to Net Core 3.1.
In our Azure directory, I have two registered apps. One for the API, and the other for the SPA. I have added the SPA client ID under authorized clients in the WEB API.
It is worth noting that the App services are actually running in a different directory (I think this if fine but just mentioning)
Here is the relevant Authentication/Authorization setup in Azure AD.
private void ConfigureAzureAD(IServiceCollection services, IMvcBuilder mvcBuilder)
{
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.TokenValidationParameters.ValidateIssuer = true;
});
mvcBuilder.AddMvcOptions(options =>
{
var policy = new AuthorizationPolicyBuilder().
RequireAuthenticatedUser().
Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).
SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseResponseCaching();
}
app.UseStaticFiles();
app.UseCors(CORS_POLICY);
if (UseAzureAD)
{
app.UseHttpsRedirection();
}
app.UseRouting();
app.UseCookiePolicy();
app.UseAuthorization();
app.UseAuthentication();
app.UseMvc();
}
I can successfully use the API directly and it authenticates.
The SPA is using Angular ADAL. Which if I read correctly will not work for Microsoft Identity V2 endpoints. - we need to upgrade to MSAL still, although I am still having trouble understanding the ultimate authentication flow (which I think is my main question).
Currently, if I hit the SPA, it will authenticate just using the current ADAL Config, but when it hits the API, and gets redirected to https://login.microsoftonline.com/{TenantId}/oauth2/v2.0/authorize? with and ID Token.
It hangs on the redirect and their is a CORS error because the origin is null. I know the is expected as the SPA is not configured correctly.
The API does make calls to MS Graph API to get groups for the user (but uses client ID and client secret to get an access token to call MS Graph), so I don't think this means I need to use the On-Behalf-Of-Flow (I did see this post Authenticating against Microsoft Graph with SPA and then using token in Web API)?
I have looked at this example https://github.com/Azure-Samples/ms-identity-javascript-angular-spa-aspnetcore-webapi for implicit grant flow.
I did notice in that example the authentication scheme is JwtBearerDefualts which makes sense as as ID Tokens are JWTs, so that makes me question my API startup config.
Here is this bit that calls MS Graph in the API which is probably needed to best answer which of the 2 flows I should use (again it gets a access token with client secret).
private async Task<IReadOnlyCollection<string>> LoadReportGroupsCurrentUserCanRead()
{
var objectID = claimsPrincipal
.Claims
.FirstOrDefault(c => c.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier")
?.Value;
var graphServiceClient = await GetGraphServiceClient();
var groups = await graphServiceClient
.Groups
.Request()
.GetAsync();
var memberGroups = await graphServiceClient
.Users[objectID]
.GetMemberGroups(true)
.Request()
.PostAsync();
return memberGroups.Select(mg => groups.SingleOrDefault(g => g.Id == mg))
.Where(g => g?.DisplayName?.EndsWith(" Report Group", StringComparison.InvariantCultureIgnoreCase) == true)
.Select(g => g.Description)
.Where(name => !string.IsNullOrWhiteSpace(name))
.ToArray();
}
private async Task<GraphServiceClient> GetGraphServiceClient()
{
string authority = new Uri(microsoftLogin, tenantID).AbsoluteUri;
var authenticationContext = new AuthenticationContext(authority);
var clientCredential = new ClientCredential(clientID, clientSecret);
var authenticationResult = await authenticationContext.AcquireTokenAsync("https://graph.microsoft.com", clientCredential);
var authProvider = new DelegateAuthenticationProvider(requestMessage =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", authenticationResult.AccessToken);
return Task.CompletedTask;
});
return new GraphServiceClient(authProvider);
}
Lastly, I think our SPA is using a version of Angular that does not support Angular MSAL. Can I just use Vanilla MSAL JS (Don't think we have time right now to upgrade Angular)?