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

5

8

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";
    }
}
Gough answered 16/4, 2013 at 19:0 Comment(1)
Kindly check this might be helpful #12223027Achromat
D
8

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.

Dresser answered 17/4, 2013 at 13:19 Comment(2)
I have figured out how to write a DNN module with custom permissions. But I am new to DNN and still not clear on how our clients will get authenticated when programmatically connecting to the API, and how this ties in with the module. I have no idea where AJAX comes into play. Could you be more specific?Gough
The Example Projects folder in github.com/bdukes/Designing-a-Mobile-Enabling-API has a module and a Windows Phone appDresser
K
5

The Services Framework in DNN is what you are after. It allows you to provide a REST API that plugs directly into DNN security.

Here are some articles to help you get started:

Note, there are some difference in DNN 6 and DNN 7 when using the Services Framework:

Kheda answered 17/4, 2013 at 9:6 Comment(0)
D
2

Just wanted to note that the DnnModuleAuthorize attribute takes a PermissionKey parameter for custom permissions so you can do stuff like this:

    [DnnModuleAuthorize(PermissionKey = "DELETEDATA")]
    [HttpPost]
    public HttpResponseMessage DeleteData(FormDataCollection data)

It doesn't look like you can supply your own error message with this so you might to wrap your method body like this instead and leave off the custom permission attribute:

    [DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.View)]
    [HttpPost]
    public HttpResponseMessage DeleteData(FormDataCollection data)
    {
        var errorMessage = "Could not delete data";

        if (ModulePermissionController.HasModulePermission(ActiveModule.ModulePermissions,"DELETEDATA"))
        {
            // do stuff here
        }
        else
        {
            errorMessage = "User does not have delete permission";
        }

        var error = new HttpResponseMessage(HttpStatusCode.BadRequest)
        {
            Content =
                new StringContent(
                errorMessage)
        };
        return error;
    }
Decline answered 16/9, 2014 at 17:55 Comment(0)
M
2

Just wanted to add to @Richards comment for using the [DnnModuleAuthorize(PermissionKey = "DELETEDATA")] for custom permissions.

The full attribute should be:

[DnnModuleAuthorize(PermissionKey = "DELETEDATA", AccessLevel = SecurityAccessLevel.Edit)]

Leaving it blank does nothing as shown here: https://github.com/dnnsoftware/Dnn.Platform/blob/f4a5924c7cc8226cfe79bbc92357ec1a32165ada/DNN%20Platform/Library/Security/Permissions/PermissionProvider.cs#L810

Misgovern answered 3/6, 2015 at 23:3 Comment(1)
Interesting that you have to pass AccessLevel = edit , to make the permissionKey working, I guess PermissionKey should work by itself, may be DNN need to improve itConnective
E
0

I guess you require a plugin that allows you to construct GET and POST APIs. you can use this plugin I found on the DNN store. https://store.dnnsoftware.com/dnn-rest-api-custom-api-authentication-authorization.

Ecospecies answered 6/6, 2022 at 9:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.