So my .NET Core WebApplication is logging in an user via Azure AD and I have a database with users and their roles.
I created already OIDC MiddleWare to add Claims from my database for the user that tries to log in.
So the flow is:
So my question: Is there a way how I could deny the authentication for the user, when he is not available in the DB or blocked?
What I did now is that I set a Claim and if that claim is not available it will be redirected to Access Denied page (by using the AuthorizationPolicy), but I want that the user will be redirected to the login page from Microsoft/AD (in best case with a message).
Is that possible somehow and if so, how ?
This is my code now:
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => { Configuration.Bind("AzureAd", options); }
);
services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.RequireAssertion(context =>
{
Claim claim = context.User.Claims.First(claim => claim.Type == ClaimTypes.Expired);
return context.User.HasClaim(x => x.Type == ClaimTypes.Expired) &&
context.User.Claims.First(claim => claim.Type == ClaimTypes.Expired).Value
.Equals("false", StringComparison.OrdinalIgnoreCase);
})
.RequireClaim(ClaimTypes.Name)
.RequireClaim(ClaimTypes.Role)
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
// OIDC Middleware, to access the User's Claims while logging in through AzureAD
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Events = new OpenIdConnectEvents
{
OnRemoteFailure = ctx =>
{
ctx.Response.Redirect("/");
ctx.HandleResponse();
return Task.CompletedTask;
},
OnSignedOutCallbackRedirect = ctx =>
{
ctx.Response.Redirect("/");
ctx.HandleResponse();
return Task.CompletedTask;
},
OnTokenValidated = ctx =>
{
// Get the user's email
var email = ctx.Principal.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;
// Query the database to get the role
using (var db = ctx.HttpContext.RequestServices.GetRequiredService<TracingContext>())
{
// Get the Users from the database, with the logged in email address (from Azure)
User user = db.Users.FirstOrDefault(u => u.UPN.Equals(email));
if (user != null)
{
user.LastLogin = DateTime.Now;
db.SaveChanges();
// Add claims
var claims = new List<Claim>
{
new Claim(ClaimTypes.Role, user.Role.ToString()),
new Claim(ClaimTypes.Expired, (!user.IsActivated || user.IsBlocked).ToString())
};
// Save the claim
var appIdentity = new ClaimsIdentity(claims);
ctx.Principal.AddIdentity(appIdentity);
}
else
{
**// Send back to Login Page (with error message, maybe?)**
}
}
return Task.CompletedTask;
},
};
});
You can override the default processing and handle the response yourself:
// Send back to Login Page (with error message, maybe?)
ctx.HandleResponse();
ctx.Response.Redirect("/path/to/login");
The call to HandleResponse
signals that we want to handle the response ourselves and the following call sets up the redirect. There are a few approaches to sending over an error message. One approach is to provide a query-string parameter to the login URL.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With