464

I am trying to create a basic authentication through the browser, but I can't really get there.

If this script won't be here the browser authentication will take over, but I want to tell the browser that the user is about to make the authentication.

The address should be something like:

http://username:password@server.in.local/

I have a form:

<form name="cookieform" id="login" method="post">
      <input type="text" name="username" id="username" class="text"/>
      <input type="password" name="password" id="password" class="text"/>
      <input type="submit" name="sub" value="Submit" class="page"/>
</form>

And a script:

var username = $("input#username").val();
var password = $("input#password").val();

function make_base_auth(user, password) {
  var tok = user + ':' + password;
  var hash = Base64.encode(tok);
  return "Basic " + hash;
}
$.ajax
  ({
    type: "GET",
    url: "index1.php",
    dataType: 'json',
    async: false,
    data: '{"username": "' + username + '", "password" : "' + password + '"}',
    success: function (){
    alert('Thanks for your comment!');
    }
});
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Patrioticcow
  • 26,422
  • 75
  • 217
  • 337
  • 6
    So you **don't** want the browser to handle the BASIC authentication? Why not just use form-based authentication? – no.good.at.coding Mar 31 '11 at 22:47
  • 1
    @no.good.at.coding If you need to integrate with a third party API behind authentication (which is what I am trying to do - https://developer.zendesk.com/rest_api/docs/core/introduction#basic-authentication) – Brian Hannay Jan 13 '17 at 04:37

10 Answers10

520

Use jQuery's beforeSend callback to add an HTTP header with the authentication information:

beforeSend: function (xhr) {
    xhr.setRequestHeader ("Authorization", "Basic " + btoa(username + ":" + password));
},
Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177
ggarber
  • 8,300
  • 5
  • 27
  • 32
  • 6
    i am using the example u gave but it doesn't work ` $.ajax ({ url: "http://server.in.local/index.php", beforeSend: function (xhr) { xhr.setRequestHeader(“Authorization”, “Basic ” + encodeBase64 (“username:password”) );}, succes: function(val) { //alert(val); alert("Thanks for your comment!"); } }); ` – Patrioticcow Mar 31 '11 at 23:07
  • 71
    `beforeSend: function(xhr) { xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password)); };` works for me – Rico Suter Jul 26 '13 at 08:11
  • 12
    Problem...If the credentials I pass fail, in Chrome the user is then presented with a dialog to enter username/pwd again. How can I prevent this 2nd dialog from appearing if the credentials fail? – David Oct 02 '13 at 00:47
  • 3
    Won't this leave the username and password out in the open for anyone to see ? Even if its in base64 they can just decode it. Same with the answer below. – cbron Aug 13 '14 at 17:32
  • 9
    @calebB Basic authentication in general just leaves the username and password in the open for anyone to see. This isn't just a problem with the method described here. If basic authentication, or really *any* authentication is being used then SSL should also be employed. – Jason Jackson Sep 10 '14 at 16:27
388

How things change in a year. In addition to the header attribute in place of xhr.setRequestHeader, current jQuery (1.7.2+) includes a username and password attribute with the $.ajax call.

$.ajax
({
  type: "GET",
  url: "index1.php",
  dataType: 'json',
  username: username,
  password: password,
  data: '{ "comment" }',
  success: function (){
    alert('Thanks for your comment!'); 
  }
});

EDIT from comments and other answers: To be clear - in order to preemptively send authentication without a 401 Unauthorized response, instead of setRequestHeader (pre -1.7) use 'headers':

$.ajax
({
  type: "GET",
  url: "index1.php",
  dataType: 'json',
  headers: {
    "Authorization": "Basic " + btoa(USERNAME + ":" + PASSWORD)
  },
  data: '{ "comment" }',
  success: function (){
    alert('Thanks for your comment!'); 
  }
});
Phil
  • 1,996
  • 1
  • 19
  • 26
jmanning2k
  • 9,297
  • 4
  • 31
  • 23
  • 15
    Shouldn't it be `username` and not `user`? Also it's not exactly the same: from the online docos and my experience it looks like it's not preemptive as some APIs require. In other words it sends the `Authorization` header only when a code 401 is returned. – Stefano Fratini Nov 19 '12 at 02:23
  • 3
    @StefanoFratini - you are correct on both counts. Fixed the username field, and it's good to know about the preemptive auth vs responding only on challenge. Setting the header explicitly as in other answers will allow use of this 'passive' variant of basic auth. – jmanning2k Jan 24 '13 at 18:39
  • for me the the above option didnt work, this worked like a charm.. thanks – Anoop Isaac Nov 17 '13 at 01:02
  • See above comment, please correct this answer to indicate { "Authorization": "Basic " + btoa("user:pass") } as the correct header – Jason Jun 25 '14 at 17:14
  • I think the latest jQuery supports setRequestHeader instead of 'headers' – mike_hornbeck Aug 12 '14 at 11:28
  • 2
    This is the answer for which I have been looking! The key bit in this answer for me is how the 'header' is used to preemptivly send authentication. My question here is answered by this. http://stackoverflow.com/questions/28404452/phonegap-ios-ajax-request-never-completes – Steven Anderson Feb 10 '15 at 01:19
  • Adding authorization in the headers section did work for me. Thank you so much! I had to remove dataType = json to make work at my end. – tarekahf Jul 16 '20 at 16:45
65

Use the beforeSend callback to add a HTTP header with the authentication information like so:

var username = $("input#username").val();
var password = $("input#password").val();  

function make_base_auth(user, password) {
  var tok = user + ':' + password;
  var hash = btoa(tok);
  return "Basic " + hash;
}
$.ajax
  ({
    type: "GET",
    url: "index1.php",
    dataType: 'json',
    async: false,
    data: '{}',
    beforeSend: function (xhr){ 
        xhr.setRequestHeader('Authorization', make_base_auth(username, password)); 
    },
    success: function (){
        alert('Thanks for your comment!'); 
    }
});
Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
Adrian Toman
  • 11,316
  • 5
  • 48
  • 62
42

Or, simply use the headers property introduced in 1.5:

headers: {"Authorization": "Basic xxxx"}

Reference: jQuery Ajax API

AsemRadhwi
  • 887
  • 11
  • 21
36

The examples above are a bit confusing, and this is probably the best way:

$.ajaxSetup({
  headers: {
    'Authorization': "Basic " + btoa(USERNAME + ":" + PASSWORD)
  }
});

I took the above from a combination of Rico and Yossi's answer.

The btoa function Base64 encodes a string.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Paul Odeon
  • 4,407
  • 1
  • 37
  • 37
  • 6
    This is a bad idea, because you will send the login information with *all* AJAX requests, regardless of URL. Also, the documentation for `$.ajaxSetup()` says "Set default values for future Ajax requests. **Its use is not recommended.**" – Zero3 Feb 11 '16 at 12:10
31

As others have suggested, you can set the username and password directly in the Ajax call:

$.ajax({
  username: username,
  password: password,
  // ... other parameters.
});

OR use the headers property if you would rather not store your credentials in plain text:

$.ajax({
  headers: {"Authorization": "Basic xxxx"},
  // ... other parameters.
});

Whichever way you send it, the server has to be very polite. For Apache, your .htaccess file should look something like this:

<LimitExcept OPTIONS>
    AuthUserFile /path/to/.htpasswd
    AuthType Basic
    AuthName "Whatever"
    Require valid-user
</LimitExcept>

Header always set Access-Control-Allow-Headers Authorization
Header always set Access-Control-Allow-Credentials true

SetEnvIf Origin "^(.*?)$" origin_is=$0
Header always set Access-Control-Allow-Origin %{origin_is}e env=origin_is

Explanation:

For some cross domain requests, the browser sends a preflight OPTIONS request that is missing your authentication headers. Wrap your authentication directives inside the LimitExcept tag to respond properly to the preflight.

Then send a few headers to tell the browser that it is allowed to authenticate, and the Access-Control-Allow-Origin to grant permission for the cross-site request.

In some cases, the * wildcard doesn't work as a value for Access-Control-Allow-Origin: You need to return the exact domain of the callee. Use SetEnvIf to capture this value.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
SharkAlley
  • 11,399
  • 5
  • 51
  • 42
  • 7
    Thanks for the info that browsers send a CORS preflight request without auth headers! Details like this can cause hours of debugging! – jbandi Apr 13 '14 at 21:58
23

Use the jQuery ajaxSetup function, that can set up default values for all ajax requests.

$.ajaxSetup({
  headers: {
    'Authorization': "Basic XXXXX"
  }
});
Yossi Shasho
  • 3,632
  • 31
  • 47
11

There are 3 ways to achieve this as shown below

Method 1:

var uName="abc";
var passwrd="pqr";

$.ajax({
    type: '{GET/POST}',
    url: '{urlpath}',
    headers: {
        "Authorization": "Basic " + btoa(uName+":"+passwrd);
    },
    success : function(data) {
      //Success block  
    },
   error: function (xhr,ajaxOptions,throwError){
    //Error block 
  },
});

Method 2:

var uName="abc";
var passwrd="pqr";

$.ajax({
    type: '{GET/POST}',
    url: '{urlpath}',
     beforeSend: function (xhr){ 
        xhr.setRequestHeader('Authorization', "Basic " + btoa(uName+":"+passwrd)); 
    },
    success : function(data) {
      //Success block 
   },
   error: function (xhr,ajaxOptions,throwError){
    //Error block 
  },
});

Method 3:

var uName="abc";
var passwrd="pqr";

$.ajax({
    type: '{GET/POST}',
    url: '{urlpath}',
    username:uName,
    password:passwrd, 
    success : function(data) {
    //Success block  
   },
    error: function (xhr,ajaxOptions,throwError){
    //Error block 
  },
});
Gauri Bhosle
  • 4,985
  • 1
  • 15
  • 19
  • Hi, i have tested all 3 methods. But it never produced the Authorization header. As dataType i need to use jsonp because doing crossdomain call. And calling application API which i have no chance to change. Directly from java http client it works. But i am not able to handle it by jquery. – Reddy SK Jun 19 '20 at 14:24
11

JSONP does not work with basic authentication so the jQuery beforeSend callback won't work with JSONP/Script.

I managed to work around this limitation by adding the user and password to the request (e.g. user:pw@domain.tld). This works with pretty much any browser except Internet Explorer where authentication through URLs is not supported (the call will simply not be executed).

See http://support.microsoft.com/kb/834489.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
arnebert
  • 141
  • 1
  • 2
8

According to SharkAlley answer it works with nginx too.

I was search for a solution to get data by jQuery from a server behind nginx and restricted by Base Auth. This works for me:

server {
    server_name example.com;

    location / {
        if ($request_method = OPTIONS ) {
            add_header Access-Control-Allow-Origin "*";
            add_header Access-Control-Allow-Methods "GET, OPTIONS";
            add_header Access-Control-Allow-Headers "Authorization";

            # Not necessary
            #            add_header Access-Control-Allow-Credentials "true";
            #            add_header Content-Length 0;
            #            add_header Content-Type text/plain;

            return 200;
        }

        auth_basic "Restricted";
        auth_basic_user_file /var/.htpasswd;

        proxy_pass http://127.0.0.1:8100;
    }
}

And the JavaScript code is:

var auth = btoa('username:password');
$.ajax({
    type: 'GET',
    url: 'http://example.com',
    headers: {
        "Authorization": "Basic " + auth
    },
    success : function(data) {
    },
});

Article that I find useful:

  1. This topic's answers
  2. http://enable-cors.org/server_nginx.html
  3. http://blog.rogeriopvl.com/archives/nginx-and-the-http-options-method/
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Maks
  • 766
  • 8
  • 11