Error 400 Cognito /oauth2/token endpoint from client-side javascript - javascript

I am using AWS Cognito-hosted UI for my signup and login. There is no app client secret defined.
I am trying to make an API call from the browser javascript code to the /oauth2/token endpoint in order to exchange autohorization_token with an ID token.
The problem is, when I make the call through Postman, Insomnia it works fine. However, when I make the same call through javascript from the browser it fails with the 400 response type and I can't get much about the reason.
This is the Insomnia call which is a success;
However, when I make the same call via javascript it fails.
I have used both fetch and XMLHttpRequest and the same result.
const XHR = new XMLHttpRequest();
let urlEncodedData = "",
urlEncodedDataPairs = [],
name;
urlEncodedDataPairs.push( encodeURIComponent( 'grant_type' ) + '=' + encodeURIComponent( 'authorization_code' ) );
urlEncodedDataPairs.push( encodeURIComponent( 'code' ) + '=' + encodeURIComponent( code ) );
urlEncodedDataPairs.push( encodeURIComponent( 'client_id' ) + '=' + encodeURIComponent( 'xxxxx' ) );
urlEncodedDataPairs.push( encodeURIComponent( 'redirect_url' ) + '=' + encodeURIComponent( 'https://www.xxx.me/xxx' ) );
XHR.addEventListener( 'load', function(event) {
alert( 'Yeah! Data sent and response loaded.' );
} );
// Define what happens in case of error
XHR.addEventListener( 'error', function(event) {
alert( 'Oops! Something went wrong.' );
});
// Set up our request
XHR.open( 'POST', 'https://xxx.auth.us-west-2.amazoncognito.com/oauth2/token' );
// Add the required HTTP header for form data POST requests
XHR.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' );
// Finally, send our data.
XHR.send( urlEncodedData );
This is the response I am getting:
I have tried the same request with fetch and the result is the same.
let tokenRequest = new Request(tokenURL, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
'Origin' : 'https://www.xxx.me'
},
body: new URLSearchParams({
'grant_type': 'authorization_code',
'code': code,
'client_id': 'xxx',
'redirect_url': 'https://www.xxx.me/xxx'
}).toString()
});
let response = await fetch(tokenRequest);
let data = await response.json();
One thing I have realized when I check the browser developer tool for other POST calls made to auth endpoints with content type=application/x-www-form-urlencoded, it shows query params added to URL like shown below, however, for my call, params are not encoded as part of URL. I am not sure if the problem might be related to this.
Any idea how I can make this call with client-side javascript?

Looks like the browser is using redirect_url which is wrong, and Postman is using redirect_uri which is correct. Also you should use PKCE to make the message more secure - see steps 4 and 8 of my blog post to understand how this looks.
Note also that in OAuth it is common to not return error details, or return to the app, if an unrecognised Client ID or Redirect URI are supplied. If these are both correct you should instead receive the standard OAuth error and error_description fields in a response payload.

Related

Wordpress doesen't process AJAX request

I'm having trouble processing an AJAX request on my Wordpress site. A plugin that I'm writing needs to serve a shortcode with a script that should dispatch an AJAX request back to the plugin, which should send back some JSON data.
Shortcode [payment_form] being served:
public function initPaymentFormShort()
{
ob_start();
wp_register_script('Stripe', 'https://js.stripe.com/v3/', null, null, true);
wp_enqueue_script('Stripe');
wp_enqueue_script('payment.js', get_template_directory_uri() . '/js/payment.js');
wp_localize_script( 'payment.js', 'ajax_post_params', array( 'ajax_post_url' => admin_url( 'admin-ajax.php' ) ) );
include __DIR__ . '/../templates/payment_form_short.php';
return ob_get_clean();
}
Request handler registered to process the request:
add_action('wp_ajax_get_public_key', array($this, 'handleGetPublicKey'));
add_action('wp_ajax_nopriv_get_public_key', array($this, 'handleGetPublicKey'));
Handlers are registered within the plugins_loaded action
Part of the payment.js script that dispatches the request:
function getPublicKey() {
return fetch(ajax_post_params.ajax_post_url, {
method: 'post',
body: JSON.stringify({action : 'get_public_key'})
})
.then(function(response) {
return response.json();
})
.then(function(response) {
stripeElements(response.publicKey);
});
}
The request gets sent to: http://mysite.local/wp-admin/admin-ajax.php (served with Flywheel Local if that matters)
The response is a 400 Bad Request with a 0 in the body. The handler doesn't get executed at all.
Please help. Thanks
Wordpress's ajax API does not use JSON encoded requests. You need to do form-encoded. Something like this should work:
function getPublicKey() {
return fetch(ajax_post_params.ajax_post_url, {
method: 'post',
body: 'action=get_public_key',
headers: { 'Content-type': 'application/x-www-form-urlencoded' }
})
.then(function(response) {
return response.json();
})
.then(function(response) {
stripeElements(response.publicKey);
});
}
Note that the body is urlencoded. So if you had multiple parameters it would look something like this:
action=get_public_key&foo=bar&baz=bin
All modern browsers also support the URLSearchParams API so you can also generate these query strings like this:
let params = new URLSearchParams({
action: 'get_public_key',
foo: 'bar',
baz: 'bin'
});
let body = params.toString();
That would be the preferred way of generating form data query strings for complex data sets.

Yahoo New Weather API oAuth call in jQuery

As of the 3rd of January Yahoo Weather has a new weather API that requires oAuth https://developer.yahoo.com/weather/
Now I have gotten my Client ID (Consumer Key) and my Client Secret (Consumer Secret) from yahoo. My question is how would I call this API with oAuth in jquery or javascript?
Here is what a call looks like:
GET /forecastrss?location=sunnyvale,ca HTTP/1.1
Host: weather-ydn-yql.media.yahoo.com
Yahoo-App-Id: YOUR_APP_ID
Authorization: OAuth
oauth_consumer_key="YOUR_CONSUMER_KEY",oauth_signature_method="HMAC-SHA1",oauth_timestamp="YOUR_TIMESTAMP",oauth_nonce="YOUR_NONCE",oauth_version="1.0",oauth_signature="YOUR_GENERATED_SIGNATURE"
cache-control: no-cache
and it states "Please also include your OAuth app id in the header."
What would this look like in jquery?
I have tried the following:
$.get("https://weather-ydn-yql.media.yahoo.com/forecastrss", { location: "sunnyvale,ca", format: "json", oauth_consumer_key: "Client ID (Consumer Key)", oauth_signature_method: "HMAC-SHA1" } ).done(function( data ) {
console.log("Data Loaded: " + data);
});
and I get this error:
ERR_ABORTED 401 (Unauthorized)
Though I didn't run this code specifically but this should work like every other get request in jQuery
$.get( url, { location: "sunnyvale,ca", format: "json", oauth_consumer_key: oauth_consumer_key, oauth_signature_method: oauth_signature_method, ... } )
.done(function( data ) {
alert( "Data Loaded: " + data );
});
Ref1: jQuery API https://api.jquery.com/jQuery.get/
Ref2: Yahoo API: https://developer.yahoo.com/weather/documentation.html

Nodejs request post with body include api key

I have been trying about a week but I couldn't make a post request to get a result.
I tried a bunch of middlewares (exp: 'request', 'axios', 'reqclient','superagent etc..) but I couldn't make it.
Please provide me a simple post request with sending API key and body.
I also read all the documentation.
Please check below to see what I want :
*Authentication API key required.
*O-Auth Scopes trades
*Input One of: user_id + token or user_url is required.
here is my one of try :
const request = require('request-promise')
const options = {
method: 'POST',
uri: 'api-site.com/Offer/v1/',
headers: {
'User-Agent': 'Request-Promise',
'Authorization': 'Basic 123123asdasd123123'
},
body: {
user_url: "site.com/user/user1234123",
otherparams: "parameter"
},
json: true
};
request(options)
.then(function (response) {
Console.log(response);
})
.catch(function (err) {
console.log('Error ', err.message);
});
I am getting this output :
Error : 401 - {"status":401,"time":1540458426,"message":"API Key Required"}
I tried some other request post middle-wares and played with content-type (application/json. dataForm, x-www-form-urlencoded) or
changed the location of my API key from header to body or
tried my API key inside of auth{authorization: "API Key"}
tried much more.
the result didn't change. I got the same output or errors.
EDIT :
this is the link that I am trying to do but got stack :
check here
Solved !
Everything works great. Problem was I needed to send my API Key base64 string.
Buffer.from("your_api_key_value" + ":", "ascii").toString("base64")

How to add 'field' property to a Google Drive API v3 call in JavaScript?

tl;dr I am new to JavaScript and Google Apps Script and I have no idea how to add the 'fields' property to a Google Drive v3 API call.
I am trying to modify file permissions in a G Suite domain using Google Apps Script, a service account, and the OAuth 2 sample from Google. I wrote a function for Drive API v3 to replace Drive API v2 getIdForEmail, but API v3 requires the 'fields' query parameter to request specific fields.
The error given when I run the script is:
Request failed for https://www.googleapis.com/drive/v3/about returned code 400. Truncated server response: { "error": { "errors": [ { "domain": "global", "reason": "required", "message": "The 'fields' parameter is required for this meth...
I found the answer in a different programming language but can't translate it to Google Apps Script / JavaScript. See Fields on the previous answer: Google Drive API v3 Migration. How do I add the 'fields' property to request 'permissionId'?
function getPermissionIdForEmail(userEmail) {
var service = getService(userEmail);
if (service.hasAccess()) {
var url = 'https://www.googleapis.com/drive/v3/about';
var options = {
'method': 'get',
'contentType': 'application/json'
};
var response = UrlFetchApp.fetch(url, {
headers: {
Authorization: 'Bearer ' + service.getAccessToken()
}
});
var result = JSON.parse(response.getContentText());
Logger.log('getPermissionIdForEmail result: %s', JSON.stringify(result, null, 2));
} else {
Logger.log('getPermissionIdForEmail getLastError: %s', service.getLastError());
}
}
Edit: Thank you Cameron Roberts for the help. The solution I used is
var url = 'https://www.googleapis.com/drive/v3/about' + '?fields=user/permissionId';
I can't recall offhand if Google will accept a POST request here, if they will this could be passed as a request payload:
var response = UrlFetchApp.fetch(url, {
headers: {
Authorization: 'Bearer ' + service.getAccessToken()
},
payload: {
fields: 'kind,user,storageQuota'
}
});
Or if it must be a GET request you can append the parameters directly to the url:
url = url+'?fields=kind,user,storageQuota'
var response = UrlFetchApp.fetch(url, {
headers: {
Authorization: 'Bearer ' + service.getAccessToken()
}
});

NodeJS XML HTTP POST request headers object not accepting

I am trying to get a Ping message back from an API of a channel manager for hotels. (XML Open Travel Alliance)
I made the HTTP XML POST request first with SoapUI-5.3.0 tool including the following parameter:
otaRQ: <OTA_PingRQ xmlns="http://www.opentravel.org/OTA/2003/05" Version="3.30" TimeStamp="2011-07-24T10:07:24" Target="Production"> <EchoData><![CDATA[Hello World!!]]> </EchoData> </OTA_PingRQ>
and received the following XML response:
<OTA_PingRS PrimaryLangID="en" Target="Production" TimeStamp="2017-03-21T09:43:55" Version="3.00" xmlns="http://www.opentravel.org/OTA/2003/05">
<Success/>
<EchoData>Hello World!!</EchoData>
</OTA_PingRS>
I included the same parameter in the http POST request in NodeJS in the options variable in the headers object. (see code below)
Still I recieve the following response: 200 "'otaRQ' is missing in post-form data!"
So my question is, how do I get the same response like with SoapUI-5.3.0?
Thanks a lot for the efforts!
var http = require('http');
var body = '<OTA_PingRQ xmlns="http://www.opentravel.org/OTA/2003/05" Version="3.30" TimeStamp="2011-07-24T10:07:24" Target="Production"> <EchoData><![CDATA[Hello World!!]]></EchoData> </OTA_PingRQ>'
var postRequest = {
hostname: "cultswitch.cultuzz.de",
path: "/cultswitch/processOTA",
method: "POST",
port: 8080,
headers: {
'otaRQ': '<OTA_PingRQ xmlns="http://www.opentravel.org/OTA/2003/05" Version="3.30" TimeStamp="2011-07-24T10:07:24" Target="Production"> <EchoData><![CDATA[Hello World!!]]> </EchoData> </OTA_PingRQ>',
'Cookie': "cookie",
'Content-Type': 'text/xml',
'Content-Length': Buffer.byteLength(body)
}
};
var buffer = "";
var req = http.request( postRequest, function( res ) {
console.log( res.statusCode );
var buffer = "";
res.on( "data", function( data ) { buffer = buffer + data; } );
res.on( "end", function( data ) { console.log( buffer ); } );
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
req.write( body );
req.end();
Excerpt from channel manager provider:
The data exchange will be carried out using the standard HTTP protocol. The
request message should be sent as POST-DATA within a parameter called 'otaRQ'
and the reply message will be written directly into the HTTP-Response by the
CultSwitch. CultSwitch accepts request in "text/xml" format only. CultSwitch also
supports gzip compression for every request and response. The requesting system
should set proper request headers. "PrimaryLangID" is mandatory to post any
request to CultSwitch.
I see that it requires your parameter be called otaRQ. You have not done that. Try this:
var body = 'otaRQ=<OTA_PingRQ xmlns="http://www.opentravel.org/OTA/2003/05" Version="3.30" TimeStamp="2011-07-24T10:07:24" Target="Production"> <EchoData><![CDATA[Hello World!!]]></EchoData> </OTA_PingRQ>'
Since your error message says that you are missing post-form data, try setting your content-type header to "application/x-www-form-urlencoded".
So, 'Content-type': 'application/x-www-form-urlencoded',

Categories