Custom Authorize Attribute in Web API - asp.net-mvc

I want to create my custom authorization in web api controller to check the roles of the user and if its active user. So far this is my code and I don't know yet how/what to override in this codes.
Thanks! your help is appreciated :D
using Avanza.Conference.Persistence;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Controllers;
namespace Avanza.Conference.Core.Extensions
{
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
ApplicationDbContext _context = new ApplicationDbContext(); // my entity
public override void OnAuthorization(HttpActionContext actionContext)
{
//Sample on what to do here??
if (AuthorizeRequest(actionContext))
{
return;
}
HandleUnauthorizedRequest(actionContext);
}
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
//Code to handle unauthorized request
var challengeMessage = new HttpResponseMessage(HttpStatusCode.Unauthorized);
challengeMessage.Headers.Add("WWW-Authenticate", "Basic");
throw new HttpResponseException(challengeMessage);
}
private bool AuthorizeRequest(HttpActionContext actionContext)
{
//Sample on what to do here??
return true;
}
}
}

Here is the sample you required, this check the request contains the authenticationtoken then only allow to execute the request. you can here check your session is available to check user logged in or not.
public class CustomAuthorize : System.Web.Http.AuthorizeAttribute
{
public override void OnAuthorization({
System.Web.Http.Controllers.HttpActionContext actionContext)
private readonly string Resource {get; set; }base.OnAuthorization(actionContext);
if (actionContext.Request.Headers.GetValues("authenticationToken") != null)
string authenticationToken =public Convert.ToStringCustomAuthorize(
string resource, string actionContext.Request.Headers.GetValues("authenticationToken").FirstOrDefault()action);
//authenticationTokenPersistant{
// it is saved in someResource data= storeresource;
// i will compare the authenticationToken sent byAction client= withaction;
// authenticationToken persist in database against specific user, and act accordingly}
public override ifvoid OnAuthorization(authenticationTokenPersistant != authenticationToken)
{
HttpContextSystem.CurrentWeb.ResponseHttp.AddHeader("authenticationToken",Controllers.HttpActionContext authenticationTokenactionContext);
HttpContext.Current.Response.AddHeader("AuthenticationStatus", "NotAuthorized");{
actionContext.Response = actionContext.Requestbase.CreateResponseOnAuthorization(HttpStatusCode.ForbiddenactionContext);
return;
}
//Check your post authorization logic using Resource HttpContext.Current.Response.AddHeader("authenticationToken",and authenticationToken);Action
HttpContext.Current.Response.AddHeader("AuthenticationStatus", "Authorized");
//Your logic here to return return;
authorize or unauthorized response }
actionContext.Response =
actionContext.Request.CreateResponse(HttpStatusCode.ExpectationFailed);}
actionContext.Response.ReasonPhrase = "Please provide valid inputs";
}

Related

How to populate UserInfo object in DNN Authentication providers?

I am trying to create an authentication provider in DotNetNuke 7.4 which supports LinkedId. I have used the source package for the Facebook provider from the DnnPlatform GIT as the base and have modified it for LinkedIn's oAuth. I am able to connect via LinkedIn and get the auth token but the code fails on
OAuthClient.GetCurrentUser<LinkedInUserData>();
due to LinkedInUserData being null. the specific logged error is
DotNetNuke.Services.Exceptions.Exceptions - ~/Default.aspx?tabid=55&error=An unexpected error has occurred
System.ArgumentNullException: Value cannot be null.
Parameter name: value
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
at DotNetNuke.Services.Authentication.OAuth.OAuthClientBase.GetCurrentUser[TUserData]()
at DotNetNuke.Authentication.LinkedIn.Login.GetCurrentUser() in c:\Websites\dnndev74_2\DesktopModules\AuthenticationServices\LinkedIn\Login.ascx.cs:line 103
Below are the 3 classes that are in play, there is a lot of inheritance going on so I'm having trouble understanding the mechanism for how LinkedInUserData gets populated in the first place. On a note. when I take the facebook codebase from GIT and install it as a provider on my local, and try to register with facebook account, I get the same error. However, if I install the provider through the CMS it runs fine or use the dll that comes with the dnn 7.4 install, facebook works. So I am lead to believe there is something fundamentally flawed with the GIT code..
LinkedInClient.cs
namespace DotNetNuke.Authentication.LinkedIn.Components
{
public class LinkedInClient : OAuthClientBase
{
#region Constructors
public LinkedInClient(int portalId, AuthMode mode)
: base(portalId, mode, "LinkedIn")
{
base.AuthorizationEndpoint = new Uri("https://www.linkedin.com/uas/oauth2/authorization");
base.RequestTokenEndpoint = new Uri("https://api.linkedin.com/uas/oauth/requestToken?scope=r_emailaddress");
base.TokenMethod = HttpMethod.POST;
base.TokenEndpoint = new Uri("https://www.linkedin.com/uas/oauth2/accessToken");
base.MeGraphEndpoint = new Uri("https://api.linkedin.com/v1/people/~:(id,first-name,last-name,email-address,formatted-name,picture-url)?format=json");
base.AuthTokenName = "LinkedInUserToken";
base.OAuthVersion = "2.0";
base.LoadTokenCookie(string.Empty);
}
#endregion
protected override TimeSpan GetExpiry(string responseText)
{
var jsonSerializer = new JavaScriptSerializer();
var tokenDictionary = jsonSerializer.DeserializeObject(responseText) as Dictionary<string, object>;
return new TimeSpan(0, 0, Convert.ToInt32(tokenDictionary["expires_in"]));
}
protected override string GetToken(string responseText)
{
var jsonSerializer = new JavaScriptSerializer();
var tokenDictionary = jsonSerializer.DeserializeObject(responseText) as Dictionary<string, object>;
return Convert.ToString(tokenDictionary["access_token"]);
}
}
}
LinkedInUserData.cs
namespace DotNetNuke.Authentication.LinkedIn.Components
{
[DataContract]
[Serializable]
public class LinkedInUserData : UserData
{
#region Overrides
public override string FirstName
{
get { return LinkedInFirstName; }
set { }
}
public override string LastName
{
get { return LinkedInLastName; }
set { }
}
public override string Email
{
get { return emailAddress; }
set { }
}
public override string ProfileImage
{
get { return LinkedInPictureUrl; }
set { }
}
#endregion
[DataMember(Name = "first-name")]
public string LinkedInFirstName { get; set; }
[DataMember(Name = "last-name")]
public string LinkedInLastName { get; set; }
[DataMember(Name = "picture-url")]
public string LinkedInPictureUrl { get; set; }
[DataMember(Name = "email-address")]
public string emailAddress { set; get; }
}
Login.cs
namespace DotNetNuke.Authentication.LinkedIn
{
public partial class Login : OAuthLoginBase
{
protected override string AuthSystemApplicationName
{
get { return "LinkedIn"; }
}
public override bool SupportsRegistration
{
get { return true; }
}
protected override UserData GetCurrentUser()
{
return OAuthClient.GetCurrentUser<LinkedInUserData>();
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
loginButton.Click += loginButton_Click;
registerButton.Click += loginButton_Click;
OAuthClient = new LinkedInClient(PortalId, Mode);
loginItem.Visible = (Mode == AuthMode.Login);
registerItem.Visible = (Mode == AuthMode.Register);
}
protected override void AddCustomProperties(NameValueCollection properties)
{
base.AddCustomProperties(properties);
properties.Add("LinkedIn", OAuthClient.GetCurrentUser<LinkedInUserData>().Link.ToString());
}
private void loginButton_Click(object sender, EventArgs e)
{
AuthorisationResult result = OAuthClient.Authorize();
if (result == AuthorisationResult.Denied)
{
UI.Skins.Skin.AddModuleMessage(this, Localization.GetString("PrivateConfirmationMessage", Localization.SharedResourceFile), ModuleMessage.ModuleMessageType.YellowWarning);
}
}
}
}
Mark,
I wrote a DNN provider for Linkedin a couple years ago. Comparing my code to yours, the first thing mine does is redirect the user in order to get an access token. The base url for obtaining the access token and permission from the user is: www.linkedin.com/uas/oauth2. I must pass my linked API key, a redirect Url and a few other pieces of data which comes from the provider settings.
My redirect Url is the same as the default portal login page which loads my provider. Once the user allows LinkedIn to allow my application access and verifies he is logged in, the redirect back to my provider will look for the LinkedInAuthToken cookie in the Request.Cookies. Once I verify the token is valid, I can make an additional API call to the /v1/people/ API to get user data to complete any kind of auto-registration or auto profile updates to DNN.
It seems like your provider is immediately attempting the user data lookup API call before obtaining the oauth access cookie.
The code for my LinkedIn provider is not open source, but I suppose I could get permission from my work to make it public. Message me if you are interested in it.

Trigger authorization validation manually

I've a custom AuthorizeAttribute in my website. It has some logic about the Result created for unathorized requests.
In some cases, I want to trigger its validation manually*. I don't know if its possible. As I haven't found how to do that, I thought that I could extract the logic to get the Result to a diferrent method, and call it when I want. But then I don't know how to execute the ActionResult (outside de controllers).
How can I do to manually execute authorize validation? If not possible, how can I do to execute an ActionResult outside a controller?
*I need to trigger it manually because some request may pass the validation (because the session is created) and then, when accessing my services, found that the session was closed by someone else. I wouldn't like to add a call to the services in OnAuthorization to reduce services calls.
I'm not sure if its the best, but I've found a way to get it working (still listening for better answers).
When I call the services and notice that the work session has expired, all I do is removing the active user in the web session.
My custom authorize attribute also implements IResultFilter and IExceptionFilter.
In both OnResultExecuted and OnException I validate the active user once more. If the session was removed, then apply the same ActionResult that I would apply in OnAuthorization.
Here is the final class:
public class CustomAuthorizeAttribute : AuthorizeAttribute, IResultFilter, IExceptionFilter
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
ActionResult result = Validate(filterContext.HttpContext);
if (result != null)
filterContext.Result = result;
}
public void OnResultExecuted(ResultExecutedContext filterContext)
{
ActionResult result = Validate(filterContext.HttpContext);
if (result != null)
filterContext.Result = result;
}
public void OnResultExecuting(ResultExecutingContext filterContext)
{
}
public void OnException(ExceptionContext filterContext)
{
ActionResult result = Validate(filterContext.HttpContext);
if (result != null)
{
filterContext.Result = result;
filterContext.ExceptionHandled = true;
}
}
public static ActionResult Validate(HttpContextBase httpContext)
{
if (UserActiveInSession)
return null;
// Different rules to build an ActionResult for this specific case.
}
}
I did not get Diego answer's, But Just simply answering the title, I got it to work like that, You can use it as attribute on controllers actions and also trigger it manually at any place in C# or in Razor views.
namespace SomeNameSpace
{
public class CustomAuthorizeAttributeMVC : AuthorizeAttribute
{
private readonly string[] rolesParams;
public CustomAuthorizeAttributeMVC(params string[] roles)
{
this.rolesParams = roles;
}
public bool IsAuthorized { get {
//Do your authorization logic here and return true if the current user has permission/role for the passed "rolesParams"
string[] allowedRoles = new string[] {"role 1", "role 2", "role 3"};
return allowedRoles.Intersect(rolesParams).Any(); //for the example
}
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return this.IsAuthorized;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
//...
}
}
public class AuthorizeHelper
{
public static bool HasPermission(params string[] roles)
{
return new CustomAuthorizeAttributeMVC(roles).IsAuthorized;
}
}
}
Usage example:
[CustomAuthorizeAttributeMVC("role 2")]
public ActionResult SomeAction()
{
return Content("Authorized !");
}
public ActionResult SomeOtherAction()
{
if(AuthorizeHelper.HasPermission("role 2"))
{
return Content("Authorized !");
}
return Content("401 Not Authorized !");
}
And as said, it can be used in Razor views by calling it normally
#if(AuthorizeHelper.HasPermission("role 2")) {
//...
}
Thanks

Get Session from HttpActionContext

I'm trying to create a permission attribute to configure in each action of my controllers so this custom attribute should take the sessionId from the user.
My code is like that:
public class PermissionChecker: ActionFilterAttribute
{
private int _permissionId { get; set; }
private IUserSelectorService _userService { get; set; }
public PermissionChecker(int permissionId)
{
_permissionId = permissionId;
_userService = new UserSelectorService();
}
public PermissionChecker(int permissionId, IUserSelectorService userService)
{
_permissionId = permissionId;
_userService = userService;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (_userService.HasPermission(_permissionId, /* here I must pass the session["Id"]*/)){
base.OnActionExecuting(actionContext);
return;
}
throw new HttpException(401, "Unauthorized");
}
}
Use this
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(filterContext.HttpContext.Session != null)
{
var id = filterContext.HttpContext.Session["Id"];
}
}
EDIT
Given the fact that you're using MVC 4 and you don't have
public override void OnActionExecuting(ActionExecutingContext filterContext)
Try using
System.Web.HttpContext.Current.Session
if you are trying to access using ActionFilterAttribute then OnActionExecting event it wont give the accessibility of HttpContext with System.Web.Http.
Instead of that If you are trying to access using System.Web.Mvc it will provide you the current session with onActionExecting event with help of ActionExecutingContext class.

How to implement IIdentity for a custom User object in ASP.NET MVC?

In my ASP.NET MVC app, I'm trying to create a custom HttpContent.User object. I've started by creating a Member class, which implements IPrincioal.
public class Member : IPrincipal
{
public string Id { get; set; }
public IIdentity Identity { get; set; }
public bool IsInRole(string role) { throw new NotImplementedException(); }
...
}
Then at authentication time I set HttpContext.User to an instance of a Member class:
FormsAuthentication.SetAuthCookie(email, false);
HttpContext.User = member;
Then later I want to check if the user is authenticated, like so:
if (User.Identity.IsAuthenticated) { ... }
That's where I'm stuck. I'm not sure what I need to do for the public IIdentity Identity property on the instance of the Member. So that I can use the HttpContext.User object something like this:
IsAuthenticated = HttpContext.User.Identity.IsAuthenticated;
ViewBag.IsAuthenticated = IsAuthenticated;
if (IsAuthenticated) {
CurrentMember = (Member)HttpContext.User;
ViewBag.CurrentMember = CurrentMember;
}
A Principal is not something you can just set once when writing the auth cookie and forget later. During subsequent requests, the auth cookie is read and the IPrincipal / IIdentity is reconstructed before executing an action method. When that happens, trying to cast the HttpContext.User to your custom Member type will throw an exception.
One option would be to intercept in an ActionFilter, and just wrap the standard implementation.
public class UsesCustomPrincipalAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var systemPrincipal = filterContext.HttpContext.User;
var customPrincipal = new Member(systemPrincipal)
{
Id = "not sure where this comes from",
};
filterContext.HttpContext.User = customPrincipal;
}
}
public class Member : IPrincipal
{
private readonly IPrincipal _systemPrincipal;
public Member(IPrincipal principal)
{
if (principal == null) throw new ArgumentNullException("principal");
_systemPrincipal = principal;
}
public string Id { get; set; }
public IIdentity Identity { get { return _systemPrincipal.Identity; } }
public bool IsInRole(string role)
{
return _systemPrincipal.IsInRole(role);
}
}
This way, you're not losing anything that comes out of the box with the default IPrincipal and IIdentity implementations. You can still invoke IsAuthenticated on the IIdentity, or even IsInRole(string) on the IPrincipal. The only thing you're gaining is the extra Id property on your custom IPrincipal implementation (though I'm not sure where this comes from or why you need it).

Session handling with RavenDB and ASP.NET MVC

I have a service class UserService that gets an instance of IDocumentStore injected using AutoFac. This is working fine but now I'm looking at code like this:
public void Create(User user)
{
using (var session = Store.OpenSession())
{
session.Store(user);
session.SaveChanges();
}
}
Every action that writes to the db uses this same structure:
using (var session = Store.OpenSession())
{
dosomething...
session.SaveChanges();
}
What is the best way to eliminate this repetitive code?
The easiest way is implementing OnActionExecuting and OnActionExecuted on a base controller and use it.
let's imagine you create your RavenController like this:
public class RavenController : Controller
{
public IDocumentSession Session { get; set; }
protected IDocumentStore _documentStore;
public RavenController(IDocumentStore documentStore)
{
_documentStore = documentStore;
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
Session = _documentStore.OpenSession();
base.OnActionExecuting(filterContext);
}
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
using (Session)
{
if (Session != null && filterContext.Exception == null)
{
Session.SaveChanges();
}
}
base.OnActionExecuted(filterContext);
}
}
then all you need to do in your own controllers is inherit from RavenController like this:
public class HomeController : RavenController
{
public HomeController(IDocumentStore store)
: base(store)
{
}
public ActionResult CreateUser(UserModel model)
{
if (ModelState.IsValid)
{
User user = Session.Load<User>(model.email);
if (user == null) {
// no user found, let's create it
Session.Store(model);
}
else {
ModelState.AddModelError("", "That email already exists.");
}
}
return View(model);
}
}
Interesting enough, I have found a blog post showing exactly this technique ...
it does explain way more that what I did. I hope it helps you better
Building an ASP.NET MVC app using RavenDB as a Backing Store

Resources