112

I am trying to POST data from my API but I can't pass the basic authentication.

I try:

$.ajax({
  type: 'POST',
  url: http://theappurl.com/api/v1/method/,
  data: {},
  crossDomain: true,
  beforeSend: function(xhr) {
    xhr.setRequestHeader('Authorization', 'Basic [REDACTED]');
  }
});

My server configuration response is:

response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Methods"] = "POST"
response["Access-Control-Max-Age"] = "1000"
response["Access-Control-Allow-Headers"] = "*"

The headers that I get is:

Request Headers

OPTIONS /api/v1/token-auth/ HTTP/1.1
Host: theappurl.com
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://127.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.63 Safari/537.31
Access-Control-Request-Headers: origin, authorization, content-type
Accept: */*
Referer: http://127.0.0.1:8080/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: es,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

Response header

HTTP/1.1 401 Unauthorized
Server: nginx/1.1.19
Date: Fri, 16 Aug 2013 01:29:21 GMT
Content-Type: text/html
Content-Length: 597
Connection: keep-alive
WWW-Authenticate: Basic realm="Restricted"

I guess the server configuration is good because I can access to API from the Advanced REST Client (Chrome Extension)

Any suggestions?

PD: The header that I get from Advanced REST client is:

    User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.63 Safari/537.31
    Origin: chrome-extension://hgmloofddffdnphfgcellkdfbfbjeloo
    Authorization: Basic [REDACTED]
    Content-Type: application/x-www-form-urlencoded 
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: es,en;q=0.8
    Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

and

    Server: nginx/1.1.19 
    Date: Fri, 16 Aug 2013 01:07:18 GMT 
    Content-Type: application/json; charset=utf-8 
    Transfer-Encoding: chunked 
    Connection: keep-alive
    Vary: Accept, Cookie 
    Allow: POST, OPTIONS 
    X-Robots-Tag: noindex

sending OPTION method

Marcel
  • 13,233
  • 18
  • 77
  • 130
individuo7
  • 1,374
  • 2
  • 12
  • 17
  • 19
    I realize this post is long dead, but I just want to point out in case you're not aware that by posting your Authorization: header, you've essentially posted your password in the clear. The string of gibberish there is just the base64 encoding of your username:password, so everyone can see your password. Hopefully you realized this and used a dummy password here :) – Lexelby Nov 13 '14 at 16:41
  • This works fine with ssrs report server 2017. It hides the password and username in the URL. – Clark Vera Dec 19 '19 at 21:46
  • 1
    @Lexelby: The username is "the user" and the password is "and the password" in Spanish. So I'm guessing these aren't real credentials. – pdr Mar 20 '20 at 16:21

6 Answers6

74

Per https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding and http://en.wikipedia.org/wiki/Basic_access_authentication , here is how to do Basic auth with a header instead of putting the username and password in the URL. Note that this still doesn't hide the username or password from anyone with access to the network or this JS code (e.g. a user executing it in a browser):

$.ajax({
  type: 'POST',
  url: http://theappurl.com/api/v1/method/,
  data: {},
  crossDomain: true,
  beforeSend: function(xhr) {
    xhr.setRequestHeader('Authorization', 'Basic ' + btoa(unescape(encodeURIComponent(YOUR_USERNAME + ':' + YOUR_PASSWORD))))
  }
});
Mig82
  • 3,665
  • 1
  • 30
  • 55
seanp2k
  • 958
  • 8
  • 8
  • 1
    Note: A window.btoa polyfill such as Base64.js will be required for this feature to work in IE6,7,8,9. Also unescape is deprecated as per ECMAScript v3. – null Feb 24 '15 at 11:03
  • @Techbrunch you are mistaken, seanp2k's example works very well, it uses a very known trick to decode Unicode characters to ASCII, it actually uses the fact that (un)escape does not support Unicode, but (dec)encodeURIComponent does.. –  May 08 '15 at 19:32
52

You can include the user and password as part of the URL:

http://user:passwd@www.server.com/index.html

see this URL, for more

HTTP Basic Authentication credentials passed in URL and encryption

of course, you'll need the username password, it's not 'Basic hashstring.

hope this helps...

Community
  • 1
  • 1
Dru
  • 1,306
  • 9
  • 6
  • please can you edit your answer and change the url, to prevent google or bots indexing – individuo7 Aug 16 '13 at 04:08
  • 7
    That solution won't work for Android's Native Browser (Pre KitKat). The username/login form will still popup for your user, so be careful. – Sterling Bourne Jan 20 '14 at 22:07
  • 64
    This method exposes the username and password to anyone listening on the network or devices... – Adam Jul 21 '14 at 22:26
  • 5
    @Adam hash string is also not safe – Mustafa Nov 30 '14 at 03:06
  • 3
    This method exposes username/password in the browser history since it's part of the URL. In addition, it's likely to get logged on the server since requests logging includes the query string. It's better to include user/pass as part of the "Authorization" header. Search on http basic authentication. – thebiggestlebowski Dec 17 '14 at 05:58
  • 42
    This doesn't answer the question at all. Passing it as a url is not a header. – jemiloii Jan 29 '15 at 19:06
  • @JemiloII How do you think it works in the background? The browser generates the authorization header for you. It absolutely does get sent as a header. – Brad Apr 19 '15 at 20:49
  • @user1483903 Logged on the server as part of the query string? Instead of telling people to search on basic auth, why don't you perform the same search yourself? Fire up a packet sniffer to see how this really works before adding comments that are not correct. – Brad Apr 19 '15 at 20:50
  • @Adam All basic auth is exposed to anyone listening on the network, unless you're doing this over TLS/SSL. – Brad Apr 19 '15 at 20:51
  • 2
    @Brad because the question specifically states send a correct header, it doesn't ask for browser magic. Plus, sending it as a header eliminates the url from being seen in the user's history. It's not a good thing to have credentials in an easily searchable format. – jemiloii Apr 20 '15 at 16:51
  • @JemiloII AJAX URLs don't appear in history. And, it's not magic... it's a well understood standard. This *does* answer the question in an alternative way, and is a perfectly valid answer, even if it isn't appropriate for your specific situation. – Brad Apr 20 '15 at 16:58
  • Ideally we'd use the Authorization header to pass in the Base64 encoded username:password as Basic. This approach is not ideal given it's much more natural for a programatic AJAX client to add request headers than to concatenate things onto an URL – Ricardo Rodrigues May 24 '16 at 21:33
  • Can't you read, he asked about POST request. And you suggest him to use GET request. – Green Oct 06 '16 at 06:27
  • 4
    This is no longer supported by major browsers and is deprecated – Single Entity Jul 07 '17 at 16:42
  • 6
    downvote reason: https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication. Towards the end of the article, it mentions that `The use of these URLs is deprecated` – anurupr Feb 08 '18 at 01:47
  • 1
    This is like someone asking how to fix their car, and you pasted instructions on bicycle maintenance – eselskas Feb 13 '19 at 12:37
  • 4
    According to RFC3986 https://tools.ietf.org/html/rfc3986 Use of the format "user:password" in the userinfo field is deprecated. – Alex Feb 28 '19 at 12:30
47

NodeJS answer:

In case you wanted to do it with NodeJS: make a GET to JSON endpoint with Authorization header and get a Promise back:

First

npm install --save request request-promise

(see on npm) and then in your .js file:

var requestPromise = require('request-promise');

var user = 'user';
var password = 'password';

var base64encodedData = Buffer.from(user + ':' + password).toString('base64');

requestPromise.get({
  uri: 'https://example.org/whatever',
  headers: {
    'Authorization': 'Basic ' + base64encodedData
  },
  json: true
})
.then(function ok(jsonData) {
  console.dir(jsonData);
})
.catch(function fail(error) {
  // handle error
});
drmrbrewer
  • 8,355
  • 11
  • 60
  • 138
jakub.g
  • 30,051
  • 7
  • 78
  • 118
14

If you are in a browser environment you can also use btoa.

btoa is a function which takes a string as argument and produces a Base64 encoded ASCII string. Its supported by 97% of browsers.

Example:

> "Basic " + btoa("billy"+":"+"secretpassword")
< "Basic YmlsbHk6c2VjcmV0cGFzc3dvcmQ="

You can then add Basic YmlsbHk6c2VjcmV0cGFzc3dvcmQ= to the authorization header.

Note that the usual caveats about HTTP BASIC auth apply, most importantly if you do not send your traffic over https an eavesdropped can simply decode the Base64 encoded string thus obtaining your password.

This security.stackexchange.com answer gives a good overview of some of the downsides.

fernandohur
  • 6,395
  • 9
  • 44
  • 82
3

no need to use user and password as part of the URL

you can try this

byte[] encodedBytes = Base64.encodeBase64("user:passwd".getBytes());

String USER_PASS = new String(encodedBytes);

HttpUriRequest request = RequestBuilder.get(url).addHeader("Authorization", USER_PASS).build();
erhanck
  • 67
  • 5
0

PHP - curl:

$username = 'myusername';
$password = 'mypassword';
...
curl_setopt($ch, CURLOPT_USERPWD, $username . ":" . $password);
...

PHP - POST in WordPress:

$username = 'myusername';
$password = 'mypassword';
...
wp_remote_post('https://...some...api...endpoint...', array(
  'headers' => array(
    'Authorization' => 'Basic ' . base64_encode("$username:$password")
  )
));
...
DrLightman
  • 455
  • 5
  • 12