How to handle the CSRF when implementing Google Identity Services popup code model? - google-authentication

On my site I have implemented Google Identity Services using a popup code model.
To prevent CSRF attacks the docs say this:
With Popup mode, you add a custom HTTP header to your requests, and
then on your server confirm it matches the expected value and origin.
Here is the popup sample code provided in the docs:
const client = google.accounts.oauth2.initCodeClient({
client_id: 'YOUR_GOOGLE_CLIENT_ID',
scope: 'https://www.googleapis.com/auth/calendar.readonly',
ux_mode: 'popup',
callback: (response) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', code_receiver_uri, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// Set custom header for CRSF
xhr.setRequestHeader('X-Requested-With', 'XmlHttpRequest');
xhr.onload = function() {
console.log('Auth code response: ' + xhr.responseText);
};
xhr.send('code=' + code);
},
});
It's my understanding that I need to add a random CSRF code string to the above code, and then verify that on my server.
But where exactly do I put that CSRF code?
I thought that maybe it should go in the code variable like this xhr.send('code=' + 'MY_CSRF_STRING') but when I do that my headers look like this (MY_CSRF_STRING is missing so I cannot verify it on the server):
{
"host":"localhost:5001",
"connection":"keep-alive",
"accept":"*/*",
"access-control-request-method":"POST",
"access-control-request-headers":"x-requested-with",
"origin":"http://localhost:8080",
"user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36",
"sec-fetch-mode":"cors",
"sec-fetch-site":"same-site",
"sec-fetch-dest":"empty",
"referer":"http://localhost:8080/",
"accept-encoding":"gzip, deflate, br",
"accept-language":"en-US,en;q=0.9",
"severity":"INFO",
"message":"googleAuthorization req.headers"
}
And if the CSRF code does not go there, what else is supposed to go in the code variable?

Think I figured this out.
But I would still love to hear from others to make sure this is correct.
In short, this answer outlines that the csrf protection comes from checking for the existance of the custom X-Requested-With header. No need to create your own unique value.
There is also a typo in the Google code sample which created this confusion.
The line xhr.send('code=' + code); should be xhr.send('code=' + response.code);. Because we are taking the auth code from the response and passing it along to the server.

Related

Spotify API demanding a parameter that is not required

I know this is a particular issue that I'm having but I'm struggling with it for ages...If anyone could give me a hint on how to proceed It would be great.
I'm trying to integrate my flutter app to Spotify API and according to Spotify docs, after the user accepted the conditions at their site, it would be redirected back with a code - In my application I'm already retrieving this code. After that, I need to make a request for another endpoint to "trade" my code for an auth.
The docs demand a POST with 3 parameters(grant_type,redirect_uri and code). But even though I send all of this informations I get a (400 -Bad Request) with the description:
{"error":"invalid_request","error_description":"code_verifier
required"}
But note that code_verifier is only required for a PKCE request, as far as I understand.
My post method is the following:
Future getUserAccessToken(String authorizationCode) async {
// Get the Access Token in exchange for the Authorization Code
var url = 'https://accounts.spotify.com/api/token';
var body = {
'grant_type': 'authorization_code',
'code': authorizationCode,
'redirect_uri': redirectUrl,
};
var response = await post(Uri.parse(url), body: body, headers: {
'Content-Type': "application/x-www-form-urlencoded",
'Authorization':
'Basic ${base64Encode(utf8.encode('$clientID:$clientSecret'))}',
});
}
They also demand this at the authorization header:
Base 64 encoded string that contains the client ID and client secret key. The field must have the format: Authorization: Basic
I would recommend going forward with PKCE. It will protect your app against CSRF and authorization code injection attacks. It's also not too bad to implement.
All you should have to do is:
Generate a random string using the characters A-Z, a-z, 0-9, and the punctuation characters -._~ (hyphen, period, underscore, and tilde), between 43 and 128 characters long. This is your code_verifier
Hash that string with SHA256. This is your code_challenge.
Send those values in the requests as the documentation indicates, which means base64 url encoding them, as well. The Spotify doc says "The body of this POST request must contain the following parameters encoded in application/x-www-form-urlencoded".
Also, in case you haven't seen it, there's a OAuth flow example implemented in JS here: https://github.com/spotify/web-api-auth-examples/blob/master/authorization_code/app.js Their copy of this portion looks like:
var authOptions = {
url: 'https://accounts.spotify.com/api/token',
form: {
code: code,
redirect_uri: redirect_uri,
grant_type: 'authorization_code'
},
headers: {
'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64'))
},
json: true
};
request.post(authOptions, function(error, response, body) {...})
The example has "request": "~2.83.0" in the package.json.
Notice how they use form which I would assume is base 64 url encoding the body, as the docs indicate. Not sure how the post method you're using with dart/flutter works with respect to base 64 url encoding, so that's something to investigate, as well.
Credit to https://www.oauth.com/oauth2-servers/pkce/authorization-request/ for providing some of the text in this response

Protractor not wait for https.request callback

I hit a Protractor issue, if I run this within "ts-node demo.js" it works well and can output the response code and response body.
But if I run this in Protractor it block, then can't get the expected output response code and body, seems like Protractor won't wait for the callback.
In this situation, how can I output the response code and boday?
it(Check manifests V2 api, async() => {
const https = require('https')
const options = {
hostname: 'demo-quayecosystem-quay-quay.com',
port: 443,
path: '/v2/quay/multiarchdemo/manifests/latest',
method: 'GET',
headers: {
'Accept': 'application/vnd.docker.distribution.manifest.list.v2+json'
}
}
https.request(options, res => {
browser.getTitle().then(()=>{
console.log("starting.........");
console.log(`statusCode: ${res.statusCode}`)
res.on('data', d => {
process.stdout.write(d)
})
})
})
})
The function above is an async and when you make your function async, then you should use the await keyword in front of the https.request or return it like this.
return https.request...
And another thing that could lead to problems are...
https.request is making a direct HTTP request, without using a browser
broser.getTitle() is using the browser to interact with the web page.
Be aware - browser uses the browser, and http.request uses direct HTTP from node.js - these are two different things. And it will lead to unpredictable things to mix them. So consider if you want to "test as a user" and then use the browser, or if you want to do the fastest possible test and "test as a website or javascript" and use HTTP.request.
Try reading the async/await page on the Protractor website.
https://www.protractortest.org/#/async-await
If you are puzzled about async/await consider seeing the video from Fun Fun Function on promises https://www.youtube.com/watch?v=568g8hxJJp4&t=251s.

CodePen -> getJSON works only in IE but does not in Chrome or FF, why is that?

please help me with a tip
I have created this pen https://codepen.io/Dimas_X/pen/yKEzBE?editors=0010
$(function() {
const func = function(){
let counter = 0;
return function(){
$.getJSON("https://quotesondesign.com/wp-json/posts?filter[orderby]=rand&filter[posts_per_page]=1",
function(data){
$('p').html(data[0].content + '<p>'+ data[0].title +'</p>');
});
counter++;
$('span').html(counter);
}
}
$('button').on('click', func());
// $('button').trigger('click');
});
and it works properly only in IE. In Chrome it works when devTools is open.
Counter of clicks works, mean click event works.
There are no any mistakes in console. Thx in advance.
I am seeing the request get made several times within the network tab of firefox and chrome, but the response is the same every time. Your application is populating the first response correctly, it just seems as if the first request is being cached.
If you have access to the web server code, it look like wordpress is being used because of wp json. You should add the following code to ensure the response isn't being cached
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
Once that is Done I think your code should work just fine.
You can also on the frontend perform a query to a "unique url" each time by making the url .getJSON("https://quotesondesign.com/wp-json/posts?filter[orderby]=rand&filter[posts_per_page]=1?_=" + new Date().getTime() to prevent caching

OpenTok Rest Service Invalid JWT Error on Fiddler Request

I'm trying to create OpenTok session by Rest services with JWT object as suggested. I tried to generate session with Fiddler.
Here is my fiddler request (JWT string has been changed with *** partially for security reasons)
POST https: //api.opentok.com/session/create HTTP/1.1
Host: api.opentok.com
X-OPENTOK-AUTH: json_web_token
Accept: application/json
Content-Length: 172
eyJ0eXAiOiJKV1QiL******iOiJIUzI1NiJ9.eyJpc3MiOjQ1NzM******OiJkZW5l******XQiOjE0ODI3OTIzO***SOMESIGNEDKEYHERE***.izvhwYcgwkGCyNjV*****2HRqiyBIYi9M
I got 403 {"code":-1,"message":"Invalid token format"} error probably means my JWT object is not correct. I tried creating it using http://jwt.io (as opentok suggests) and other sites and all seems correct and very similar to the one on tokbox (opentok) site.
I need an explanation to fix it and create a session.
May it be because I am using opentok trial?
JWT creation Parameters
I had the same problem. I resolved the error by setting the correct key-value pairs for the payload part.
Example of my payload is as follows in C#:
var payload = new Dictionary<string, object>()
{
{ "iss", "45728332" },
{ "ist", "project" },
{ "iat", ToUnixTime(issued) },
{ "exp", ToUnixTime(expire) }
};
The value of the "ist" should be set to "project", not the actual name of your project.
Update: Looking at your screenshot, I can say you have not set the secret key (here, it's your ApiKeySecret from TokBox account > project) at the very bottom right.
OK I have found the answer at last,
Your Opentok API Secret key should not be used directly as Sign parameter. In java as shown below, it should be encoded first.
Base64.encodeToString("db4******b51a4032a83*******5d19a*****e01".getBytes(),0)
I haven't tried it on http://jwt.io and fiddler but it seems it will work on it too. Thanks. Full code is below;
payload = Jwts.builder()
.setIssuedAt(currentTime)
.setIssuer("YOUR_OPENTOK_KEY")
.setExpiration(fiveMinutesAdded)
.claim("ist", "project")
.setHeaderParam("typ","JWT")
.signWith(SignatureAlgorithm.HS256, Base64.encodeToString("YOUR_OPENTOK_SECRET".getBytes(),0))
.compact();
return payload;

Grails REST plugin using HTTPBuilder for HTTPS

I have a service method in grails that was working fine.
It pulls a JSON via a GET request. After moving to prod we had to change the protocol to HTTPS and now I am getting an exception.
Is there anything I have to change to use the HTTPS protocol? I look all over The HTTPBuilder Documentation and I could not find a single reference to using HTTPS. I also could not find a example on Google.
def reportList = new ArrayList()
def result
//TODO Dynamic PatientKey
def http = new HTTPBuilder( 'https://mydomain/servicename?key=' + key )
reportList = null
http.request( GET, JSON ) { req ->
headers.Accept = 'application/json'
response.success = { resp, reader ->
reportList = reader.getAt("patientReports")
}
}
}
[ reportList : reportList ]
Whats the exception you are getting?
please check that SSL certificate is valid for the website. More here.
http://groovy.codehaus.org/modules/http-builder/doc/ssl.html
This Grails plugin solution works well in a test or local env because Same-Origin Policy will prevent you from implementing a front-end jQuery AJAX call since the domains are different.
In Prod, since HTTPS was used, and since the domains are the same, a jQuery AJAX call works much better then having the logic in the controller and using the REST plugin.
$.getJSON('${YOUR_URL}', function(data){ var yourData = data.yourData; //Operate on data here });