1

I have an Angular application that I'm trying to authenticate with Basic authentication to my REST service. I'm adding the authorization header with the corresponding "Base {username:password}" encoded in base64 and I'm calling my rest api but keep getting back a 401. I'm obviously missing a step here...

Here's the angular code:

angular
    .module('myApp.services')
    .factory('AuthenticationService', ['$http', '$q', '$location', 'Base64', 'SessionService', function ($http, $q, $location, encoder, session) {
        return {
            authenticate:
                function (user, password) {

                    var deferred = $q.defer();
                    var url = "http://localhost:28924/api/login";

                    if (user && password) {
                        var encoded = encoder.encode(user + ':' + password);
                        $http.defaults.headers.common.Authorization = 'Basic ' + encoded;
                        console.log('here');
                        sessionStorage.setItem('Authorization', $http.defaults.headers.common.Authorization);

                        $http.get(url)
                            .success(function (data, status, headers, config) {
                                console.log('login Successful in Authentication service');
                                deferred.resolve(data);
                                session.setSession();
                            })
                            .error(function (data, status, headers, config) {
                                deferred.reject(status);
                            });
                    }
                    else {
                        deferred.reject(401);
                    }

                    return deferred.promise;
                },
            logout:
                function () {
                    $http.defaults.headers.common.Authorization = null;
                    $http.defaults.headers.common.Impersonate = null;
                    $location.url('/login');
                }
        };
    }
    ]
);

Here's my LoginController:

public class LoginController : ApiController
{
    public LoginController()
    {

    }
    [Authorize]
    public HttpResponseMessage Get()
    {
        //return this.ControllerContext.Request.CreateResponse(HttpStatusCode.OK);

        if (this.User.Identity.IsAuthenticated)
        {
            return this.ControllerContext.Request.CreateResponse(HttpStatusCode.OK);
        }
        return this.ControllerContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
    }
}

And I've also set my IISExpress config file to "Allow" basic authentication, like described here: set basic authentication in IIS Express.

Does just adding the "Authorize" attribute to my Get method let the host know to check the credentials that were passed in the Authorization header? Or do I need to implement something extra, like in this SO post (the question part): basic authentication with custom message handler?

It seems to me that there needs to be more in my "Get" method, but I can't find any good examples to help walk me through this....(if you haven't figure it out, this is my first try with basic authentication AND REST api/services.

Edit: Aha! I'm getting closer. @Chandermani's response for some reason prompted me to check out my OWN web.config and I realized I didn't have this:

<security>
  <authentication>
    <basicAuthentication enabled="true"/>
  </authentication>
</security>

Adding this now prompts me for my credentials when navigating to the .../api/login page.

Edit2: Checking to see what was sent across the wire via Chrome Dev tools shows that the authorization header is being sent, while specifying Basic authentication and my base64 encoded username and password. Here's what it looks like:

Request URL:http://localhost:28924/api/login
Request Method:GET
Status Code:401 Unauthorized
Request Headersview source
Accept:application/json, text/plain, */*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Authorization:Basic YWblahblahblahDE=
Connection:keep-alive
Host:localhost:28924
Referer:http://localhost:28924/index.html
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
Response Headersview source
Cache-Control:private
Content-Length:6333
Content-Type:text/html; charset=utf-8
Date:Wed, 08 Jan 2014 14:32:14 GMT
Server:Microsoft-IIS/8.0
WWW-Authenticate:Negotiate
WWW-Authenticate:NTLM
WWW-Authenticate:Basic realm="localhost"
X-Powered-By:ASP.NET
X-SourceFiles:=?UTF-8?B?QzpcU291cmNlQ29kZVxUcmlhbHNNb2JpbGVcVHJhaWxzTW9iaWxlLldlYlxhcGlcbG9naW4=?=

When the api/login page prompts me for my credentials it gets stuck in an infinite loop. I'll enter them, hit enter, then it asks for them again.

Edit3: Ok, I'm able to authenticate!!! I had to specify the domain in front of my username since I'm running localhost...

Thanks everyone for pointing me in the right direction!

Community
  • 1
  • 1
ganders
  • 7,285
  • 17
  • 66
  • 114
  • 2
    The first thing would be to try to access the api directly from browser. Does is prompt for username \ password? If it does and then shows the content the service is configured correct. Then you can see what angular is sending. You can use fiddler to track the request being made. – Chandermani Jan 08 '14 at 14:18
  • @Chandermani when navigating directly to that page without entering my credentials, this is what gets displayed: {"Message":"Authorization has been denied for this request."} – ganders Jan 08 '14 at 14:20
  • Does it popup a browser dialog to username password? – Chandermani Jan 08 '14 at 14:25
  • How about setting authentication mode to `Windows` in the `system.web/authentication'? – Wiktor Zychla Jan 08 '14 at 14:25
  • @Chandermani yes, (browser dialog), see my edit in my question at the end... – ganders Jan 08 '14 at 14:26
  • I agree with Chandermani that you may want to use fiddler, or, on IE I hit F12 to bring up a tool so I can see what is passed to the server, just use that so you can make certain what is being sent, then you can narrow down where the problem is better. – James Black Jan 08 '14 at 14:36
  • @JamesBlack I added the output via Chrome Dev Tools to the question at the very end... – ganders Jan 08 '14 at 14:36

1 Answers1

2

A combination of changes led to fixing my problem:

1) I needed to allow basic authentication in my OWN web.config. I had only previously set it up in the IISExpress applicationhost.config (see Edit(1))

2) Showing the output from Chrome Dev tools proved that the Basic Authentication authorization header was, in fact, being sent across.

3) The last problem was since I'm running on localhost, I needed to specify the domain to authenticate against in my username. There's probably a cleaner way of doing this, but I just entered this in my username field: mydomain\ganders and it started working.

ganders
  • 7,285
  • 17
  • 66
  • 114