C# Custom authorization with Web API

243 views Asked by At

I'm designing a WebAPI project and my first controller is the LoginController. The user logs in as follows:

    [ActionName("Login")]
    [AcceptVerbs("PUT")]
    [HttpPost]
    public HttpResponseMessage Login(DTO.LoginDetails loginDetails)
    {
        HttpResponseMessage response = null;
        DTO.User user = null;

        try
        {
                string connectionString = string.Empty;

                //Login the user
                user = Mapper.Map<DTO.User>(loginService.Login(loginDetails.Username,
                                                               Md5.Encrypt(loginDetails.Password));

                if (user != null)
                {
                    //Create session for the user
                    DTO.Session session = new DTO.Session()
                    {
                        User = user,
                        Token = Guid.NewGuid().ToString()
                    };

                    //Create collection to store the cookie values
                    var nv = new NameValueCollection(3);
                    nv[SessionHandler.UserId] = user.Id.ToString();

                    //Create a cookie
                    var cookie = new CookieHeaderValue(SessionHandler.Session, nv)
                    {
                        Expires = DateTimeOffset.Now.AddDays(1),
                        Domain = Request.RequestUri.Host,
                        Path = "/"
                    };

                    response = Request.CreateResponse<DTO.Session>(HttpStatusCode.OK, session);
                    response.Headers.AddCookies(new CookieHeaderValue[] { cookie });
                }

                //Login failed
                else
                {
                    response = Request.CreateErrorResponse(HttpStatusCode.Unauthorized, new HttpError("User or connectionstring is null or empty"));
                }
            }
        }
        catch (Exception ex)
        {
            response = Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
            logService.Logger().Error("Login failed", ex);
        }

        return response;
    }

I also have a DelegatingHandler that gets the above cookie from each request. Currently it just gets the cookie from the request.

The users are stored in the database table, and they are validated against this table. Each user in the table has 4 explicit rights (CRUD) to determine whether he can perform an action on an entity (customers, products, etc).

So my questions:

  1. Is it a good idea to store the user in MemoryCache after successful login so that I avoid a roundtrip to the database for verifying that the user has been authenticated?

  2. How can I use a custom Authorize attribute to make sure that the user has the rights to perform an action on the entity? Should this code be at the Controller or Service level?

  3. The Login controller is decorated with [AllowAnonymous], whilst all other controllers aren't. However my DelegatingHandler is system-wide. Should I move my controllers into their own area and leave the login controller in the main area?

1

There are 1 answers

5
lavrik On
  1. Sure. It's good idea to have users cache and get user from cache first, and if missed then from database (by key stored in cookies and db - like LastSessionId).
  2. It's better yor User (or Session in your code) class to derive from GenericPrincipal interface and override/add some code to manage Roles collection. So IsInRole method will work for you (used by AuthorizeAttribute). IUserPrincipal is your own interface to add the desired behaviuor/data. So doesn't matter it's custom attribute or not it will work properly at Controller level

     class UserPrincipal : GenericPrincipal, IUserPrincipal
    
  3. I think no :)