How to redirect to login page in ABP? - authentication

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

Related

Asp.Netcore Web app with Azure AD b2c redirect url

Azure AD B2C is setup for authentication in Asp.netCore web app. The authentication process works perfectly. After authentication, the user needs to be redirected back the page where the login was initiated at.
the way the current flow happens:
This is the button <a class="btn btn-primary" asp-area="AzureADB2C" asp-controller="Account" asp-action="SignIn">Sign in</a>
PublicPage (has an sign-in/register button) on part of page that user need be authenticated to interact with -> signin button clicked -> redirected to Azure AD B2C -> user returned to IndexPage.
the way the needs to be:
PublicPage (has an sign-in/register button) on part of page that user need be authenticated to interact with -> signin button clicked -> redirected to Azure AD B2C -> user returned to PublicPage.
EDIT
#Jit_MSFT thanks for the suggestion, but I'm not exactly sure where to add those configurations.
services.AddAuthentication(AzureADB2CDefaults.AuthenticationScheme)
.AddAzureADB2C(options => {
Configuration.Bind("AzureAdB2C", options);
});
The settings above don't have those options. Also there are several page need to have that dynamic ability.
context.Properties.RedirectUri = "/xxxx;
this seems like i wold be locked into one page on the returnUrl
In your AccountController, please define the SignIn method as something like:
public async Task SignIn()
{
var redirectUri = ... // your redirect URI
await HttpContext.ChallengeAsync(AzureADB2CDefaults.AuthenticationScheme,
new AuthenticationProperties { RedirectUri = redirectUri });
}
You may also check other details and options in this answer
In addition, please check if, in Azure AD, you have to register your client app with a matching redirect URI (more details here) :)

Authentication for .NET Core Razor Pages application doesn't work for views without an "/Identity" route while using .AddIdentityServerJwt()

Using the .NET Core 3.1 framework, I'm trying to configure a web platform with the following setup:
A Razor Pages application, that acts as the landing page for the platform with features/pages such as advertising the platform, cookie consent, privacy policy, contacts, and the pages that come with Identity (e.g., login, register, manage account).
Authentication for the Razor Pages application is performed in the standard Identity way.
An Angular SPA, that is only accessible after the user is logged in.
OIDC configuration with Identity Server in order to add authentication and authorisation to the Angular SPA.
All of these three components (Razor Pages + Angular + Identity Server) are bundled into one single .NET Core web project. I have also scaffolded Identity so that I am able to customise the look and behaviour of the pages.
I was able to almost configure the application the way I want it, by basically mixing the code of the startup templates of the Razor Pages option (with user accounts stored locally) and the Angular template option (with user accounts stored locally) and with a bit of trial and error and investigation.
The current status of my application is:
The user logs in in the Razor Pages application.
The login is successful and the email is displayed on the navigation bar.
When we navigate to the SPA, my Angular app tries to silently login and is successful:
localhost:5001/Dashboard (Angular SPA home route)
If we navigate to a part of the Razor Pages application that does not have the /Identity route (which is only used for the pages that come with Identity) the cookies appear to no longer contain the right information and I have no session in those routes. This means that, for example, if I am using the SignInManager.IsSignedIn(User) to only display a navigation option to an Administration page that is protected with an options.Conventions.AuthorizePage($"/Administration"), if I am in a URL that has the Identity route, the navigation tab will be displayed, otherwise it will not be displayed:
localhost:5001/Identity/Account/Login
localhost:5001 (Razor Pages application home route)
However, even though the Administration navigation tab is being displayed when I am on a URL that has the /Identity route, if I click on it I will get a 401 unauthorised error, because the Administration page is not preceded by the /Identity route:
localhost:5001/Administration
I have managed to trace the problem to the the AddIdentityServerJwt(). Without this, the login for the Razor Pages application works as intended, but I am obviously unable to use authentication and authorisation with the Angular application afterwards.
I went to check the source code for that method and it turns out that it creates a new IdentityServerJwtPolicySchemeForwardSelector that forwards the JWT policy scheme to the DefaultIdentityUIPathPrefix which, as you might have guessed it, contains only the value "/Identity".
I have configured my Startup class in the following way:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services
.AddDbContext<ApplicationDbContext>(optionsBuilder =>
{
DatabaseProviderFactory
.CreateDatabaseProvider(configuration, optionsBuilder);
});
services
.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services
.AddIdentityServer()
.AddApiAuthorization<IdentityUser, ApplicationDbContext>();
services
.AddAuthentication()
.AddIdentityServerJwt();
services
.AddControllersWithViews();
services
.AddRazorPages()
.AddRazorPagesOptions(options =>
{
options.Conventions.AuthorizePage($"/Administration");
});
services
.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
services.AddTransient<IEmailSender, EmailSenderService>();
services.Configure<AuthMessageSenderOptions>(configuration);
services.AddTransient<IProfileService, ProfileService>();
}
public void Configure(IApplicationBuilder applicationBuilder, IWebHostEnvironment webHostEnvironment)
{
SeedData.SeedDatabase(applicationBuilder, configuration);
if (webHostEnvironment.IsDevelopment())
{
applicationBuilder.UseDeveloperExceptionPage();
applicationBuilder.UseDatabaseErrorPage();
}
else
{
applicationBuilder.UseExceptionHandler("/Error");
applicationBuilder.UseHsts();
}
applicationBuilder.UseHttpsRedirection();
applicationBuilder.UseStaticFiles();
applicationBuilder.UseCookiePolicy();
if (!webHostEnvironment.IsDevelopment())
{
applicationBuilder.UseSpaStaticFiles();
}
applicationBuilder.UseRouting();
applicationBuilder.UseAuthentication();
applicationBuilder.UseIdentityServer();
applicationBuilder.UseAuthorization();
applicationBuilder.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
applicationBuilder.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (webHostEnvironment.IsDevelopment())
{
if (bool.Parse(configuration["DevelopmentConfigurations:UseProxyToSpaDevelopmentServer"]))
{
spa.UseProxyToSpaDevelopmentServer(configuration["DevelopmentConfigurations:ProxyToSpaDevelopmentServerAddress"]);
}
else
{
spa.UseAngularCliServer(npmScript: configuration["DevelopmentConfigurations:AngularCliServerNpmScript"]);
}
}
});
}
How can I configure my application so that the session is available across my entire application and not just on URLs that have the "/Identity" route while maintaining both authentication and authorisation for the Razor Pages application and the Angular application?
I had the same problem and solved it by adding my own PolicyScheme that decides which type of authentication should be used based on the request path. All my razor pages have a path starting with "/Identity" or "/Server" and all other requests should use JWT.
I set this up in ConfigureServices using the collowing coding:
// Add authentication using JWT and add a policy scheme to decide which type of authentication should be used
services.AddAuthentication()
.AddIdentityServerJwt()
.AddPolicyScheme("ApplicationDefinedAuthentication", null, options =>
{
options.ForwardDefaultSelector = (context) =>
{
if (context.Request.Path.StartsWithSegments(new PathString("/Identity"), StringComparison.OrdinalIgnoreCase) ||
context.Request.Path.StartsWithSegments(new PathString("/Server"), StringComparison.OrdinalIgnoreCase))
return IdentityConstants.ApplicationScheme;
else
return IdentityServerJwtConstants.IdentityServerJwtBearerScheme;
};
});
// Use own policy scheme instead of default policy scheme that was set in method AddIdentityServerJwt
services.Configure<AuthenticationOptions>(options => options.DefaultScheme = "ApplicationDefinedAuthentication");

.net core 2 Google Authentication login loop

I am trying to authenticate using google in .net core 2 and have done what seems to be the very simple set up required for this:
1) Added app.UseAuthentication(); to Configure(..) in startup.cs
2) Added to ConfigureServices(..) startup.cs
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
})
.AddCookie()
.AddGoogle(options =>
{
options.ClientId = Configuration["auth:google:clientid"];
options.ClientSecret = Configuration["auth:google:clientsecret"];
});
Added the appropriate values to my appsettings.json for the id and secret that I got from Google Dev.
Added a web-api controller with an [Authorize] attribute.
Done the appropriate stuff in google dev to set the Authorised JavaScript origins to http://localhost:50741 (my root) and Authorised redirect URIs to http://localhost:50741/signin-google
Result
Going to the secured controller endpoint results in a redirect to the google webpage where I can choose a google profile, I choose one and it redirects back to http://localhost:50741/signin-google and then immediately back to the google profiles screen creating an infinite loop.
Where have I gone wrong?
It all works fine if I change the [Authorize] attribute to [Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]

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.

Login redirect on ASP.NET Core application with external login

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?
.