441

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
  • 28,342
  • 21
  • 95
  • 123
Patrioticcow
  • 23,784
  • 68
  • 202
  • 327
  • 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
  • @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

11 Answers11

497

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
  • 70,955
  • 24
  • 137
  • 155
ggarber
  • 8,019
  • 5
  • 25
  • 31
  • 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
  • 70
    `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
  • 2
    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
  • 8
    @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
372

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,566
  • 1
  • 14
  • 21
jmanning2k
  • 8,769
  • 4
  • 27
  • 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 – Jay 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
64

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
  • 230,326
  • 38
  • 368
  • 578
Adrian Toman
  • 10,902
  • 5
  • 44
  • 60
  • 4
    you should notice that "btoa" used here for Base64 encryption is not supported in IE versions less that 10 and in some mobile platforms – SET May 15 '13 at 07:42
  • 1
    also, according to this https://developer.mozilla.org/en-US/docs/DOM/window.btoa you should use btoa(unescape(encodeURIComponent(str))) – SET May 15 '13 at 07:47
  • @SET, i guess it needs to be btoa(encodeURIComponent(escape(str))) – Saurabh Kumar Jul 15 '14 at 17:27
  • I wanted to get the response so got rid of async false, and added result parameter to success function. I also had pre-generated Authorization header so just used that instead. – radtek Sep 11 '14 at 16:00
  • 1
    @SET is should be noted the Base64 is not encryption, it's encoding. If it was encryption, it wouldn't be super simple to decode it with a single function call – Chris Aug 11 '15 at 14:39
  • I am trying this and I do not see the Authorization header being set in the request (when I look at the headers actually being sent using the network tab). And I get an error in the console: "No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin is therefore not allowed access." So I don't know if the lack of Authorization in the request is causing that or something else? – Elisabeth Feb 09 '17 at 01:13
  • @Elisabeth Access-Control-Allow-Origin errors occur when cross-origin HTTP requests (CORS) aren't permitted. It's not related to Authorization header. Is your API on the same domain as the web page? If not you will need to allow CORS on the API. – Adrian Toman Feb 09 '17 at 05:48
  • Thank you Adrian. I must have an error in my code (missing Auth header). And yes, I'll need to allow CORS on the API. – Elisabeth Mar 08 '17 at 19:33
41

Or, simply use the headers property introduced in 1.5:

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

Reference: jQuery Ajax API

AsemRadhwi
  • 877
  • 11
  • 21
  • Would you please tell me , how to do it for each user? I mean get api token for each user from database and send it with ajax header. – Haniye Shadman Jul 27 '19 at 07:46
33

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
  • 28,342
  • 21
  • 95
  • 123
Paul Odeon
  • 3,574
  • 1
  • 31
  • 31
  • 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
28

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
  • 28,342
  • 21
  • 95
  • 123
SharkAlley
  • 10,523
  • 5
  • 48
  • 41
  • 5
    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,525
  • 28
  • 46
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
  • 28,342
  • 21
  • 95
  • 123
arnebert
  • 141
  • 1
  • 2
10

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
  • 3,339
  • 1
  • 13
  • 17
  • 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
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
  • 28,342
  • 21
  • 95
  • 123
Maks
  • 746
  • 7
  • 10
0

Let me show you and Apache alternative- IIS which is need it before start real JQuery Ajax authentication

If we have /secure/* path for example. We need to create web.config and to prohibited access. Only after before send applayed must be able to access it pages in /secure paths

<?xml version="1.0"?>
<configuration>
  <system.web>
    <!-- Anonymous users are denied access to this folder (and its subfolders) -->
    <authorization>
      <deny users="?" />
    </authorization>
  </system.web>
</configuration>



<security>
   <authentication>
      <anonymousAuthentication enabled="false" />
      <basicAuthentication enabled="true" />
   </authentication>
</security>
Pit
  • 119
  • 8