The problem is that you haven't set anything to indicate that the user is signed in.
You validate the credentials and redirect to the
returnUrl
, but that redirected request will be anonymous. The
Authorize
filter will see an anonymous request for a resource which requires authentication, and will redirect you back to the login page.
The simplest approach would probably be to store the authenticated username in the session, and use that in the
AuthenticateRequest
event to set the authenticated user for the request. For example:
Helper class:
public static class AuthenticationHelper
{
private const string SessionKey = "AuthenticationHelper.UserName";
public static void MarkAsAuthenticated(this HttpSessionStateBase session, string authenticatedUserName)
{
session[SessionKey] = authenticatedUserName;
}
public static IPrincipal GetAuthenticatedUser(this HttpSessionState session)
{
string authenticatedUserName = (string)session[SessionKey];
if (string.IsNullOrEmpty(authenticatedUserName)) return null;
return new GenericPrincipal(new GenericIdentity(authenticatedUserName), Array.Empty<string>());
}
}
Sign in action:
if (ctx.ValidateCredentials(model.UserName, model.Password))
{
Debug.Writeline("You are logged in");
Session.MarkAsAuthenticated(model.UserName);
return RedirectToLocal(returnUrl);
}
Global.asax.cs
:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
IPrincipal user = Session.GetAuthenticatedUser();
if (user != null) Context.User = user;
}
If you're using
[Authorize(Users = "...")]
, the usernames will need to match what the user entered when they signed in.
NB: It's usually preferable to use roles instead of users, so that you don't have to recompile your code to change who has access to what.
NB2: If everyone who signs in can access all actions, and you don't need to restrict specific actions to specific users, you can just use
[Authorize]
instead.
Edit: For ASP.NET Core, you'd need to use custom middleware instead of the
Global.asax
file.
Migrate HTTP handlers and modules to ASP.NET Core middleware | Microsoft Docs[
^]
You'll also need to update the helper to account for the new
ISession
interface.
Helper class:
public static class AuthenticationHelper
{
private const string SessionKey = "AuthenticationHelper.UserName";
public static void MarkAsAuthenticated(this Microsoft.AspNetCore.Http.ISession session, string authenticatedUserName)
{
session.SetString(SessionKey, authenticatedUserName);
}
public static ClaimsPrincipal GetAuthenticatedUser(this Microsoft.AspNetCore.Http.ISession session)
{
string authenticatedUserName = session.GetString(SessionKey);
if (string.IsNullOrEmpty(authenticatedUserName)) return null;
return new GenericPrincipal(new GenericIdentity(authenticatedUserName), Array.Empty<string>());
}
}
Middleware:
public class CustomAuthenticationMiddleware
{
private readonly RequestDelegate _next;
public CustomAuthenticationMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
ClaimsPrincipal user = context.Session.GetAuthenticatedUser();
if (user != null) context.User = user;
await _next(context);
}
}
public static class CustomAuthenticationMiddlewareExtensions
{
public static IApplicationBuilder UseCustomAuthentication(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CustomAuthenticationMiddleware>();
}
}
Startup:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
app.UseCustomAuthentication();
app.UseAuthorization();
...
}
Make sure you add the authentication / authorization lines after
UseRouting
, but before
UseEndpoints
.
You'll also need to call
AddSession
in the
ConfigureServices
method, and
UseSession
in the
Configure
method, as described in the documentation:
Session in ASP.NET Core | Microsoft Docs[
^]
The
UseSession
call will need to appear before the
UseCustomAuthentication
call.