ApiController with CORS does not like Anonymous requests - asp.net-mvc

I'm hosting an ASP.NET MVC project in Azure web apps.
In this project I'm using an ApiController to serve data to a client program.
This Api controller has a method defined as such:
[AllowAnonymous]
[RoutePrefix("api/v1/search")]
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class CompanyDataController : APIController
{
[Route("companies")]
public string CompanySearch(string request)
{
return "well hello there beautiful";
}
}
When I try to make requests to this controller after its been published to Azure I get this error:
"The HTTP request was forbidden with client authentication scheme 'Anonymous'."
I can access the rest of the website without issue.
I have tried to allow anonymous access with a few variations of this in <system.web>:
<authentication mode="None" />
<authorization>
<allow users="?"/>
</authorization>
But that has made no difference.. any bright ideas?

From the link I posted in comments above... (wouldn't let me mark this as duplicate since the answer below wasn't marked as an answer!)
Since there is already an existing custom authorization filter on the class/controller level, therefore, to override a specific action handler (the method) and have it work without any authorization filters, we need to override the filter at the controller/class level. So adding the OverrideAuthorization filter did the trick. Now AllowAnonymous will be to do its magic.
[Route("api/accounts/{accountNumber}/GetX")]
[AllowAnonymous]
[OverrideAuthorization]
[HttpGet]
public HttpResponseMessage GetX(string accountNumber)
{
// Process
// ..
// ..
}

Related

Blocked Webhook

I have a messaging webhook setup in Twilio that does a post to a URL on a server on Azure in the format https://******.*****corps.org:441/SMS The controller has been tested using NGrok locally and works great, in Azure when I use the get by typing in the URL I am able to get a response from the web server no problem, but the post from Twilio gets a 11200 retrieval failure. Is there something that would block the cross domain post on IIS that I am unaware of?
''' public class SMSController : TwilioController
{
[HttpPost]
public TwiMLResult Index(SmsRequest request)
{
var response = new MessagingResponse();
UserProfile _userProfileFrom = UserProfileService.GetByTwilioFromPhone(request.From);
...
return TwiML(response);
}
[HttpGet]
public ActionResult Index() //works fine..
{
return View();
}
}'''
Thanks for the edit Shoaib K. I found the problem using Postman (database connectivity error in my code). I was able to create a manual post and setting the following in the Web.config file (ASP.NET):
<compilation debug="true" targetFramework="4.7.2" />
<customErrors mode="Off"></customErrors>```

How to intercept a non SSL connection in onAuthorization method?

I am using MVC4, ASP.NET 4.5, C#
I want to add code to my onAuthorization method in global.asa to identify whether the connection is SSL or not, if not then to issue a permanent redirect to a SSL domain. I am using another domain that is SSLed.
In my Login controller I have code along the lines of :
[HttpGet]
public virtual ActionResult LogOn(string Error="")
{
if (Request.IsSecureConnection)
{
return View(viewModel);
}
else
{
return RedirectPermanent("https://www.mysslapp.com/logon");
}
}
I want to add this same functionality to the onAuthorization method so that when actions, covered by the [authorize] filter are called then they must also be accessed by a SSL connection. So I believe my global.asa code needs changing. However it will not accept "Request.IsSecureConnection", as the context is different.
My "pseudo" Global.asa onAuthorization routine is:
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (Request.IsSecureConnection)
{
base.OnAuthorization(filterContext);
}
else
{
RedirectPermanent("https://www.mysslapp.com/logon");
}
}
The above will not work, but it describes what I am trying to achieve. I would appreciate help on how I need to change the above code to make it work, such that any "adventurous" use of urls, on a non SSL connection will automatically redirect to the SSL site logon page.
Thanks in advance.
EDIT1
Think I have the first bit:
filterContext.HttpContext.Request.IsSecureConnection
EDIT2
if (filterContext.HttpContext.Request.IsSecureConnection)
{
base.OnAuthorization(filterContext);
}
else
{
filterContext.Result = new RedirectResult("https://www.mysslapp.com");
}
I think your main issue is that you have 2 separate concerns and you are trying to achieve both in one go. Your 2 concerns are:
Making every URL of domain A 301 redirect to the same URL on domain B
Making domain B redirect all requests to HTTPS
The first one is really easy. Create a new IIS site for domain A, install the IIS rewrite module, add this web.config to the site, and then adjust your DNS (if necessary) to make the site live.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<httpRedirect
enabled="true"
destination="https://www.mysslapp.com$V$Q"
exactDestination="true"
httpResponseStatus="Permanent" />
<httpProtocol>
<redirectHeaders>
<!-- This is to ensure that clients don't cache the 301 itself -
this is dangerous because the 301 can't change when put in place
once it is cached -->
<add name="Cache-Control" value="no-cache"/>
</redirectHeaders>
</httpProtocol>
</system.webServer>
</configuration>
NOTE: The above configuration is for IIS 7.5. I am not sure if it will work on other versions of IIS.
Now none of the users of domain B will incur the performance hit of the redirect rule, so all is good.
For redirecting your users of domain B to HTTPS, you should not use 301. Why? Because not all browsers respond to 301.
You should also not only allow HTTPS on the domain, but allow both HTTP and HTTPS. Why? Because your users that type myssldomain.com will get an ugly error message instead of a fast redirect to you HTTPS protected site.
So the simple solution to making your whole site redirect to HTTPS is to use the RequireHttps attribute and register it as a global filter. The RequireHttpsAttribute uses a 302 redirect when a request comes in that is not secure.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new RequireHttpsAttribute());
filters.Add(new HandleErrorAttribute());
}
}
As for the AuthorizeAttribute, you should leave that out of the equation altogether unless you need some customization that deals with authorization.

Windows Authentication IsAuthenticated is false

Have an MVC5 project using Windows authentication where User.Identity.Name randomly turns up empty. The site needs to display public facing and secure pages. Anonymous authentication is enabled in IIS and set to Application Pool Identity. The behavior is pretty random but most commonly repeated by navigating away from home and back again (if I sit there and click a home link it happens about 1 every 10 or so clicks) There's no special sauce in the web.config or controller action:
Web.Config
<system.web>
<authentication mode="Windows" />
</sytem.web>
HomeController
public class HomeController : BaseController
{
protected IMailer _mailer;
public HomeController(INLogger logger, IMailer mailer) : base(logger) {
this._mailer = mailer;
}
public ActionResult Index()
{
return View();
}
}
Threads out there say to use Request.SeverVariables["LOGON_USER"], but this turns up empty too.
Has me baffled, any insight would get great. Thanks!

Web Api 2 global route prefix for route attributes?

I'd like to expose a company's api by two ways:
api.company.com (pure WebApi web site)
company.com/api (add WebApi to existing MVC5 company site)
So, I placed models/controllers in a separate assembly and reference it from both web sites.
Also, I use route attributes:
[RoutePrefix("products")]
public class ProductsController : ApiController
Now, the controller above can be accessed by:
api.company.com/products which is good
company.com/products which I'd like to change to company.com/api/products
Is there a way to keep using route attributes and setup MVC project so it adds "api" for all routes?
So this is probably not the only way you could do it, but this is how I would do it:
Create your own Attribute that inherits from RoutePrefixAttribute
Override the Prefix property and add some logic in there to prepend "api" to the prefix if running on the desired server.
Based on a setting in your web.config, prepend to the route or not.
public class CustomRoutePrefixAttribute : RoutePrefixAttribute
{
public CustomRoutePrefixAttribute(string prefix) : base(prefix)
{
}
public override string Prefix
{
get
{
if (Configuration.PrependApi)
{
return "api/" + base.Prefix;
}
return base.Prefix;
}
}
}
EDIT
(The below option is no longer supported as of Web API 2.2)
Alternatively you could also specify more than one route prefix:
[RoutePrefix("api/products")]
[RoutePrefix("products")]
public class ProductsController : ApiController
You can use Map on IAppBuilder
So Startup class will looks something like this
class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/api", map =>
{
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
map.UseWebApi(config);
});
}
}
Another option would be to forward traffic from one site to your Web API. Are you set on hosting two instances? If not, you could host only the one instance under say api.company.com/products. On your MVC company site implement an HttpHandler to redirect all traffic matching /api/* to api.company.com:
a. Create the handler in your MVC app:
public class WebApiHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string url = "api.company.com" + context.Request.RawUrl.Replace("/api","");
//Create new request with context.Request's headers and content
//Write the new response headers and content to context.Response
}
}
b. Register the handler in your web.config:
<configuration>
<system.web>
<httpHandlers>
<add verb="*" path="api/*" type="Name.Space.WebApiHandler" validate="false" />
</httpHandlers>
</system.web>
</configuration>
c. Enable CORS in your Web API if you haven't done so already.
You can just implement your api service as www.mycompany.com/api.
Then use UrlRewrite to map api.mycompany.com to www.mycompany.com/api
We even support this method of UrlRewrite in link generation, so if you generate links from the api.mycompany.com, your links will point to api.mycompany.com/controller/id.
Note that this is the only form of URL rewrite that works correctly for MVC link generation (api.xxx.yyy -> www.xxx.yyy/api)

asp.net mvc - limit access to web pages

Greetings,
in my asp.net mvc application what i would like to do is to enable access to some pages only after user was successfully authorized. I have already created custom membership provider and that works fine. How can I, in web config create such rule - for instance for all pages in ~Admin/ folder? I don't want to create on every controller's action the validation code.
For now i have in my web.config the following statement:
<location path="~/Admin">
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
but it doesn't work.
Doing authorization logic in config files has one big disadvantage: it cannot be easily unit tested, and something so important as authentication should be unit tested. I would recommend you for this matter to write a custom authorization filter which could be used to decorate a base controller for all admin actions that requires authentication:
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Class,
Inherited = true
)]
public class RequiresAuthenticationAttribute
: FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectResult(
string.Format("{0}?ReturnUrl={1}",
FormsAuthentication.LoginUrl,
filterContext.HttpContext.Request.Url.AbsoluteUri
)
);
}
}
}
And your admin controller:
[RequiresAuthentication]
public class AdminController : Controller
{
// .. some actions that require authorized access
}

Resources