AngularJS WebApi Authorization header does not appear to be getting passed - angularjs

I am trying to create a call with AngularJS v1.3.15 into ASP.NET WebAPI (latest from Nuget) to get a list of customers. I can successfully authenticate and get back a token. I add the token to an Authentication header but when I make the call it gets kicked back saying the Authentication header is missing.
The Angular call after I get the token looks like this
$scope.baseUrl = "http://localhost:7800/";
$http({
method: 'GET',
url: $scope.baseUrl + 'customer',
headers: {
'Authorization': $scope.token
}})
I have also tried to utilize the angularjs $resourse
return $resource($scope.baseUrl + 'customer', { }, { 'get': { method: 'GET', isArray: true, headers: { 'Authorization': $scope.token } } });
In the WebApiConfig Register method I have the following setup
var cors = new EnableCorsAttribute("*","*","*");
config.EnableCors(cors);
in a DelegatingHandler I check for the Authorization header like so
var authHeader = request.Headers.Authorization;
From the Angular app it is always null. If I run a check from Fiddler and PostMan I get the Authorization header just fine.
When I press F12 from Chrome and look at the request header these are the results
OPTIONS /customer HTTP/1.1
Host: localhost:7800
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://localhost:63342
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2376.0 Safari/537.36
Access-Control-Request-Headers: accept, authorization
Accept: */*
Referer: http://localhost:63342/Test/index.html
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8

Fixed my issue, after some searches I found that OPTIONS does not seem to be supported out of the box. I found that if I add NuGet package Microsoft.Owin.Cors and then add
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
to the startup.cs file

Related

AngularJS - Can't pass a header on a $http request

I'm trying to do requests to a RestAPI coded with Codeigniter 3. Access-Control-Allow-Origin and Access-Control-Allow-Headers are set to "*". The API has been successfully tested on Postman.
I'm coding a Web App using AngularJS, and trying to do requests to that API. The following code works properly:
api_service.getUsers = function() {
return $http({
method : 'GET',
url : url + '?dd-api-key=' + api_key
});
}
As this other code functions well too:
api_service.getUsers = function() {
return $http.get(url + '?dd-api-key=' + api_key);
}
But I need to send the API key as a header and not in the URL. So I wrote the function this way:
api_service.getUsers = function() {
return $http({
method: 'GET',
url: url,
headers: {
'accept': undefined,
'dd-api-key': api_key
}
});
}
That code doesn't work. Google Chrome console displays me this:
OPTIONS http://www.myweb.com/api/v1/users
XMLHttpRequest cannot load http://www.myweb.com/api/v1/users. Invalid HTTP status code 403
And at the network tab it shows that the request uses a method type 'OPTIONS', not a 'GET' method.
The Response Headers are:
Access-Control-Allow-Headers:*
Access-Control-Allow-Methods:PUT, GET, POST, DELETE, OPTIONS
Access-Control-Allow-Origin:*
Cache-Control:no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Connection:keep-alive
Content-Language:es-ES
Content-Length:45
Content-Type:application/json; charset=utf-8
Date:Wed, 25 Mar 2015 01:45:09 GMT
Expires:Thu, 19 Nov 1981 08:52:00 GMT
Pragma:no-cache
Server:nginx/1.6.2
And the Request Headers are:
Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:es-ES,es;q=0.8,en;q=0.6
Access-Control-Request-Headers:dd-api-key
Access-Control-Request-Method:GET
Connection:keep-alive
Host:www.myweb.com
Origin:http://127.0.0.1:55652
Referer:http://127.0.0.1:55652/index.html
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36
Excuse me if my english is not correct.
Many thanks.
you should add header with this:
$http.defaults.headers.common.dd-api-key = api_key;
You can add it in some config as global.

CSRF mismatch when POSTing to sails backend from Angular JS

In angular, I obtain a CSRF token like this:
// Get CSRF token and set as header
var csrfRequest = Auth.getCSRF().then(function(data){
console.log(data.data._csrf);
$rootScope.csrf = data.data._csrf;
});
Which logs the new token to the console (this works fine).
Then, I try to login to a sails.js api. Here's the request:
POST /auth/login HTTP/1.1
Host: localhost:1337
Connection: keep-alive
Content-Length: 108
Pragma: no-cache
Cache-Control: no-cache
Accept: application/json, text/plain, */*
Origin: http://localhost
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36
Content-Type: application/json;charset=UTF-8
Referer: http://localhost/sails-front/src/login/
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6
Request Payloadview parsed
{"email":"myemail#email.com","password":"mypass","_csrf":"PIlVO7S362OroPGBSG0X1vW2FydkP9VhK8cMk="}
The _csrf field is the same as that which was received in the getCSRF call.
Yet I get the response 'CSRF mismatch'. However, when I try the exact same thing with Postman, it works fine (so I don't think it's a problem with the sails server).
Here's the login code in angular. In the controller:
$scope.login = function() {
Auth.login({
email: $scope.email,
password: $scope.password,
_csrf: $rootScope.csrf
},
function(res) {
$state.go('app.home');
},
function(err) {
$rootScope.error = "Failed to login";
});
};
And the Auth service:
/*********************************
* login
******************************** */
function login(user, success, error) {
$http.post(API_BASE_URL + 'auth/login', user).success(function(user){
changeUser(user);
success(user);
}).error(error);
}
You posted your full request, and the answer is hidden there in plain sight--not by looking at what is being sent, but what is being omitted: namely, the cookie. The CSRF token is valid for a single Sails session, but you are neglecting to send the cookie with your AJAX request, so Sails has no idea which session the CSRF token you're sending is for.
To tell Angular to send cookies with your request, use the withCredentials setting:
$http.post(API_BASE_URL + 'auth/login', user, {withCredentials: true})

AngularJS http request doesn't send session cookie for CORS

My script is:
<script>
function eventController($scope, $http) {
$http.get("http://example.com/api/Event", {withCredentials: true})
.success(function (response) {
$scope.events = response;
});
}
</script>
Request header from Fiddler:
GET http://example.com/api/Event HTTP/1.1
Host: example.com
Connection: keep-alive
Cache-Control: max-age=0
Accept: application/json, text/plain, */*
Origin: http://www.example.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
Referer: http://www.example.com/Home/Event
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
I need a authentication session cookie to be sent with this request.
It only sends cookie when the url of the webpage has no www in the front.
I have found a similar question in this site and the answer is:
.config(function ($routeProvider, $httpProvider) {
$httpProvider.defaults.withCredentials = true;
//rest of route code
But where do I put this code in my script?
The web application I'm developing is an ASP.NET MVC5 app with Web Api 2.
That configuration needs to be injected into the angular module, like this:
// get an existing module by name
var app = angular.module('app');
// inject $httpProvider configuration
app.config(function ($httpProvider) {
$httpProvider.defaults.withCredentials = true;
});
It turns out that my AngularJS script works fine (Just pass {withCredentials: true} as a parameter in the $http.get method). What went wrong was that my MVC application doesn't store session cookie in the subdomain. So I fixed it by adding CookieDomain in the CookieAuthenticationOptions.

Error 405 when angularjs makes a post request to web api

I have Web Api service hosted on IIS (localhost:81) and my angularjs app running on IIS Express (localhost:8080) is trying to communicate with it.
The angularjs service function is as below
function Register(username, password) {
var data = { "username": username, "password": password };
var promise = $http.post('http://localhost:81/api/Account', JSON.stringify(data));
return promise;
}
My Web Api service method looks like
public class AccountController : ApiController
{
[HttpPost]
public bool Post(User user)
{
Core.DataContracts.AuthenticationResponse authResponse = Account.CreateUser(Mapper.Map<Core.DataContracts.User>(user));
return (authResponse.Code == Core.DataContracts.ResponseCode.Created);
}
}
I read on various forum that it could be to do with CORS but I have got Access-Control-Allow-Origin in my web.config
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
Also, I am able to make a GET request but having this issue with the POST request.
Details from Fiddler are:
OPTIONS http://localhost:81/api/Account HTTP/1.1
Host: localhost:81
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.101 Safari/537.36
Access-Control-Request-Headers: accept, content-type
Accept: */*
Referer: http://localhost:8080/app/index.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6
I tried making the request directly from Fiddler which results in a success
POST http://localhost:81/api/Account HTTP/1.1
User-Agent: Fiddler
Host: localhost:81
Content-Length: 40
Content-Type: application/json
{"username":"abc","password":"asasasas"}
Any help would be appreciated.
I managed to fix it by adding following to WebApiConfig.Register method
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
And removed CORS configuration from Web.config and Startup.cs

Angular POST to Web API doesn't pass data

I've been writing code against ASP.NET Web API for a while now with jQuery and I'm starting something new in Angular (writing against the same Web API backend.)
I'm POSTing to a method that will return some search results for an entity in the system. It looks like this:
public IEnumerable<dynamic> Post(string entity, FormDataCollection parameters)
{
// entity is passed on the route.
// parameters contains all the stuff I'm trying to get here.
}
If I call the method using jQuery.post:
$.post('api/search/child', {where : 'ParentID = 1'}, function(d){ foo = d });
it works just right and returns what I would expect.
I've made a service in my angular application that makes a similar call:
$http.post('api/search/child', { where: 'parentID = ' + parent.ID })
.success(function (data, status, headers, config) {
// etc.
})
But when it hits my "Post" method on the server, "paramters" is null.
After some googling I've tried adding a content-type header to ensure it's passed as JSON, and trying JSON.stringify-ing and $.param()-ing the "data" argument, but that didn't do anything (and from what I've read that shouldn't be necessary.) What am I doing wrong here? Thanks for your help!
UPDATE:
Here's the raw request from the (working) jQuery example:
POST http://localhost:51383/api/search/child HTTP/1.1
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://localhost:51383/mypage.aspx
Accept-Language: en-US
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Host: localhost:51383
Content-Length: 23
DNT: 1
Connection: Keep-Alive
Pragma: no-cache
Cookie: (etc)
Authorization: (etc)
where=ParentID+%3D+1
And the raw request from the (failing) Angular sample:
POST http://localhost:51383/api/search/parent HTTP/1.1
Content-Type: application/json;charset=utf-8
Accept: application/json, text/plain, */*
Referer: http://localhost:51383/mypage.aspx
Accept-Language: en-US
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Connection: Keep-Alive
Content-Length: 27
DNT: 1
Host: localhost:51383
Pragma: no-cache
Cookie: (etc)
{"where":"ScorecardID = 1"}
Very weird. Even when I add the 'json' data type parameter to the end of the jQuery call, it still creates the www-form-urlencoded request. And that's the one that works. My Web API application is already set up for JSON (but thank-you Dalorzo).
Check if you have included the JSON Formatter in your configuration. It should be something like :
System.Web.Http.GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
config.Formatters.Insert(0, new System.Net.Http.Formatting.JsonMediaTypeFormatter());
The Content-Type=application/json only will work if you set the proper formatter.
You can also try using [FromBody] next to your parameter type.
Solved! Discovered this question:
AngularJs $http.post() does not send data
Pointing to this lovely article:
http://victorblog.com/2012/12/20/make-angularjs-http-service-behave-like-jquery-ajax/
Turns out Angular doesn't post data the same way jQuery does but you can override it with some tweaking.
I solved this by below codes:
Client Side:
$http({
url: me.serverPath,
method: 'POST',
data: data,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
}).
success(function (serverData) {
console.log("ServerData:", serverData);
......
Notice that data is an object.
On the server (ASP.NET MVC):
[AllowCrossSiteJson]
public string Api()
{
var data = JsonConvert.DeserializeObject<AgentRequest>(Request.Form[0]);
if (data == null) return "Null Request";
var bl = Page.Bl = new Core(this);
return data.methodName;
}
and 'AllowCrossSiteJsonAttribute' is needed for cross domain requests:
public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.RequestContext.HttpContext.Response.AddHeader("Access-Control-Allow-Origin", "*");
base.OnActionExecuting(filterContext);
}
}

Resources