I have a web API that I authorize against custom JWT like this:
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x =>
{
x.Events = new JwtBearerEvents
{
OnTokenValidated = context =>
{
var userService = context.HttpContext.RequestServices.GetRequiredService<IUserService>();
var userId = new Guid(context.Principal.Identity.Name);
var user = userService.GetById(userId);
if (user == null)
{
context.Fail("Unauthorized");
}
return Task.CompletedTask;
}
};
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["Jwt:Key"])),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true
};
});
This all works fine. But now I also want to access the Microsoft Graph API in order to retrieve calendar data from a user. So I figured I have to present the client with an OAuth dialogue, so I can retrieve the access token and make calls against the Microsoft Graph API.
I then registered an Microsoft application following the guide from https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/master/2-WebApp-graph-user/2-1-Call-MSGraph
Now I have troubles to combine those two authentication schemes. I followed the answer from Asp Core: Azure Ad Auth + custom JWT + custom Identity store
And I added [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
over all my "old" controllers. This works until I add the new authentication scheme
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
.AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();
like this
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x =>
{
x.Events = new JwtBearerEvents
{
OnTokenValidated = context =>
{
var userService = context.HttpContext.RequestServices.GetRequiredService<IUserService>();
var userId = new Guid(context.Principal.Identity.Name);
var user = userService.GetById(userId);
if (user == null)
{
context.Fail("Unauthorized");
}
return Task.CompletedTask;
}
};
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["Jwt:Key"])),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true
};
}).AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
.AddMicrosoftGraph(Configuration.GetSection("MicrosoftApi"))
.AddInMemoryTokenCaches();
I do not know how to incooperate the OpenId-part and the OAuth workflow is never called.
How can I integrate the additional Microsoft OAuth workflow from the github example on specific controllers without breaking my current authentication scheme? I know that the AuthenticationSchemes
-name is missing, but I don't know, where to add it.
Did I choose the right OAuth workflow in the first place (user calls API, API triggers Microsoft OAuth to get token, API then uses token to fetch calendar data, API processes calendar data and then API passes the processed calendar data to user): https://learn.microsoft.com/en-us/azure/active-directory/develop/sample-v2-code?
As you can see A web app that calls web APIs: Code configuration, it mentions the following information:
So far, what you have done seems to be right, and as per the documentation and the GitHub sample which you have provided, you have to add
GraphServiceClient
as one of the parameters in your target Controller and use it by assigning it's value to the controller's variable. The sample code of the controller will look like this: