Custom Authorization - Web Api - asp.net-web-api

I have a web api for which i would need to implement custom authentication and authorization. The authorization should be defined by Resource and Action as shown below:
[Authorize("users","view")]
public async Task<IHttpActionResult> GetAsync()
{
}
Is there any way i can use custom authorization filter and implement this?
Also, the web api is protected by client certificate and the caller is identifier by key which is passed in the request header. The Key is authenticated using a custom authentication filter.
Regarads,
John

Yes you can create custom Authorization and put on controller or method that needs to be authorized.
sample example is as below
public class CustomAuthorize : System.Web.Http.AuthorizeAttribute
{
private string Resource { get; set; }
private string Action { get; set; }
public CustomAuthorize(string resource, string action)
{
Resource = resource;
Action = action;
}
public override void OnAuthorization(
System.Web.Http.Controllers.HttpActionContext actionContext)
{
base.OnAuthorization(actionContext);
//Check your post authorization logic using Resource and Action
//Your logic here to return authorize or unauthorized response
}
}
Here you can do your custom authorization logic and sample controller will look like this
public class DoThisController : ApiController
{
[CustomAuthorize("users","view")]// for specific methods
public string Get()
{
return "Sample Authorized";
}
}

Related

WebApi HTTPPOST Endpoint not being hit

I have the following simple HTTPPOST endpoint;
[AllowAnonymous]
[HttpPost]
[Route("forgotPassword")]
public IHttpActionResult ForgotPassword(string userName, string callbackUrl)
Where the controller is decorated as follows;
[Authorize]
[RoutePrefix("api/accounts")]
public class AccountsController : ApiController
Now when i try to test this endpoint in postman, using the following url;
http://localhost:11217/api/accounts/forgotPassword
with the strings in the body of the message
I get the following return.
{ "Message": "No HTTP resource was found that matches the request
URI 'http://localhost:11217/api/accounts/forgotPassword'.",
"MessageDetail": "No action was found on the controller 'Accounts'
that matches the request." }
Now I would rather not have to create a model for the two strings (if possible). Also if I try to put the params in the query string I get a potantially dangerous request response
http://localhost:11217/api/accounts/forgotPassword/test&callbackUrl=local
Can anyone help please?
If you want to send mulitple parameters when doing a post request you should create a DTO that contains the parameters as
public class forgetPasswordDTO
{
public string userName { get; set; }
public string callbackUrl { get; set; }
}
Then add the DTO as a method parameter with the [FromBody]
[AllowAnonymous]
[HttpPost]
[Route("forgotPassword")]
public IHttpActionResult ForgotPassword([FromBody] forgetPasswordDTO data)
And in you client, create the object as
var data = {
'userName': user,
'callbackUrl': url
};
And add it to the body of the request.
Here's a nice article about this topic

How to access Identity UserManager in Web API Controller to Send an Email

I am using Identity v2 and need to send an Email from Web Api Controller using the UserManager.SendAsync method which is OWIN Middleware component. But I don't know how do I access the UserManager itself in Web Api Controller Method. I am trying a similar approach like a Regular MVC controller but usermanager always null. Any suggestion please?
public class MyApiController: ApiController
{
public MyApiController()
{
}
public MyApiController(ApplicationUserManager userManager)
{
UserManager = userManager;
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
public void SendEmail()
{
_userManager.SendAsync(...);
}
}
The constuctor that takes a ApplicationUserManager will never be called with your current solution. Change your empty constructor to call your other constructor.
public class MyApiController: ApiController
{
public MyApiController()
: this(new ApplicationUserManager())
{
}
THE REST OF YOUR IMPLEMENTATION GOES HERE
}
Check whether you have correctly configured UserManager from Startup class
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.DataProtection;
using Owin;
namespace Identity_PasswordPolicy
{
public partial class Startup
{
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
///...
// Configure the UserManager
app.UseUserManagerFactory(new IdentityFactoryOptions<ApplicationUserManager>()
{
DataProtectionProvider = app.GetDataProtectionProvider(),
Provider = new IdentityFactoryProvider<ApplicationUserManager>()
{
OnCreate = ApplicationUserManager.Create
}
});
/// ...
}
}
}
Like here in Startup.Auth class from asp.net identity samples
https://aspnet.codeplex.com/SourceControl/latest#Samples/Identity/Identity-PasswordPolicy/Identity-PasswordPolicy/App_Start/Startup.Auth.cs
Startup is partial class and method ConfigureAuth by link is called from https://aspnet.codeplex.com/SourceControl/latest#Samples/Identity/Identity-PasswordPolicy/Identity-PasswordPolicy/Startup.cs
In sample it is called from owin startup method, but depending from hosing it could be called from global.asax.cs

webapi actionfilters, how to inject a value when using different argument types that inherit from a base type

I have a base request type..
class RequestBase
{
public string inputId;
public string derivedid;
}
and types that inherit ..
class RequestA : RequestBase
{
public string name;
}
and
class RequestB : RequestBase
{
public string color;
}
I have a webapi service, some actions take an input parameter of RequestA, some take RequestB
[HttpPost]
[MyFilter]
[ActionName("Process1")]
public HttpResponseMessage Process1(RequestA request)
{
//do something with request.derivedId
}
[HttpPost]
[MyFilter]
[ActionName("Process2")]
public HttpResponseMessage Process2(RequestB request)
{
//do something with request.derivedId
}
I have an actionfilter that takes the inputId from the request and generates a derivedId
public override void OnActionExecuting(HttpActionContext actionContext)
{
RequestBase request = (RequestBase)actionContext.ActionArguments["request"];
string inputId = request.inputId;
string derivedId = inputId + "123";
// ?? somehow inject derivedId back into the actionContext so that my controller methods can access?
}
As my comment states above, I'd like to populate the derivedId field and have it accessible to my controller methods.
Thanks in advance
There's a few solutions to this problem already described in this thread - one of them should suit you:
ASP.NET MVC Pass object from Custom Action Filter to Action

Passing ViewModel to Web-Api action

Is it possible to pass an ViewModel object to WebApi controller action instead of separate params?
Instead of using:
public class ContactsController : ApiController
{
public IEnumerable<Contact> GetContacts(string p1, string p2)
{
// some logic
}
}
I would like to use:
public class ContactsController : ApiController
{
public IEnumerable<Contact> GetContacts(TestVM testVM)
{
// some logic
}
}
public class TestVM
{
public string P1 { get; set; }
public string P2 { get; set; }
}
This doesn't seem to work for me. When I call /api/contacts/?P1=aaa&P2=bbb the testVM object doesn't get populated (null).
Also, I would like the TestVM to have valdiation attribtues defined and use ModelState.IsValid in my API controller.
Unless told otherwise WebApi will deserialise complex models using the content/body of the request. To tell WebApi to use the Url to construct the model you need to specify the [FromUri] attribute:
public IEnumerable<Contact> GetContacts([FromUri]TestVM testVM)
{
// some logic
}
I know it's kind of late to post another answer but I thought it could be useful for anyone who uses .net core as a web API service
public IEnumerable<Contact> GetContacts([FromQuery]TestVM testVM)

MVC3 REST service - how do I access the request body content for a PUT or POST request?

I am creating an ASP.NET MVC3 restful web service to allow reports to be uploaded from a set of servers. When a new report is created, I want the client app to do a PUT to
http://MyApp/Servers/[ServerName]/Reports/[ReportTime]
passing the content of the report as XML in the body of the request.
My question is: how do I access the content of the report in my controller? I would imagine that it is available somewhere in the HttpContext.Request object but I am reluctant to access that from my controller as it is not possible(?) to unit test that. Is it possible to tweak the routing to allow the content to be passed as one or more parameters into the controller method? The outcome needs to be RESTful, i.e. it has to PUT or POST to a URL like the one above.
Currently my routing is:
routes.MapRoute(
"SaveReport",
"Servers/{serverName}/Reports/{reportTime",
new { controller = "Reports", action = "Put" },
new { httpMethod = new HttpMethodConstraint("PUT") });
Is there any way to modify this to pass content from the HTTP request body into the controller method?
The controller method is currently:
public class ReportsController : Controller
{
[HttpPut]
public ActionResult Put(string serverName, string reportTime)
{
// Code here to decode and save the report
}
}
The object I am trying to PUT to the URL is:
public class Report
{
public int SuccessCount { get; set; }
public int FailureOneCount { get; set; }
public int FailureTwoCount { get; set; }
// Other stuff
}
This question looks similar but doesn't have any answer.
Thanks in advance
Seems like you just need to use the standard ASP.NET MVC model binding capability with the slight wrinkle that you would doing an HTTP PUT instead of the more common HTTP POST. This article series has some good samples to see how model binding is used.
Your controller code would then look like:
public class ReportsController : Controller
{
[HttpPut]
public ActionResult Put(Report report, string serverName, string reportTime)
{
if (ModelState.IsValid)
{
// Do biz logic and return appropriate view
}
else
{
// Return invalid request handling "view"
}
}
}
EDIT: ====================>>>
Jon added this code to his comment as part of the fix so I added it to the answer for others:
Create a custom ModelBinder:
public class ReportModelBinder : IModelBinder
{
public object BindModel(
ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
var xs = new XmlSerializer(typeof(Report));
return (Report)xs.Deserialize(
controllerContext.HttpContext.Request.InputStream);
}
}
Modify the Global.asax.cs to register this model binder against the Report type:
ModelBinders.Binders[typeof(Report)] = new Models.ReportModelBinder();

Resources