I have Global Filter as
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new AuthorizeAttribute());
}
My Signin Code is
[AllowAnonymous,HttpPost]
public async Task<ActionResult> Login(UsersViewModel usermodel)
{
if (ModelState.IsValid)
{
Mapper.CreateMap<UsersViewModel, Users>();
Users model = Mapper.Map<Users>(usermodel);
Users result = await UserManager.FindAsync(model.UserName, usermodel.Password);
if (result != null)
{
await SignInAsync(result, true);
return RedirectToAction("Success", "Home");
}
ModelState.AddModelError("Error", "Incorrect username and/or password");
}
return RedirectToAction("Index", "Home");
}
private async Task SignInAsync(Users user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
ClaimsIdentity identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ExternalCookie);
AuthenticationManager.SignIn(new AuthenticationProperties()
{
IsPersistent = isPersistent
}, identity);
}
Now when I am signing in then its redirecting to /Home/Success. Its alright I saw the cookies its also creating the authentication cookie. However when I am trying to access /Account/AddRole which isnt marked as allowannonymous. In spite I have already authenticated . Its still redirecting me to /Account/Login . Which shouldn't happen . However my Authentication Startup Class for Owin is :
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(() => new RemsContext());
app.CreatePerOwinContext<RemsContext>(() => new RemsContext());
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
CookieName = "Authcookie",
CookieSecure = CookieSecureOption.Always,
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}
Why its not authentication working inspite it sets the authentication cookie for Asp.net ? From Identity Framework . I m currently using Identityframework beta 2.0.1 . can someone guide why authorize in global isnt working as expected in spite I have already logged in and the authentication cookie has been set ?
Related
I'm using Azure AD Connection for integrated login in an ASP.Net website.
If I use the same code and same configurations in a brand new project, it works. When implementing in my main project, it has some unusual behaviour.
Startup.cs is the following:
public class startup
{
string clientId = System.Configuration.ConfigurationManager.AppSettings["ClientId"];
string redirectUri = System.Configuration.ConfigurationManager.AppSettings["RedirectUri"];
static string tenant = System.Configuration.ConfigurationManager.AppSettings["Tenant"];
string authority = String.Format(System.Globalization.CultureInfo.InvariantCulture, System.Configuration.ConfigurationManager.AppSettings["Authority"], tenant);
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.IdToken,
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed
}
}
);
}
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
context.HandleResponse();
context.Response.Redirect("/?errormessage=" + context.Exception.Message);
return Task.FromResult(0);
}
}
My c# code in login page:
protected void btnLogin_Click(object sender, EventArgs e)
{
SignIn();
}
public void SignIn()
{
if (!Request.IsAuthenticated)
{
HttpContext.Current.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "/MainAzure.aspx" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
else
Response.Redirect("MainAzure.aspx");
}
The issue is this:
When I Press the login button in my aspx page, it opens the Microsoft login page. When I select the account and enter the password, it redirects to my redirect page (MainAzure.aspx) but without the authentication properties. Request.IsAuthenticated returns false.
Any hints?
Try changing your implementation as below approach:
Startup.cs :
public class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
/*
* Configure the OWIN middleware
*/
public void ConfigureAuth(IAppBuilder app)
{
// Required for Azure webapps, as by default they force TLS 1.2 and this project attempts 1.0
..ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Generate the metadata address using the tenant and policy information
MetadataAddress = String.Format(Globals.WellKnownMetadata, Globals.Tenant, Globals.DefaultPolicy),
// These are standard OpenID Connect parameters, with values pulled from web.config
ClientId = Globals.ClientId,
RedirectUri = Globals.RedirectUri,
PostLogoutRedirectUri = Globals.RedirectUri,
// Specify the callbacks for each type of notifications
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed,
},
// Specify the claim type that specifies the Name property.
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
ValidateIssuer = false
},
// Specify the scope by appending all of the scopes requested into one string (separated by a blank space)
Scope = $"openid profile offline_access {Globals.ReadTasksScope} {Globals.WriteTasksScope}"
}
);
}
/*
* On each call to Azure AD B2C, check if a policy (e.g. the profile edit or password reset policy) has been specified in the OWIN context.
* If so, use that policy when making the call. Also, don't request a code (since it won't be needed).
*/
private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
var policy = notification.OwinContext.Get<string>("Policy");
if (!string.IsNullOrEmpty(policy) && !policy.Equals(Globals.DefaultPolicy))
{
notification.ProtocolMessage.Scope = OpenIdConnectScope.OpenId;
notification.ProtocolMessage.ResponseType = OpenIdConnectResponseType.IdToken;
notification.ProtocolMessage.IssuerAddress = notification.ProtocolMessage.IssuerAddress.ToLower().Replace(Globals.DefaultPolicy.ToLower(), policy.ToLower());
}
return Task.FromResult(0);
}
/*
* Catch any failures received by the authentication middleware and handle appropriately
*/
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
notification.HandleResponse();
// Handle the error code that Azure AD B2C throws when trying to reset a password from the login page
// because password reset is not supported by a "sign-up or sign-in policy"
if (notification.ProtocolMessage.ErrorDescription != null && notification.ProtocolMessage.ErrorDescription.Contains("AADB2C90118"))
{
// If the user clicked the reset password link, redirect to the reset password route
notification.Response.Redirect("/Account/ResetPassword");
}
else if (notification.Exception.Message == "access_denied")
{
notification.Response.Redirect("/");
}
else
{
notification.Response.Redirect("/Home/Error?message=" + notification.Exception.Message);
}
return Task.FromResult(0);
}
/*
* Callback function when an authorization code is received
*/
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification notification)
{
try
{
/*
The `MSALPerUserMemoryTokenCache` is created and hooked in the `UserTokenCache` used by `IConfidentialClientApplication`.
At this point, if you inspect `ClaimsPrinciple.Current` you will notice that the Identity is still unauthenticated and it has no claims,
but `MSALPerUserMemoryTokenCache` needs the claims to work properly. Because of this sync problem, we are using the constructor that
receives `ClaimsPrincipal` as argument and we are getting the claims from the object `AuthorizationCodeReceivedNotification context`.
This object contains the property `AuthenticationTicket.Identity`, which is a `ClaimsIdentity`, created from the token received from
Azure AD and has a full set of claims.
*/
IConfidentialClientApplication confidentialClient = MsalAppBuilder.BuildConfidentialClientApplication(new ClaimsPrincipal(notification.AuthenticationTicket.Identity));
// Upon successful sign in, get & cache a token using MSAL
AuthenticationResult result = await confidentialClient.AcquireTokenByAuthorizationCode(Globals.Scopes, notification.Code).ExecuteAsync();
}
catch (Exception ex)
{
throw new HttpResponseException(new HttpResponseMessage
{
StatusCode = HttpStatusCode.BadRequest,
ReasonPhrase = $"Unable to get authorization code {ex.Message}."
});
}
}
(For user authentication activities) AccountController.cs :
public class AccountController : Controller
{
/*
* Called when requesting to sign up or sign in
*/
public void SignUpSignIn(string redirectUrl)
{
redirectUrl = redirectUrl ?? "/";
// Use the default policy to process the sign up / sign in flow
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = redirectUrl });
return;
}
/*
* Called when requesting to edit a profile
*/
public void EditProfile()
{
if (Request.IsAuthenticated)
{
// Let the middleware know you are trying to use the edit profile policy (see OnRedirectToIdentityProvider in Startup.Auth.cs)
HttpContext.GetOwinContext().Set("Policy", Globals.EditProfilePolicyId);
// Set the page to redirect to after editing the profile
var authenticationProperties = new AuthenticationProperties { RedirectUri = "/" };
HttpContext.GetOwinContext().Authentication.Challenge(authenticationProperties);
return;
}
Response.Redirect("/");
}
/*
* Called when requesting to reset a password
*/
public void ResetPassword()
{
// Let the middleware know you are trying to use the reset password policy (see OnRedirectToIdentityProvider in Startup.Auth.cs)
HttpContext.GetOwinContext().Set("Policy", Globals.ResetPasswordPolicyId);
// Set the page to redirect to after changing passwords
var authenticationProperties = new AuthenticationProperties { RedirectUri = "/" };
HttpContext.GetOwinContext().Authentication.Challenge(authenticationProperties);
return;
}
/*
* Called when requesting to sign out
*/
public async Task SignOut()
{
// To sign out the user, you should issue an OpenIDConnect sign out request.
if (Request.IsAuthenticated)
{
await MsalAppBuilder.ClearUserTokenCache();
IEnumerable<AuthenticationDescription> authTypes = HttpContext.GetOwinContext().Authentication.GetAuthenticationTypes();
HttpContext.GetOwinContext().Authentication.SignOut(authTypes.Select(t => t.AuthenticationType).ToArray());
Request.GetOwinContext().Authentication.GetAuthenticationTypes();
}
}
}
For complete code, you can visit Azure AD B2C: Call an ASP.NET Web API from an ASP.NET Web App
I've set up a simple login page to login my user when he clicks the login button. The user gets assigned roles upon the login. To test if it works out I've done the following code for login:
[HttpPost]
[ActionName("Login")]
public ActionResult Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
string userName = model.Username;
string[] userRoles = new string[5];
userRoles[0] = "Administrator";
ClaimsIdentity identity = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userName));
// userRoles.ToList().ForEach((role) => identity.AddClaim(new Claim(ClaimTypes.Role, role)));
identity.AddClaim(new Claim(ClaimTypes.Role, userRoles[0]));
identity.AddClaim(new Claim(ClaimTypes.Name, userName));
AuthenticationManager.SignIn(identity);
return RedirectToAction("Success");
}
else
{
return View("Login",model);
}
}
And I've added a Authorize attribute to my MVC action, just to see if the user will really be able to access it after the login... Here's how I've done it:
[Authorize(Roles="Administrator")]
public ActionResult Register()
{
var model = new UserRegistrationViewModel();
var countries = Connection.ctx.Countries.OrderBy(x => x.CountryName).ToList();
model.Countries = new SelectList(countries, "CountryId", "CountryName");
return View(model);
}
But for some reason when I try to access like following:
mywebsite.com/user/register
It shows me:
HTTP Error 401.0 - Unauthorized
You do not have permission to view this directory or page.
What could it be ?
Edit:
Here is the snapshot of claims and identities after the user logs in:
And 2nd one:
Could you ensure that you have Cookie middleware? For example,
Startup.cs
[assembly: OwinStartup(typeof(YourApplicationName.Startup))]
namespace YourApplicationName
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
}
}
}
Using Cookie Middleware without ASP.NET Core Identity
I'd say you are getting the 401 because your user does not have the "Administrator" role. Check your user (identity) on the subsequent request(s), I am not sure that the Roles persist in the cookie - you may need to find a way to persist the roles yourself.
I am trying to create a login screen using owin on Asp MVC.
This is the code that is in the controller.
I have even tried hard coding the values but I still get a 401 when I try enter the dashboard after login.
[HttpPost]
public ActionResult Login(string username, string password)
{
int userId = 0;
string role = string.Empty;
if (new UserManager().IsValid(username, password, ref userId, ref role))
{
var ident = new ClaimsIdentity(
new[] {
// adding following 2 claim just for supporting default antiforgery provider
new Claim(ClaimTypes.NameIdentifier, userId.ToString()),
new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string"),
new Claim(ClaimTypes.Name,username),
// optionally you could add roles if any
new Claim(ClaimTypes.Role, role)
},
DefaultAuthenticationTypes.ApplicationCookie);
HttpContext.GetOwinContext().Authentication.SignIn(
new AuthenticationProperties { IsPersistent = false }, ident);
return RedirectToAction("Dashboard"); // auth succeed
}
// invalid username or password
ModelState.AddModelError("", "invalid username or password");
return View("Index", "_LayoutUnAuthorised");
}
[Authorize(Roles = "Default")]
public ActionResult Dashboard()
{
return View();
}
My startup file is empty am I missing something here
public class Startup
{
public void Configuration(IAppBuilder app)
{
}
}
Yes, you missed the Owin Cookie Configuration on Startup class:
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AuthenticationType = "ApplicationCookie",
LoginPath = new PathString("/Account/LogOn"),
});
}
Install Nuget Package Microsoft.Owin.Security.Cookies then you are good to go
I setup my own oauth authorization Server (NO Login via Facebook, Google and co). Unfortunately my server does not redirect to the login page after the Authorize-Endpoint is hit. Why doesn't the middleware redirect to my login page?
I followed some tutorials:
OWIN OAuth 2.0 Authorization Server
OAuth custom provider c# (Which is by the way the only code example i found for client login into custom oauth server. Thanks to #MatthiasRamp. All other stuff I read is about how to login with the social media Clients which is very frustrating)
MVC 5 application - implement OAuth Authorization code flow
This is what I made shortend on my oauth server side:
Startup.Auth.cs use active Cookie Authentication with Loginpath
Startup.Auth.cs use passive Cookie Authentication
Startup.Auth.cs use authorization Server with AuthorizeEndpointPath
SecurityController/Authorize calls Authentication.challenge() to change the status to 401
One point in the post from #Satish P he described:
Redirect the client to a login page
For this purpose I set the property CookieAuthenticationOptions.LoginPath which tells me
The LoginPath property informs the middleware that it should change an outgoing 401 Unauthorized status code into a 302 redirection onto the given login path.
This is my Startup.Auth.cs including LoginPath:
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Application",
AuthenticationMode = AuthenticationMode.Passive,
LoginPath = new PathString("/Security/Login"),
LogoutPath = new PathString("/Security/Logout")
});
// Enable the External Sign In Cookie.
app.SetDefaultSignInAsAuthenticationType("External");
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "External",
AuthenticationMode = AuthenticationMode.Passive,
CookieName = CookieAuthenticationDefaults.CookiePrefix + "External",
ExpireTimeSpan = TimeSpan.FromMinutes(5),
});
// The UseOAuthAuthorizationServer extension method is to setup the authorization server. The setup options are: [...]
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
AuthorizeEndpointPath = new PathString("/Security/Authorize"),
TokenEndpointPath = new PathString("/Token"),
ApplicationCanDisplayErrors = true,
#if DEBUG
AllowInsecureHttp = true,
#endif
// Authorization server provider which controls the lifecycle of Authorization Server
Provider = new ApplicationOAuthProvider(PublicClientId)
});
}
I created a class which derives from OAuthAuthorizationServerProvider.
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
// [Check username and pw here]
var oAuthIdentity = new ClaimsIdentity(new GenericIdentity(context.UserName, OAuthDefaults.AuthenticationType), context.Scope.Select(x => new Claim("urn:oauth:scope", x)));
var cookiesIdentity = new ClaimsIdentity(new GenericIdentity(context.UserName, CookieAuthenticationDefaults.AuthenticationType), context.Scope.Select(x => new Claim("urn:oauth:scope", x)));
AuthenticationProperties properties = CreateProperties(context.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
var grantType = context.Parameters.SingleOrDefault(p => p.Key == "grant_type").Value;
if (grantType != null)
{
if (grantType[0] == "authorization_code")
{
string clientId;
string clientSecret;
if (context.TryGetBasicCredentials(out clientId, out clientSecret) || context.TryGetFormCredentials(out clientId, out clientSecret))
{
if (clientId == Clients.ClientApp.Id && clientSecret == Clients.ClientApp.Secret)
{
context.Validated();
}
}
}
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == Clients.ClientApp.Id)
{
context.Validated(Clients.ClientApp.RedirectUrl);
}
return Task.FromResult<object>(null);
}
public static AuthenticationProperties CreateProperties(string userName)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "userName", userName }
};
return new AuthenticationProperties(data);
}
}
And this is the where the redirect should happen because challenge will change the response to unauthorized (401). Instead of the redirect to the login page it wants to return the authorize view.
public ActionResult Authorize()
{
if (Response.StatusCode != 200)
{
return View("AuthorizeError");
}
var authType = DefaultAuthenticationTypes.ApplicationCookie;
var authentication = HttpContext.GetOwinContext().Authentication;
var ticket = authentication.AuthenticateAsync(authType).Result;
var identity = ticket != null ? ticket.Identity : null;
if (identity == null)
{
authentication.Challenge(authType);
}
else
{
// login stuff
}
return View();
}
I know it's too late and you might not need this anymore but just in case if anyone else also faces this issue then you need to return HttpUnauthorizedResult(); after challenge and it will rediret you to login page.
e.g
if (identity == null)
{
authentication.Challenge(authType);
return new HttpUnauthorizedResult();
}
//login stuff
Just like in this question, I want to sign out another user via updating the Security Stamp. However it doesn't work when testing on my local machine. I suspect the problem might be with the order of commands I'm using to reset a user and persisting the different properties to the db.
That's my Startup.Auth
public partial class Startup
{
public static TimeSpan expireTimeSpan = TimeSpan.FromHours(24);
public static IDataProtectionProvider DataProtectionProvider { get; private set; }
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<ApplicationUserManager>());
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
ExpireTimeSpan = expireTimeSpan,
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
DataProtectionProvider = app.GetDataProtectionProvider();
}
}
And this is a controller method that allows changing another users email == username. On changing the email, the user is supposed to be logged out and not have a valid password anymore.
public async Task<IHttpActionResult> UpdateUser(string id, ApplicationUser newUser)
{
var user = await _userManager.FindByIdAsync(id);
if (user == null) ...
IdentityResult result;
user.name = newUser.name;
user.roles = newUser.roles;
// sign out user and invalidate password
if (user.email != newUser.email)
{
user.email = newUser.email;
user.PasswordHash = null;
result = await _userManager.UpdateSecurityStampAsync(user.Id);
if (!result.Succeeded) throw new Exception("Security Stamp not updated.");
await _account.SendPasswordEmail(user);
}
result = await _userManager.UpdateAsync(user);
if (!result.Succeeded)
return GetErrorResult(result);
return Ok();
}
I have tried persisting the user first, then generating a new SecurityStamp, but that didn't work either.
Any ideas what could be wrong?
Thanks!
Apparently you didn't read the answer in the question you linked to clearly.
// important to register UserManager creation delegate. Won't work without it
app.CreatePerOwinContext(UserManager.Create);
This is further explained here:
https://aspnetidentity.codeplex.com/workitem/2209
However, you should be aware of this bug:
ExpireTimeSpan ignored after regenerateIdentity / validateInterval duration in MVC Identity (2.0.1)
This will be fixed in the forthcoming Identity 2.2/Owin 3.0 release, but is not yet final.
https://aspnetidentity.codeplex.com/workitem/2347
Also see this blog article:
http://tech.trailmax.info/2014/08/cookieauthenticationprovider-and-user-session-invalidation-on-change-of-securitystamp/