Login redirect on ASP.NET Core application with external login - asp.net-core

I have an ASP.NET Core web application and I am decorating a few controller action methods with Authorize attribute.
So, when I am not logged in, it doesn't do any redirect and only shows me a blank page for that controller action. I have gone through a couple of tutorials and they talk about Cookie authentication.
So, I made changes in my Startup.cs and added the following:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookie",
LoginPath = new PathString("/Account/Login/"),
AccessDeniedPath = new PathString("/Account/Forbidden/"),
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
I also made a change in Authorize attribute to include ActiveAuthenticationScheme as:
[Authorize(ActiveAuthenticationSchemes = "Cookie")]
Now when I tried to go to that controller action, I get the login page. I am able to login successfully but I am again redirected to Login page instead of showing the controller action method View.
I can tell that I successfully logged in as I can see my email and a 'logoff' button on top of page (Layout with partial view). It seems like I am authenticated but Not Authorized. It that is true that I should have seen the forbidden page but I am seeing only the login page.
Am I missing something here? Why I am being redirected to Login page even after logging in?
.

Related

ServiceStack Authentication IsAuthenticated always false for users authenticated via facebook

Hi am trying to use OAuth authentication FacebookAuthProvider provided by servicestack
var AppSettings = appHost.AppSettings;
appHost.Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new CredentialsAuthProvider(AppSettings),
new FacebookAuthProvider(AppSettings),
new GoogleAuthProvider(AppSettings)
}));
I have implement custom AuthEvents and I have access to authorized user data
but after RedirectUrl happens in endpoint session is empty and IsAuthenticated property is false
In the same time from Controller I can see that created session successfully saved in Cache
This scenario occurred only if user doesn't login in facebook in the browser yet, but for user that already logged in facebook, authentication works fine
What am I doing wrong ?
Thanks in advance!
Updates:
Scenario for repsoducing:
ensure that you didn't loged in facebook in browser (open facebook.com and log out if need)
run web app (project which created with template mvcauth)
try to login via facebook
3.1. for first attempt you will be redirected to https://www.facebook.com/login.php? page for specify your facebook account credentials
after first attempt you will be returned to web app page /Account/Login#s=1 but User.Identity.IsAuthenticated = false in AccountController.Login and you still unauthorized.
after second attempt you will finally successfully logged in
At the same time after first attempt Session with user data will be created and saved in Cache, it looks like after redirecting from page 'https://www.facebook.com/login.php' а new session being created without auth data, but for second attempt we successfully login, maybe the core of issue is redirection from 'https://www.facebook.com/login.php'
In order to be able to use ServiceStack.Auth in MVC and vice-versa you'll need to register the NetCoreIdentityAuthProvider which provides integration between ServiceStack Authenticated Sessions and ASP.NET Core's Claims Principal, e.g:
public void Configure(IAppHost appHost)
{
var AppSettings = appHost.AppSettings;
appHost.Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new NetCoreIdentityAuthProvider(AppSettings), /* Use ServiceStack Auth in MVC */
new CredentialsAuthProvider(AppSettings), /* Sign In with Username / Password */
new FacebookAuthProvider(AppSettings),
new GoogleAuthProvider(AppSettings),
new MicrosoftGraphAuthProvider(AppSettings),
}));
appHost.Plugins.Add(new RegistrationFeature()); //Enable /register Service
//override the default registration validation with your own custom implementation
appHost.RegisterAs<CustomRegistrationValidator, IValidator<Register>>();
}

How to redirect to login page in ABP?

I download the .NET Core sample from ASP.NET Boilerplate website,
change the DB connection string, update DB,
run Web Api, show the Swagger successfully,
add a Home/Index View, change Home/Index action to return the View, not Swagger,
run again, show home page successfully,
then add a Home.Account Controller and Home.Account View, login page.
Add AbpMvcAuthentication attribute on Home/Index, what I want is when access Home, redirect to login page.
When I go to the Home, it shows an empty page, not Home, nor login page. It seems authenticate failed, but did not redirect to login page.
My question is: how to let AbpMvcAuthentication know which page to redirect when authenticate failed?
There is no login page for the *.Web.Host project, where the redirect to Swagger is.
You should change your Startup Project to *.Web.Mvc instead:
If you just want to login, consider using Swagger authentication helpers from the browser console:
abp.swagger.login();
To answer your question, you can re-enable authentication redirect by reverting commit 92b6270:
// What it was (and what you want)
services.AddAuthentication()
.AddJwtBearer(options =>
// What it is
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "JwtBearer";
options.DefaultChallengeScheme = "JwtBearer";
}).AddJwtBearer("JwtBearer", options =>
Startup.cs:
app.UseAuthentication();
app.UseJwtTokenMiddleware(); // Add this back
app.UseAbpRequestLocalization();
To be clear, this redirects to a blank page since there is no login page for the *.Web.Host project.
This is the opposite of the expected behaviour in ABP 401 response from API instead of redirect.
Also, you can configure login path for redirect:
You can configure that in Startup.cs:
IdentityRegistrar.Register(services);
AuthConfigurer.Configure(services, _appConfiguration);
// Add this line:
services.ConfigureApplicationCookie(options => options.LoginPath = "/Admin/Login");
Related docs: https://learn.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/identity-2x

Implement Different Login Page for each role in asp.net-core 2

I want to implement different login page for each user based in its role in asp net core . I can set login path but its static for any roles.
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "Account/Login/";
options.AccessDeniedPath = "Account/Forbidden/";
});
so when i call action that authorize(role="Admin") redirect to admin login page. and when call action that authorize(role="User") redirect to User login page
I add two different Authentication scheme in start up ConfigureServices like this
services.AddAuthentication(options =>
{
options.DefaultScheme = "UserAuth";
})
.AddCookie("UserAuth", options =>
{
options.LoginPath = "/Account/Login/";
options.AccessDeniedPath = "/Account/AccessDenied/";
})
.AddCookie("AdminAuth", options =>
{
options.LoginPath = "/Admin/Account/Login/";
options.AccessDeniedPath = "/Admin/Account/AccessDenied/";
});
When authorize with admin role controller i choose admin scheme
[Authorize(Roles = "Administrator", AuthenticationSchemes = "AdminAuth")]
When authorize with user role controller i choose user scheme
[Authorize(Roles = "User", AuthenticationSchemes = "UserAuth")]
You can review this link How do I setup multiple Authentication schemes in ASP.NET Core 2.0?
Sorry, not possible. The role of a user is not known until the user has authenticated. So you can't tell which login page to serve until they have already logged in, and they can't log in until you have served a login page, so the idea simply doesn't work.
The best you can do is offer a single login page that allows the user to select their role before signing on (e.g. with radio buttons, a dropdown list, or links that take the user to separate login pages). If you like, you can set a cookie to persist the user's selection so that they will only see the appropriate role-specific page the next time they sign on.
If you wish to redirect to a different login page based on some piece of data other than user context (e.g. if you want to redirect to different login pages depending on what URL the user was originally requesting) you can always write a custom authorize attribute and override the HandleUnauthorizedRequest method. Then you can redirect anywhere you want.

AutomaticChallenge not working for CookieAuthentication in ASP.NET MVC Core with Azure AD sample

I am slightly altering the code from the Integrating Azure AD into an ASP.NET Core web app sample. I can run the application successfully, and if I click the "Log in" link then I correctly get redirected to the Azure AD sign-in page.
However, I also would like for the application to automatically redirect me to the login page if I try to access a route that is protected with the Authorize attribute. I have added the [Authorize] attribute to the HomeController's Contact action, but if I try to access this without being logged in, I do not get redirected to the login page. If I access the contact page while logged in then it displays correctly.
I have updated the Startup.cs file as follows:
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
LoginPath = "/Account/Login"
});
But even with these changes, I don't get redirected to the login page. Is there something else I'm missing?
I discovered the problem. The sample references version 1.0.0 of the Microsoft.AspNetCore.Authentication.Cookies. I had upgraded the package to version 1.1.0, and hit the issue described here. Specifically, in version 1.1.0 of this package the AutomaticChallenge behaviour changed when using multiple authentication providers (which is what the sample does).
I was able to work around the change by updating the sample's Startup.cs file's Configure method as follows:
Change the line that calls app.UseCookieAuthentication to
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AutomaticChallenge = true
});
And in the call to app.UseOpenIdConnectAuthentication, add this line:
AutomaticChallenge = false

Customise Behaviour On Authorize Failed

I have successfully implemented group based authorization in an MVC application by using the [Authorize(Roles = "Admin")] tags in my controller.
However, the default behaviour when a user requests a page they are not authorized to view is to redirect them to the login page. This far from intuitive, and causes too much confusion amongst users who will repeatedly try to login.
Instead I would like to display a custom screen, or at the very least have a message display on the login screen stating that they are already logged in, but with insufficient privileges.
A User.Identity.IsAuthenticated tag exists, which can be used in the base logic, but there doesn't appear to be a similar IsAuthorised tag.
How can this behaviour be implemented?
I believe you have already partly solved your problem. This because because when authorization fails, the user will be redirected to login page. Verify if the user is authenticated before displaying the login view. If they are authenticated re-direct them to appropriate page instead.The code snippet below will not display login view if the user is authenticated and the cookie has not expired. They will be redirected to "DashboardOrSomeOtherView.cshtml" instead
[HttpGet]
public ActionResult Login(string returnUrl)
{
// Before showing login view check if user is authenticated.
// If they are redirect to suitable page,
// and print appropriate message
if (ControllerContext.HttpContext.User.Identity.IsAuthenticated)
{
// You can even use TempData as additionally, to hold data here and check in
//the view you redirect to if it is not null and is true ,
// then they are authenticated and show some message,
// e.g. You have been redirected here because you do not
// have authorization to view previous page.
TempData["UserAlreadyAuthicated"] = true;
return RedirectToAction("DashboardOrSomeOtherView");
}
// If they are not authenticated , show them login view
return View();
}
In the DashboardOrSomeOtherView
<div>
<h1>DashboardOrSomeOtherView</h1>
#{
if(TempData["UserAlreadyAuthicated"] != null && TempData["UserAlreadyAuthicated"].ToString().ToLower() == "true")
<div>You have been redirected here because of inadequate authorization</div>
}
</div>