I have a simple ASPNET Core Razor Pages app + ASPNET Core API:
for example the API just has:
builder.Services
.AddAuthentication(...)
.AddMicrosoftIdentityWebApi()
Razor pages has:
builder.Services
.AddMicrosoftIdentityWebApp(azureConfig)
.EnableTokenAcquisitionToCallDownstreamApi(...)
.AddInMemoryTokenCaches();
I have two app registrations to support that, one registration for my UI, and one registration for my API that exposes an API.
So I would like to roll a UI that my users can use to generate a client for their own uses. In my experience, when I see this feature in other apps I usually see they support client credentials only.
Imagine a UI like this:
Now imagine code behind like this when you click that "Create new client" button:
public void CreateClientForUser(CurrentUser currentUser)
{
var graphClient = new GraphServiceClient(requestAdapter);
var requestBody = new Application
{
DisplayName = "API Key for " + currentUser.DisplayName,
};
var result = await graphClient.Applications.PostAsync(requestBody);
dbContext.UserApps.Add(new UserApp
{
UserId = currentUser.Id,
AppId = result.AppId
});
}
Finally, imagine in my API I had a OnTokenValidated event handler like this:
// Untested psudocode
services.Configure<JwtBearerOptions>(
name: JwtBearerDefaults.AuthenticationScheme,
configureOptions: jwtBearerOptions =>
{
var existingOnTokenValidatedHandler = jwtBearerOptions.Events.OnTokenValidated;
jwtBearerOptions.Events.OnTokenValidated = async tokenValidatedContext =>
{
await existingOnTokenValidatedHandler(tokenValidatedContext);
var context = tokenValidatedContext.HttpContext.RequestServices.GetRequiredService<BOIDbContext>();
var principal = tokenValidatedContext.Principal!;
var claimsIdentity = (ClaimsIdentity)principal.Identity!;
var objectidentifierClaimType = "http://schemas.microsoft.com/identity/claims/objectidentifier";
var objectIdentifier = principal.Claims.FirstOrDefault(t => t.Type == objectidentifierClaimType);
var appId = principal.Claims.FirstOrDefault(t => t.Type == "appId");
if (appId != "Main RazorPages Web UI")
{
// This must be a users access token?
var usersApp = context.UserApps.FirstOrDefault(x => x.AppId == appId);
if(usersApp is null)
{
context.Fail($"No app id found in app-user mapping table.");
return;
}
// Set the user id! Done!
claimsIdentity.AddClaim(new Claim(ApplicationClaimTypes.UserId, usersApp.UserId.ToString()));
}
};
});
Is this approach OK? On most sites that offer API access, they usually have a UI that lets me generate client credentials clients. Should I be only allowing users to create an interactive client? If I did that, the access tokens for my API would not require me to maintain a user mapping table correct?