Overriding Authorize Attribute - asp.net-mvc

I have a need to override authorize attribute.
Basically if its an ajax request and the user is not logged in or is not in specified roles then i want to return a JSON. The JSON will tell the caller the reason as not logged in or not in role and needs to return the redirect to url. In case of not signed it, it also needs to give back ReturnUrl.
If its not an ajax request then i want the default processing by Authorize attribute to kick in.
We are using forms authentication and the sign in url and error pages are specified in the web.config file.
Following is my take at it but i am not getting the following right
missing roles processing in case of an ajax request
in case of not an ajax request (else block), i am redirecting the user to the sign in page. i want the default autorize attribute to kickin in this case
I just need the push in the right direction... tutorial or a blog pointer is all i need to learn and accomplish this....
public class AuthorizePartnerProgramsAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
HttpContext httpContext = HttpContext.Current;
var url = new UrlHelper(filterContext.RequestContext);
var request = filterContext.HttpContext.Request;
if (request.IsAuthenticated == false)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
if (request.Url != null)
filterContext.Result = CommonUtilities.AddJsonUtf8Encoding(new JsonResult { Data = new { error = true, singinerror = true, message = "Sign in required!", returnUrl = request.UrlReferrer.AbsolutePath.ToString() } });
else
filterContext.Result = CommonUtilities.AddJsonUtf8Encoding(new JsonResult { Data = new { error = true, singinerror = true, message = "Sign in required!" } });
}
else
{
if (request.UrlReferrer != null)
{
filterContext.Result = new RedirectResult(url.Action("Index", "SignIn", new { Area = "Account", ReturnUrl = filterContext.RequestContext.HttpContext.Request.UrlReferrer.AbsolutePath.ToString() }));
}
else
{
filterContext.Result = new RedirectResult(url.Action("Index", "SignIn", new { Area = "Account"}));
}
}
}
}
}
Here is my second stab at it. I think i am now more confused than before and need help setting it up properly
public class AuthorizeCustomAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
var request = filterContext.RequestContext.HttpContext.Request;
if (request.IsAjaxRequest())
{
var url = new UrlHelper(filterContext.RequestContext);
var urlReferer = request.UrlReferrer != null
? request.UrlReferrer.ToString()
: String.Empty;
var signInUrl = url.Action("Index", "SignIn", new { Area = "Account", ReturnUrl = urlReferer });
var accessDeniedUrl = url.Action("PageAccessDenied", "Error", new { Area = "" });
if (!request.IsAuthenticated)
{
//not authenticated
filterContext.Result =
CommonUtilities.AddJsonUtf8Encoding(new JsonResult
{
Data =
new {error = true, singinerror = true, message = "Sign in required!", url = signInUrl},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
});
}
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext.Request.IsAjaxRequest())
{
//Use [AuthorizeCustom(Roles="MyRole1,MyRole2")]
//or [AuthorizeCustom]
//roles may not have been applied here
//checking authentication will be done by the HandleUnauthorizedRequest?????
//if no roles are specified then it is true = so give access to the resource
//user may have multiple roles or single role assigned, check and if not in role then return json back.
//....
}
else
{
return base.AuthorizeCore(httpContext);
}
}
}

This helped me setting up mine
http://www.dotnet-tricks.com/Tutorial/mvc/G54G220114-Custom-Authentication-and-Authorization-in-ASP.NET-MVC.html
use
[AuthorizeCustom(Roles = RoleNames.Admin)]
Here is the full working attribute for me without any cleanup.
public class AuthorizeCustomAttribute : AuthorizeAttribute
{
#region CONSTANTS
public const string SectionStemFuture = "StemFuture";
#endregion
#region PROPERTIES
private string Section { get; set; }
#endregion
#region Constructor
public AuthorizeCustomAttribute()
{
Section = String.Empty;
}
public AuthorizeCustomAttribute(string section)
{
Section = section;
}
#endregion
#region Overrides
public override void OnAuthorization(AuthorizationContext filterContext)
{
var request = filterContext.HttpContext.Request;
var url = new UrlHelper(filterContext.RequestContext);
/*
var urlReferer = request.UrlReferrer != null
? request.UrlReferrer.ToString()
: String.Empty;
*/
var urlReferer = request.Url.PathAndQuery;
var signInUrl = url.Action("Index", "SignIn", new { Area = "Account", ReturnUrl = urlReferer });
var accessDeniedUrl = url.Action("PageAccessDenied", "Error", new { Area = "" });
//overwrite the default sign in URL according to the section
if (!String.IsNullOrWhiteSpace(Section))
{
switch (Section)
{
case SectionStemFuture:
signInUrl = url.Action("Index", "StemFutureHome", new { Area = "StemFuture", ReturnUrl = urlReferer });
break;
}
}
if (!request.IsAuthenticated)
{
//not authenticated
if (request.IsAjaxRequest())
{
filterContext.Result =
CommonUtilities.AddJsonUtf8Encoding(new JsonResult
{
Data =
new {error = true, signinerror = true, message = "Sign in required", url = signInUrl},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
});
}
else
{
//this is not an ajax request
if (!String.IsNullOrWhiteSpace(Section))
{
filterContext.Result = new RedirectResult(signInUrl);
}
else
{
//let the base authorization take care of it
base.OnAuthorization(filterContext);
}
}
}
else if (!String.IsNullOrWhiteSpace(base.Roles))
{
var isRoleError = true;
var rolesAllowed = base.Roles.Split(',');
//authenticated and we have some roles to check against
var user = filterContext.HttpContext.User;
if (user != null && rolesAllowed.Any())
{
foreach (var role in rolesAllowed)
{
if (user.IsInRole(role))
{
isRoleError = false;
}
}
}
if (isRoleError)
{
if (request.IsAjaxRequest())
{
filterContext.Result =
CommonUtilities.AddJsonUtf8Encoding(new JsonResult
{
Data =
new
{
error = true,
signinerror = true,
message = "Access denied",
url = accessDeniedUrl
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
});
}
else
{
//here we will need to pass to the access denied
filterContext.Result = new RedirectResult(accessDeniedUrl);
}
}
}
}
#endregion
}

Related

JWT Authentication ASP.NET Core MVC application

I've seen numerous examples of how to use JWT authentication with Angular, React, Vue etc... clients but can't find any examples of using JWT authentication with ASP.NET Core (specifically 2.2) Web App Mvc.
Does anyone have any examples or advice on how to do this?
Thanks,
You can use this class based on nuget package JWT 3.0.3
using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using Newtonsoft.Json;
using System;
namespace Common.Utils
{
public class JwtToken
{
private IJwtEncoder encoder;
private IJwtDecoder decoder;
/// <remarks>
/// This requires a key value randomly generated and stored in your configuration settings.
/// Consider that it is a good practice use keys as at least long as the output digest bytes
/// length produced by the hashing algorithm used. Since we use an HMAC-SHA-512 algorithm,
/// then we can provide it a key at least 64 bytes long.
/// <see cref="https://tools.ietf.org/html/rfc4868#page-7"/>
/// </remarks>
public string SecretKey { get; set; }
public JwtToken()
{
IJwtAlgorithm algorithm = new HMACSHA512Algorithm();
IJsonSerializer serializer = new JsonNetSerializer();
IDateTimeProvider datetimeProvider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, datetimeProvider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
decoder = new JwtDecoder(serializer, validator, urlEncoder);
SecretKey = "";
}
public JwtToken(string secretKey) : this()
{
SecretKey = secretKey;
}
public bool IsTokenValid(string token)
{
return !string.IsNullOrWhiteSpace(DecodeToken(token));
}
public string GetToken(object payload)
{
try
{
return encoder.Encode(payload, SecretKey);
}
catch (Exception)
{
return encoder.Encode(new DataModel(payload), SecretKey);
}
}
public string DecodeToken(string token)
{
try
{
if (string.IsNullOrWhiteSpace(token) || token == "null")
{
return null;
}
return decoder.Decode(token, SecretKey, true);
}
catch (TokenExpiredException)
{
return null;
}
catch (SignatureVerificationException)
{
return null;
}
}
public T DecodeToken<T>(string token) where T : class
{
try
{
if (string.IsNullOrWhiteSpace(token))
{
return null;
}
return decoder.DecodeToObject<T>(token, SecretKey, true);
}
catch (TokenExpiredException)
{
return null;
}
catch (SignatureVerificationException)
{
return null;
}
catch (Exception)
{
var data = decoder.DecodeToObject<DataModel>(token, SecretKey, true).Data;
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(data));
}
}
}
public class DataModel
{
public DataModel(object data)
{
Data = data;
}
public object Data { get; set; }
}
}
Then in your Startup class Configure method set the jwt middleware
for check authentication status of each request:
app.Use((context, next) =>
{
// verify app access token if not another service call
var appAccessToken = context.Request.Headers["Authorization"];
if (appAccessToken.Count == 0)
{
context.Items["User"] = null;
}
else
{
var token = appAccessToken.ToString().Replace("Bearer ", "");
var jwtToken = new JwtToken(config.JwtTokenSecret); //you need a secret (with requirements specified above) in your configuration (db, appsettings.json)
if (string.IsNullOrWhiteSpace(token) || !jwtToken.IsTokenValid(token))
{
context.Response.StatusCode = 401;
return Task.FromResult(0);
}
dynamic user = jwtToken.DecodeToken<dynamic>(token);
var cachedToken = cache.Get(user.Id); //you need some cache for store your token after login success and so can check against
if (cachedToken == null || cachedToken.ToString() != token)
{
context.Response.StatusCode = 401;
return Task.FromResult(0);
}
context.Items["User"] = new Dictionary<string, string>() {
{ "FullName",user.Name?.ToString()},
{ "FirstName",user.FirstName?.ToString()},
{ "LastName",user.LastName?.ToString()},
{ "Role",user.Role?.ToString()},
{ "Email",user.Email?.ToString()}
};
}
return next();
});
And finally you need generate the token and return it after
authentication:
[AllowAnonymous]
public IActionResult Login(string username, string password)
{
User user = null; //you need some User class with the structure of the previous dictionary
if (checkAuthenticationOK(username, password, out user)) //chackAuthenticationOk sets the user against db data after a succesfull authentication
{
var token = new JwtToken(_config.JwtTokenSecret).GetToken(user); //_config is an object to your configuration
_cache.Set(user.id, token); //store in the cache the token for checking in each request
return Ok(token);
}
return StatusCode(401, "User is not authorized");
}
Add following code to startup
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Issuer"],
ValidAudience = Configuration["Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["SigningKey"]))
};
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env,, ILoggerFactory loggerFactory)
{
app.UseAuthentication();
}
Code for login action in AccountController
[Route("api/[controller]")]
public class AccountController : Controller
{
[AllowAnonymous]
[HttpPost]
[Route("login")]
public IActionResult Login([FromBody]LoginViewModel loginViewModel)
{
if (ModelState.IsValid)
{
var user = _userService.Authenticate(loginViewModel);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, loginViewModel.Username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
var token = new JwtSecurityToken
(
issuer: _configuration["Issuer"],
audience: _configuration["Audience"],
claims: claims,
expires: DateTime.UtcNow.AddDays(10),
notBefore: DateTime.UtcNow,
signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["SigningKey"])),
SecurityAlgorithms.HmacSha256)
);
return Ok(new
{
access_token = new JwtSecurityTokenHandler().WriteToken(token),
expires_in = (int)token.ValidTo.Subtract(DateTime.UtcNow).TotalSeconds,// TimeSpan.FromTicks( token.ValidTo.Ticks).TotalSeconds,
sub = loginViewModel.Username,
name = loginViewModel.Username,
fullName = user.FullName,
jobtitle = string.Empty,
phone = string.Empty,
email = user.EmailName,
});
}
}
}
I assume you have implemented JWT on the server side. To handle this on client side, first you have to add token to web browser local storage. Add to your main layout javascript (let's named it AuthService.js)
below code adds token to local storage after login button clicked. gettokenfromlocalstorage() retrieve token from local storage.
<script>
var token = "";
function Loginclick() {
var form = document.querySelector('form');
var data = new FormData(form);
var authsevice = new AuthService();
authsevice.LogIn(data.get("username").toString(), data.get("password").toString());
}
function gettokenfromlocalstorage() {
var authserv = new AuthService();
var mytoken = authserv.getAuth();
authserv.LogOut();
}
var AuthService = /** #class */ (function () {
function AuthService() {
this.authKey = "auth";
}
AuthService.prototype.LogIn = function (username, password) {
this.username = username;
this.password = password;
this.grant_type = "password";
this.client_id = "MyClientId";
var loginurl = "/api/Token/Auth";
var xhr = new XMLHttpRequest();
xhr.open("POST", loginurl, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(this));
xhr.onreadystatechange = function () {
console.log("onreadystatechange");
};
xhr.onerror = function () {
var aaa = this.responseText;
};
xhr.onload = function () {
var data = JSON.parse(this.responseText);
var auth = new AuthService();
auth.setAuth(data);
};
};
AuthService.prototype.LogOut = function () {
this.setAuth(null);
return true;
};
AuthService.prototype.setAuth = function (auth) {
if (auth) {
localStorage.setItem(this.authKey, JSON.stringify(auth));
}
else {
localStorage.removeItem(this.authKey);
}
return true;
};
AuthService.prototype.getAuth = function () {
var i = localStorage.getItem(this.authKey);
return i === null ? null : JSON.parse(i);
};
AuthService.prototype.isLoggedIn = function () {
return localStorage.getItem(this.authKey) !== null ? true : false;
};
return AuthService;
}());
var aa = new AuthService();
var gettoken = aa.getAuth();
if (gettoken !== null) {
token = gettoken.token;
}
</script>
To add token to the header of each anchor tag put below script also to
your main layout.
<script>
var links = $('a');
for (var i = 0; i < links.length; i++) {
links[i].onclick = function check() {
addheader(this.href);
return false;
}
}
function addheader(object) {
let xhr = new XMLHttpRequest();
xhr.open("GET", object, true);
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
xhr.send(null);
xhr.onload = function () {
window.history.pushState("/", "", xhr.responseURL);
//mycontainer is a div for parialview content
$("#mycontainer").html(xhr.responseText);
window.onpopstate = function (e) {
if (e.state) {
$("html").html = e.state;
document.title = e.state.pageTitle;
}
};
};
}
</script>
Remember that using of this approach, each view has to be loaded as a partial view.
If you insert url address in a web browser bar directly this solution doesn't work. I haven't figured it out yet. That's why to manage token authentication is better using single page application, not multipage application.
You can use this boilerplate to understand how to implement JWT tokenization with .Net Core. In the project you can find JWT, Swagger and EF features.

Custom Authorization with Parameters Web API

Can someone show me how to use the parameter in Customize AuthorizeAttribute?
Like this:
[Authorize(Role="Admin,Supervisor")]
[Authorize(User="Me,You")]
[Authorize(Action="abc,def")]
This is my code now and I dont have any idea yet how to add the parameter here.
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
ApplicationDbContext _context = new ApplicationDbContext();
public override void OnAuthorization(HttpActionContext actionContext)
{
if (AuthorizeRequest(actionContext))
{
return;
}
HandleUnauthorizedRequest(actionContext);
}
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
if (((System.Web.HttpContext.Current.User).Identity).IsAuthenticated)
{
actionContext.Response = new HttpResponseMessage()
{
StatusCode = HttpStatusCode.Unauthorized,
Content = new StringContent("You are unauthorized to access this resource")
};
}
else
{
base.HandleUnauthorizedRequest(actionContext);
}
}
private bool AuthorizeRequest(HttpActionContext actionContext)
{
var action = actionContext.ActionDescriptor.ActionName;
var controller = actionContext.ControllerContext.ControllerDescriptor.ControllerName;
var currentUser = actionContext.RequestContext.Principal.Identity.GetUserId();
var user = _context.Users.Join(_context.UserAccesses, x => x.RoleId, y => y.RoleId, (x, y) =>
new { Id = x.Id, firstName = x.firstName, lastName = x.lastName, RoleId = x.RoleId, Controller = y.Controller,
Action = y.Action }).Where(z => z.Id == currentUser && z.Controller == controller && z.Action == action)
.SingleOrDefault();
if (user != null)
return true;
else
return false;
}
}
As you have extended the default implementation of Authorize, you need to use [CustomAuthorize(Role="Admin,Supervisor")]. This will set the roles. You can then access the Roles property directly in your code as they are contained in the parent AuthorizeAttribute which has been inherited.
public override void OnAuthorization(HttpActionContext actionContext)
{
var roles = Roles;
if (AuthorizeRequest(actionContext))
{
return;
}
HandleUnauthorizedRequest(actionContext);
}

Redirect to external url from OnActionExecuting?

I need to redirect to an external url (let's say "www.google.com") from OnActionExecuting method. Right now I'm using something like this:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
var redirectUrl = "www.google.com";
try
{
var isAjaxRequest = filterContext.HttpContext.Request.IsAjaxRequest();
if (isAjaxRequest)
{
filterContext.HttpContext.Response.StatusCode = SessionController.CustomHttpRedirect;
filterContext.HttpContext.Response.StatusDescription = redirectUrl;
filterContext.Result = new JsonResult
{
Data = new { Redirect = redirectUrl },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
else
{
filterContext.Result = new RedirectResult(redirectUrl, true);
}
return;
}
else
{
throw new LoggedOutException();
}
}
catch
{
throw new LoggedOutException();
}
}
}
The problem is that it's not redirecting me to "www.google.com" but it's redirecting to "http://localhost:1234/www.google.com" (I try it locally).
There is any way to solve this ?
Thanks
The problem was verry easy to solve:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
var redirectUrl = "http://www.google.com";
try
{
var isAjaxRequest = filterContext.HttpContext.Request.IsAjaxRequest();
if (isAjaxRequest)
{
filterContext.HttpContext.Response.StatusCode = SessionController.CustomHttpRedirect;
filterContext.HttpContext.Response.StatusDescription = redirectUrl;
filterContext.Result = new JsonResult
{
Data = new { Redirect = redirectUrl },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
else
{
filterContext.Result = new RedirectResult(redirectUrl, true);
}
return;
}
else
{
throw new LoggedOutException();
}
}
catch
{
throw new LoggedOutException();
}
}
}
All I had to do was that when I assigned the value to "redirectUrl", I had tu put http before wwww. This mus be put if you use a SSL conenction and you're trying to redirect from mvc to another domain.
Instead of using:
filterContext.Result = new RedirectResult("www.google.com", true);
Try the following:
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Home", action = "External" , ReturnURL = "www.google.com"}));
and in your (Home) controller create an action called (External) and from there redirect to your external url:
public class HomeController : Controller
{
[AllowAnonymous]
public ActionResult External(string ReturnURL){
return Redirect(ReturnURL);
}
}
You can't directly perform a server side redirect from an ajax response. You could, however, return a JsonResult with the new url and perform the redirect with javascript. see this answer

Redirect to browsed url after login(authentication)

I have developed a module which authorizes the roles from the database dynamically. Now, what i want is,when a user comes and browses different actionmethod without logging in, I am able to redirect the user to the login page. As soon as the user logs in, he should be redirected to the actionmethod/view which he was trying to access without login. The following is the code which i am using to extract the URL browsed without logging in. I also have a key defined in my web.config as serverURL which gives me initial url like localhost. How to i make the below returnurl remembered and redirect the user to the desired actionmethod/view after logging in.
returnUrl = HttpContext.Current.Request.RawUrl;
public class AuthorizeUserAttribute : AuthorizeAttribute
{
public string Feature { get; set; }
public string returnUrl { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//var isAuthorized = base.AuthorizeCore(httpContext);
//if (!isAuthorized)
//{
// return false;
//}
if (httpContext != null && httpContext.Session != null && httpContext.Session["Role"] != null)
{
string userRoles = UserBL.ValidateUsersRoleFeature(httpContext.Session["Role"].ToString(), Feature);
if (!string.IsNullOrEmpty(userRoles))
{
if (userRoles.IndexOf(httpContext.Session["Role"].ToString()) >= 0)
{
return true;
}
}
return false;
}
else
return false;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
HttpSessionStateBase session = filterContext.HttpContext.Session;
if (session.IsNewSession || session["Email"] == null)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
// For AJAX requests, return result as a simple string,
// and inform calling JavaScript code that a user should be redirected.
JsonResult result = new JsonResult();
result.ContentType = "text/html";
result.Data = "SessionTimeout";
filterContext.Result = result;
//$.ajax({
// type: "POST",
// url: "controller/action",
// contentType: "application/json; charset=utf-8",
// dataType: "json",
// data: JSON.stringify(data),
// async: true,
// complete: function (xhr, status) {
// if (xhr.responseJSON == CONST_SESSIONTIMEOUT) {
// RedirectToLogin(true);
// return false;
// }
// if (status == 'error' || !xhr.responseText) {
// alert(xhr.statusText);
// }
// }
// });
//}
}
else
{
// For round-trip requests,
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary { { "Controller", "User" }, { "Action", "Login" } });
returnUrl = HttpContext.Current.Request.RawUrl;
}
}
else
base.OnAuthorization(filterContext);
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
controller = "Base",
action = "PageNotAccessible"
})
);
}
}
In attribute return the url on which user was in routes:
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "Controller", "User" },
{ "Action", "Login" },
{"returnUrl",HttpContext.Current.Request.RawUrl}
});
and in your action:
[AllowAnonymous]
public virtual ActionResult Login()
{
ViewBag.returnUrl = Request.QueryString["returnUrl"];
return View();
}
In View:
#using(Html.BeginForm("Login","User",new{returnUrl = ViewBag.returnUrl},FormMethod.Post))
{
<input type="submit" value="Login" />
}
and in Post Action:
[AllowAnonymous]
[HttpPost]
public virtual ActionResult Login(User model, string returnUrl)
{
if(ModelState.IsValid)
{
// check if login successful redirect to url from where user came
if(LoginSucessful)
return Redirect(returnUrl); // will be redirected to url from where user came to login
return View();
}
in your html page, create a hidden tag:
<div id="HiddenURL" class="hidden"></div>
the moment the user access a certain page, use Javascript to bind the url the user came from in a hidden value in you web page:
$(document).ready(function ()
{
$('#HiddenURL').text(window.location.href.toLowerCase());
...
}
In your asp.net page assign to action, the url taken from div text:
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
controller = "Base",
action = HiddenURL.Value
})
);
}

Authorize Attribute Not Working with Roles MVC C#

I'm modifying a system written in c# MVC at the moment.
I've just built in an extra bit of functionality in the Administrator area that allows the administrator create a user account that has limited administrator functionality. I've put the following over each of the controllers for the new functionality:
[Authorize(Roles = "Administrator")]
However, if I log in using limited administrator account, and navigate to this page, it lets me through.
I'm stumped because I appear to be doing this the right way but I'm also fairly new to MVC, is there anything else I can check? I haven't changed anything in the web.config file so that should be ok.
I know there's limited information above, not looking for a ready-made solution, more advice on what I can check to correct the issue.
thanks
EDIT:
This is how the new role/account was created. Go easy too, this is a first ditch attempt, there's not much validation.
[Authorize(Roles = "Administrator")]
[HttpPost]
public ActionResult AddSalesManager(App.Web.Areas.Administrator.Models.SalesManager model, FormCollection formValues)
{
if (formValues["Cancel"] != null)
{
return RedirectToAction("Index");
}
if (!string.Equals(model.password, model.confirmpassword))
{
ModelState.AddModelError("password", "Password and Confirmation must match");
}
if (ModelState.IsValid)
{
using (ModelContainer ctn = new ModelContainer())
{
// First, create the user account inside the ASP.Net membership system.
//
Membership.ApplicationName = "App";
Roles.ApplicationName = "App";
if (!Roles.RoleExists("LimitedAdmin"))
Roles.CreateRole("LimitedAdmin");
// MembershipCreateStatus createStatus = MembershipService.CreateUser(model.email, model.password, model.email);
if (Membership.GetUser(model.email) == null)
{
Membership.CreateUser(model.email, model.password);
Roles.AddUserToRole(model.email, "LimitedAdmin");
}
}
}
return RedirectToAction("Index");
}
Role attribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class PermissionsAttribute : ActionFilterAttribute
{
private readonly PermissionsType required;
public PermissionsAttribute()
{
}
public PermissionsAttribute(PermissionsType required)
{
this.required = required;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Log("OnActionExecuting", filterContext.RouteData);
HttpSessionStateBase session = filterContext.HttpContext.Session;
Controller controller = filterContext.Controller as Controller;
//This is uesd to redirect to same controller but differnect action
// controller.HttpContext.Response.Redirect("./Login");
var rjasthan = filterContext;
var URK = filterContext.HttpContext.Request.RawUrl;
if (session["UserPermissions"] != null)
{
if (!CheckPermissions((UserPermission)session["UserPermissions"]))
{
// this is used to signout from sesssion
// filterContext.HttpContext.GetOwinContext().Authentication.SignOut();
filterContext.Controller.TempData["AuthenticationMessages"] = "You are not authorized to access";
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary{
{ "controller", "Home" },{ "action", "UnAuthorizeAccess" }});
}
}
base.OnActionExecuting(filterContext);
}
protected bool CheckPermissions(UserPermission model)
{
bool result = false;
if (this.required == (PermissionsType.Add))
{
if (model.AddRight)
result = true;
}
else if (this.required == (PermissionsType.View))
{
if (model.ViewRight)
result = true;
}
else if (this.required == (PermissionsType.Edit))
{
if (model.EditRight)
result = true;
}
else if (this.required == (PermissionsType.Delete))
{
if (model.DeleteRight)
result = true;
}
else if (this.required == (PermissionsType.View | PermissionsType.Edit))
{
if (model.ViewRight && model.EditRight)
{
result = true;
}
}
else if (this.required == (PermissionsType.Add | PermissionsType.Edit))
{
if (model.AddRight && model.EditRight)
{
result = true;
}
}
return result;
}
private void Log(string methodName, RouteData routeData)
{
var controllerName = routeData.Values["controller"];
var actionName = routeData.Values["action"];
var message = String.Format("{0} controller:{1} action:{2}", methodName, controllerName, actionName);
Debug.WriteLine(message, "Action Filter Log");
}
}
[Flags]
public enum PermissionsType
{
View = (1 << 0),
Add = (1 << 1),
Edit = (1 << 2),
Delete = (1 << 3),
Admin = (View | Add | Edit | Delete)
}
[Permissions(PermissionsType.Add)]
public ActionResult Register()
{
return this.AjaxableView();
}
What do you expect from this code?
With this attribute you gain all users in the administrator role the right to execute this controller action no matter how limited the account is.

Resources