37

How can I get basic auth working in AngularJs? I've googled and the resources aren't working for me. I'm very new to AngularJS

Daniel Kaplan
  • 62,768
  • 50
  • 234
  • 356
  • 12
    If you think my question sucks, note that I just asked it so I could answer it. This took me over an hour to figure out and I didn't want anyone else to have to spend the effort. – Daniel Kaplan Jul 31 '13 at 00:15

1 Answers1

53

Assuming your html is defined like this:

<!doctype html>
<html ng-app="sandbox-app">
<head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
    <script src="todo.js"></script>
    <link rel="stylesheet" href="todo.css">
</head>
<body>
<h2>Todo</h2>
<div ng-controller="TodoCtrl">
    <ol>
...
    </ol>
</div>
</body>
</html>

You can make your backend connect to a rest api using basic auth like this:

var app = angular.module('sandbox-app', []);
app.config(function($httpProvider) {

});

app.factory('Base64', function() {
    var keyStr = 'ABCDEFGHIJKLMNOP' +
            'QRSTUVWXYZabcdef' +
            'ghijklmnopqrstuv' +
            'wxyz0123456789+/' +
            '=';
    return {
        encode: function (input) {
            var output = "";
            var chr1, chr2, chr3 = "";
            var enc1, enc2, enc3, enc4 = "";
            var i = 0;

            do {
                chr1 = input.charCodeAt(i++);
                chr2 = input.charCodeAt(i++);
                chr3 = input.charCodeAt(i++);

                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;

                if (isNaN(chr2)) {
                    enc3 = enc4 = 64;
                } else if (isNaN(chr3)) {
                    enc4 = 64;
                }

                output = output +
                        keyStr.charAt(enc1) +
                        keyStr.charAt(enc2) +
                        keyStr.charAt(enc3) +
                        keyStr.charAt(enc4);
                chr1 = chr2 = chr3 = "";
                enc1 = enc2 = enc3 = enc4 = "";
            } while (i < input.length);

            return output;
        },

        decode: function (input) {
            var output = "";
            var chr1, chr2, chr3 = "";
            var enc1, enc2, enc3, enc4 = "";
            var i = 0;

            // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
            var base64test = /[^A-Za-z0-9\+\/\=]/g;
            if (base64test.exec(input)) {
                alert("There were invalid base64 characters in the input text.\n" +
                        "Valid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\n" +
                        "Expect errors in decoding.");
            }
            input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

            do {
                enc1 = keyStr.indexOf(input.charAt(i++));
                enc2 = keyStr.indexOf(input.charAt(i++));
                enc3 = keyStr.indexOf(input.charAt(i++));
                enc4 = keyStr.indexOf(input.charAt(i++));

                chr1 = (enc1 << 2) | (enc2 >> 4);
                chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
                chr3 = ((enc3 & 3) << 6) | enc4;

                output = output + String.fromCharCode(chr1);

                if (enc3 != 64) {
                    output = output + String.fromCharCode(chr2);
                }
                if (enc4 != 64) {
                    output = output + String.fromCharCode(chr3);
                }

                chr1 = chr2 = chr3 = "";
                enc1 = enc2 = enc3 = enc4 = "";

            } while (i < input.length);

            return output;
        }
    };
});

//here's where YOUR code is finally accessed
function TodoCtrl($scope, $http, Base64) {

    $http.defaults.headers.common = {"Access-Control-Request-Headers": "accept, origin, authorization"}; //you probably don't need this line.  This lets me connect to my server on a different domain
    $http.defaults.headers.common['Authorization'] = 'Basic ' + Base64.encode('admin' + ':' + 'abc12345');
    $http({method: 'GET', url: 'http://localhost:8888/app/api/v1/pets'}).
            success(function(data, status, headers, config) {
                $scope.pets = data;
                // this callback will be called asynchronously
                // when the response is available
            }).
            error(function(data, status, headers, config) {
                alert(data);
                // called asynchronously if an error occurs
                // or server returns response with an error status.
            });

}

Note the majority of this code is the Base64 method. If you do not need to support IE9 and lower, you could replace it with native JS implementations - atob() and btoa(): https://developer.mozilla.org/en/docs/web/api/windowbase64/atob


For me, this always reports a 401 before it actually works. I believe this is a bug with the angular code but I'm not sure. I've created an issue here: https://github.com/angular/angular.js/issues/3406

mc.suchecki
  • 1,898
  • 4
  • 23
  • 44
Daniel Kaplan
  • 62,768
  • 50
  • 234
  • 356
  • 9
    don't worry man, there are sometimes bored people downvoting stuff randomly. –  Jul 31 '13 at 00:17
  • 2
    This sounds like you're running into the case where your server isn't responding to the CORS preflight request properly. Can you show the part where your server responds to the two requests (the OPTIONS request and the actual GET)? – Jeff Hubbard Sep 07 '13 at 23:36
  • 10
    Also, if you're not worried about < IE10, you could remove the Base64 stuff and simply use [window.btoa()](https://developer.mozilla.org/en-US/docs/Web/API/window.btoa) – Siyfion Oct 03 '13 at 08:51
  • this means your password shows up in the client js code. probably mini/uglyfied but it shows up. is this secure? – dasAnderl ausMinga Feb 17 '15 at 23:11
  • @dasAnderlausMinga that's only if you store it in your code. But you can ask the user to enter it in a login form and send it over. It doesn't need to be in the source that way. – Daniel Kaplan Feb 17 '15 at 23:21
  • yes, you are certainly correct if u use user input. i however, need sth like this without user input. basically default credentials for a mongo db, and i dont want that to show up in my client js. – dasAnderl ausMinga Feb 18 '15 at 14:20
  • 1
    @dasAnderlausMinga then I wouldn't recommend this. It can be useful for other things (e.g., a proof of concept that your prototype works). – Daniel Kaplan Feb 18 '15 at 18:12