How to handle security/authentication on a DNN-based web API

MysteriousWhisper picture MysteriousWhisper · Apr 16, 2013 · Viewed 8.8k times · Source

I am building a REST API for a DotNetNuke 6 website, making use of DNN's MVC-based Services Framework. However, I don't have any background in authentication, so I'm not even sure where to start.

Basically, we want our clients to be able to make GET requests for their portal's data, and we want some clients (but not all) to be able to POST simple updates to their user data.

I've been trying to search for information, but the trouble is I'm not sure what I'm searching for. DNN has different logins and roles, but I'm not sure if or how they factor in. I've heard of things like oAuth but my understanding of it is at the most basic level. I don't know if it's what I need or not and if or how it applies to DNN. Can anyone point me in the right direction?

UPDATE: Based on the answer below about tying it with a module and further research, here is what I have done:

I created a module just for this service, and I added two special permissions for it: "APIGET" and "APIPOST." I assigned these to some test roles/test accounts in DNN. I wrote a custom authorize attribute that, given the module ID, checks if the current user has the necessary permission (either through roles or directly). As far as I can tell, tab ID is irrelevant in my case.

It appears to be working both with a web browser (based on the DNN account I'm logged into) and with a php script that sends an HTTP request with an account username/password.

The authorize attribute:

using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Security;
using DotNetNuke.Security.Permissions;
using System.Web;

public class MyAuthorize : DotNetNuke.Web.Services.AuthorizeAttributeBase
{
    public const string AuthModuleFriendlyName = "MyAuthModule";
    public const string GETPermission = "APIGET";
    public const string POSTPermission = "APIPOST";

    public string Permission { get; set; }

    protected override bool AuthorizeCore(HttpContextBase context)
    {
        ModuleController mc = new ModuleController();

        ModuleInfo mi = mc.GetModuleByDefinition(PortalController.GetCurrentPortalSettings().PortalId, AuthModuleFriendlyName);

        ModulePermissionCollection permCollection = mi.ModulePermissions;

        return ModulePermissionController.HasModulePermission(permCollection, Permission);
    }
}

The controller: ("mytest" is the endpoint for both GET and POST)

public class MyController : DnnController
{
    [ActionName("mytest")]
    [AcceptVerbs(HttpVerbs.Get)]
    [DnnAuthorize(AllowAnonymous = true)]
    [MyAuthorize(Permission = MyAuthorize.GETPermission)]
    public string myget(string id = "")
    {
        return "You have my permission to GET";
    }

    [ActionName("mytest")]
    [AcceptVerbs(HttpVerbs.Post)]
    [DnnAuthorize(AllowAnonymous = true)]
    [MyAuthorize(Permission = MyAuthorize.POSTPermission)]
    public string mypost(string id = "")
    {
        return "You have my permission to POST";
    }
}

Answer

bdukes picture bdukes · Apr 17, 2013

The main way that you tie a service in the DNN Services Framework into DNN permissions is to associate the permissions with a module instance. That is, you'll require users of your service to identify which module they're calling from/about (by sending ModuleId and TabId in the request [headers, query-string, cookies, form]), then you can indicate what permissions they need on that module to take a particular action on the service.

You can use the SupportedModules attribute on your service, and pass in a comma-delimited list of module names, to ensure that only your own modules are being allowed. Then, add the DnnModuleAuthorize attribute at the service or individual action level to say what permission the user needs on that module. In your instance, you can also add the AllowAnonymous attribute on the GET actions, and have one DnnModuleAuthorize on the service, for the POST methods (and anything else). Note that you cannot put the AllowAnonymous attribute on the controller; it will override authorizations put at the action, making it impossible to make actions more restrictive.

You'll also want to add the ValidateAntiForgeryToken attribute on the POST actions, to protect against CSRF attacks.

If you don't have a module that naturally associates its permissions with your service, you can create one just for that purpose, solely to expose itself as a permissions management utility.

Once you've figured out the authorization piece above, DNN will take care of authentication using your forms cookie (i.e. AJAX scenarios are taken care of automatically), or via basic or digest authentication (for non-AJAX scenarios). That said, if you're doing non-AJAX, you'll need to figure out a way to validate the anti-forgery token only when it applies.