I have created a custom authorisation class to validate the user token. This is web api 2.
Problem is, custom authorisation validate the token but does not execute the method in the controller after. It should execute the user method in the controller after validate the token. I have debug the code and I can see the authorisation token get validated properly but not executing the method and simply return 200.
Can anyone help ? (I am new to this)
custom authorisation class code:
public class CustomAuthorize : System.Web.Http.AuthorizeAttribute
{
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
base.OnAuthorization(actionContext);
if (actionContext.Request.Headers.Authorization.Parameter != null)
{
string authenticationToken = Convert.ToString(actionContext.Request.Headers.Authorization.Parameter);
PartnerUserProfile user = new PartnerUserProfile();
user = user.validate_token(authenticationToken);
if (user.recordref > 0) //above user has some content and matches the token from validate_token method. it wil be blank if not
{
return;
}
else
{
HttpContext.Current.Response.AddHeader("Bearer", authenticationToken);
HttpContext.Current.Response.AddHeader("AuthenticationStatus", "NotAuthorized");
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden);
return;
}
}
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.ExpectationFailed);
actionContext.Response.ReasonPhrase = "Please provide valid inputs";
return;
}
}
and my controller is below this will never get executed.
[HttpPost]
[CustomAuthorize]
public IHttpActionResult user(PartnerUserProfile user) //setUser
{
ReturnData rd = user.setPartnerUserProfile();
if (rd.status == 0)
{
return BadRequest("Invalid");
}
return Ok(rd);
}
When you assign a value to Response, it short circuits and returns right away. The controller logic will only execute if you do not short-circuit (a response is set in an Filter).
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.1#cancellation-and-short-circuiting
Related
I have an ActionFilter name of Log that it log user ip and other details when user login to the website so for do this work I write following code:
public class Log : ActionFilterAttribute
{
public IAppUserManager UserManager { get; set; }
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
var status = filterContext.Controller.TempData.Any(pair => pair.Key == "status" && (int)pair.Value == 200);
if (filterContext.HttpContext.User != null && filterContext.HttpContext.User.Identity.IsAuthenticated && status)
{
var logIp = new AddIpAddressDto()
{
Browser = filterContext.HttpContext.Request.GetBrowser(),
Ip = filterContext.HttpContext.Request.GetIp(),
Os = filterContext.HttpContext.Request.UserAgent.GetOs(),
UrlReferrer = filterContext.HttpContext.Request.UrlReferrer?.ToString(),
UserId = Guid.Parse(filterContext.HttpContext.User.Identity.GetUserId()),
UserName = filterContext.HttpContext.User.Identity.GetUserName(),
};
UserManager.Log(logIp);
}
base.OnResultExecuted(filterContext);
}
}
this code work when that filterContext.HttpContext.User.Identity.IsAuthenticated is ture.
The Log Filter declare on Login Action:
[AllowAnonymous]
[Route("sign-in", Name = "signInRoute")]
[HttpPost, ValidateAntiForgeryToken]
[Log]
public virtual async Task<ActionResult> Login(LoginDto login, string returnTo)
{
var signInStatus = await _signInManager
.PasswordSignInAsync(user.UserName, login.Password, login.RememberMe, true)
.ConfigureAwait(false);
switch (signInStatus) // is Success
{
case SignInStatus.Success:
TempData["status"] = 200;
return RedirectToLocal(returnTo);
case SignInStatus.LockedOut:
// todo return time of louckout
break;
case SignInStatus.RequiresVerification:
return RedirectToAction("ConfirmEmail");
case SignInStatus.Failure:
return View(CleanPassWordInLogin(login));
default:
throw new ArgumentOutOfRangeException();
}
}
Login Action Works fine and signInStatus is Success but after excuted Action IsAuthenticated is false.
To solve this issue I've tried the following items:
Used HttpContext.Current.GetOwinContext();
Defined following code in IoC (StructureMap 4.5.2)
config.For<HttpContextBase>().Use(() => new HttpContextWrapper(HttpContext.Current));
Tried OnActionExecuted,OnActionExecuting,OnResultExecuting
Used IAuthenticationManager in Identity 2.0
How Can I Solve this issue?
After the execution of SignInManager.PasswordSignInAsync, the authentication cookie will be created which includes the user info. So User.Identity info will be filled by the claims from the authentication cookie, which are not parsed yet (this cookie will be parsed in the second request to the server, not in the same login request). That's why you can't use User.Identity just after PasswordSignInAsync. At this specific point, you have only one option to find the userId:
string userId = UserManager.FindByName(model.Email)?.Id;
I need to create a authentication for my MVC Application and WebAPI.
I have the user credential details & role information in a separate table in database. Can anyone suggest which model i can use to achieve this.
Thanks
Which Web Api are you using if it is 2 than try below code, and let me know if i could help you more, because i had same scenario like you have
you have to create a custom authorization filter and call it above ActionMethod,
Create a different class in your project and change build mode in Compile
public class BasicAuthenticationAttribute : AuthorizationFilterAttribute
{
public static bool VaidateUserRoleWise(string username, string password, int RoleId)
{
//DO DATABASE CONNECTION DO QUERY HERE
if (Username == username && Password == password)
{
return true;
}
else
{
return false;
}
}
public override void OnAuthorization(QuizzrApi.Controllers.QuizzrController.InputParamAdminLogin LoginDetails)
{
System.Web.Http.Controllers.HttpActionContext actionContext = null;
if (LoginDetails == null)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
else
{
//Bellow is the static method called above will return true or false if user matches
if (!VaidateUserRoleWise(LoginDetails.UserName, LoginDetails.Password, 1))
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
base.OnAuthorization(actionContext);
}
}
In controller :
[Route("AuthorizeSystemAdmin")]
[HttpPost]
[BasicAuthentication]
public HttpResponseMessage Login([FromBody] InputParamAdminLogin AdminLoginInput)
{
//do your logic here
}
I have an action in my controller design for users already register in my system:
[Authorize]
public ActionResult getUserData()
{
string UserId = User.Identity.GetUserId();
return getDataFromDB(UserID);
}
And is working OK. Also redirect to the LOGIN page if you arent authenticated yet.
But I also want to have a dummy function getFakeData() for anonymous users visiting the page so they can see the like a demo of the page.
Where should I put the validation to see if user is authenticated or not and change the behaviour?
On the webpage I can set the dataUrl based if user is authenticated. I can make a separated function without [Authorize] tag
#if (Request.IsAuthenticated)
{
dataUrl = '=/Project/Controller/getUserData'
} else {
dataUrl = '=/Project/Controller/getFakeData'
}
Or can I do it on the same action controller and checking if user is authenticated or not? But not sure is that is possible.
Or is a better way to do it?
There are various ways to do this but here is my recommendation
// For ASP.Net MVC 5 simply inherit from AuthorizationAttribute and override the methods.
public class AccessControlAttribute : Attribute, IAuthorizationFilter
{
private readonly Roles role;
public AccessControlAttribute(Roles role) {
this.role = role;
}
private Boolean AuthorizationCore(AuthorizationFilterContext context) {
var username = context.HttpContext.Request.Cookies["loginCookie_username"];
var password = context.HttpContext.Request.Cookies["loginCookie_password"];
if (role == Roles.FakeFullAccess) {
username = "FAKE";
goto final;
}
//In ASP.Net MVC 5 use Ninject for dependency injection and get the service using : [NinjectContext].GetKernel.Get<DbContext>();
DbContext db = (DbContext) context.HttpContext.RequestServices.GetService(typeof(DbContext));
if (username != null && password != null) {
var findUser = db.Set<Login>().Find(username);
if (findUser != null && findUser.Password.Equals(password) && findUser.RoleId == (int)role) {
goto final;
}
}
return false;
final: {
context.HttpContext.User.AddIdentity(new System.Security.Principal.GenericIdentity(username));
return true;
}
}
private void HandleUnauthorizedRequest(AuthorizationFilterContext context) {
context.Result = new RedirectToRouteResult(new {
area = "",
controller = "",
action = ""
});
}
public void OnAuthorization(AuthorizationFilterContext context)
{
if (AuthorizationCore(context))
{
// If using a combination of roles, you have to unmask it
if (role == Roles.FakeFullAccess) {
context.HttpContext.Request.Headers.Add("Render", "FakeAccess");
}
else if (role == Roles.Admin)
{
context.HttpContext.Request.Headers.Add("Render", "AdminAccess");
}
}
else {
HandleUnauthorizedRequest(context);
}
}
}
[Flags]
public enum Roles
{
FakeFullAccess = 0,
ReadOnly = 1,
Admin = 2,
Supervisor = 1 << 2,
AnotherRole = 1 << 3
}
in your view you can read the added header and customize the view (in ASP.Net Core there's no access to ControllerContext and ViewBag, if using ASP.Net MVC 5 you don't need to use the header trick)
// For ASP.Net MVC 5 use the ViewBag or ViewData
#Html.Partial(HttpContext.Request.Header["Render"])
//Assuming this renders the menu with proper functions.
Now you have fully customizable role based authentication system with fake access for testing.
Update:
To consume the attribute do the following
[AccessControl(Role.Admin)]
public TestController: Controller {
...
}
// Dedicated for testing
[AccessControl(Role.FakeAccess)]
public PreviewController: TestCoontroller{}
You can also combine roles if required like [AccessControl(Role.FakeAccess | Role.ReadOnly)] but you have to implement an unmasking method.
I have the following custom authorization class inside my asp.net mvc web application, which i call before my action methods:-
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class CheckUserPermissionsAttribute : AuthorizeAttribute
{
public string Model { get; set; }
public string Action { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (!httpContext.Request.IsAuthenticated)
return false;
//code goes here................
if (!repository.can(ADusername, Model, value)) // implement this method based on your tables and logic
{ return false; }
return true;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
var viewResult = new JsonResult();
viewResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
viewResult.Data = (new { IsSuccess = "Unauthorized", description = "Sorry, you do not have the required permission to perform this action." });
filterContext.Result = viewResult;
}
else
{
var viewResult = new ViewResult();
viewResult.ViewName = "~/Views/Errors/_Unauthorized.cshtml";
filterContext.Result = viewResult;
}
// base.HandleUnauthorizedRequest(filterContext);
}
}
and i call this custom authorization before my action method as follow:-
[CheckUserPermissions(Action = "Read", Model = "Accounts")]
public ActionResult Index(){
Currently as seen in the above code when the request is not authorized , I will return a JSON or a partial view depending on request type (if it is Ajax request or not).
And inside my code I always take care of handling the json returned from the custom authorization class inside the onsuccess script as follow:-
function addrecords(data) {
if (data.IsSuccess == "Unauthorized") {
jAlert(data.description, 'Unauthorized Access');
}
else if (data.IsSuccess) {
jAlert(data.description, 'Creation Confirmation');
}
Currently my approach is working well, but I start thinking if I should continue with the fact that I am NOT retuning 401 http response for unauthorized requests ? and instead of that I am returning an http 200 , either as json object with status = “unauthrized” or redirect to a partial view ?
Can anyone advice ?
Thanks.
i used to do like this:
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.StatusCode = 403;
filterContext.Result = new JsonResult { Data = "LogOut" };
}
else
{
filterContext.Result = new RedirectResult("~/Home/Index");
}
and in jquery i check in generic ajaxError:
$(document).ajaxError(function(xhr, statusText, err){
if(xhr.status == 403) {
alert("Unathorized Request");
}
});
or:
$.ajaxSetup({
error: function (x, e) {
if (x.status == 403) {
alert("Unauthorized Access");
}
});
});
In your approach you have to check in every Ajax call success the response what is coming, but in this approach in unauthorized case returning 403 code will make Ajax call fail and error callback executes and we i use to write a generic error handler for Ajax and check if status code is that what u i return then show message that it is unauthorized request.
you can see details : Asp.net mvc Check User is Logged In and authorized Before Access to Page
I am trying to customize my Authorize attribute so that it redirects the user to appropriate page if he is not authorized.
This is my code till now:
public class CustomAuthorizationAttribute : AuthorizeAttribute
{
public string ErrorMessage { get; set; }
public string WebConfigKey { get; set; }
private const string UnauthorizedAccessMessage = "UnauthorizedAccessMessage";
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
HttpContext.Current.Session["foo"] = "bar";
base.HandleUnauthorizedRequest(filterContext);
if (string.IsNullOrEmpty(WebConfigKey))
throw new ArgumentNullException("WebConfigKey parameter is missing. WebConfigKey should give the actual page/url");
string configValue = ConfigurationManager.AppSettings[WebConfigKey];
if (string.IsNullOrEmpty(configValue))
throw new Exception(WebConfigKey + "'s value is null or empty");
if (!configValue.StartsWith("http"))
HttpContext.Current.Response.Redirect(WebUIUtils.GetSiteUrl() + configValue);
else
HttpContext.Current.Response.Redirect(configValue);
filterContext.Controller.TempData[UnauthorizedAccessMessage] = ErrorMessage;
HttpContext.Current.Session[UnauthorizedAccessMessage] = ErrorMessage;
}
}
Problem is whatever I store in Session or TempData in this method gets lost when the user arrives in some action method in controller after redirect is done from this method. I checked Session.Keys/TempData.Keys etc. But all values are lost. Probably something is happening in base.HandleUnauthorizedRequest(filterContext);. But I guess that calling to base is important.
Can anybody tell me the exact reason of this behavior and how do I prevent it from happening?
Form authorization and Session are distinct concepts for IIS. You can be authorized but your session can be not valid (for example try to restart the application pool).
Try with this custom attribute:
public class CustomAuthorizationAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (filterContext.Result == null)
{
if (filterContext.HttpContext.Session != null )
{
//add checks for your configuration
//add session data
// if you have a url you can use RedirectResult
// in this example I use RedirectToRouteResult
RouteValueDictionary rd = new RouteValueDictionary();
rd.Add("controller", "Account");
rd.Add("action", "LogOn");
filterContext.Result = new RedirectToRouteResult("Default", rd);
}
}
}
}