How do I POST to Google's OAuth 2.0 endpoints for authorization?
I'm building a chrome extension in React and have been following Google's documentation. It seems pretty straightforward but I'm not fully grasping the mechanics of the implementation.
For example, in my popup.js file I call my background.js file which performs and axios POST request to the created redirect url. Step 3 in the guide says that Google will prompt the user for consent, however, that never happens. I get a 200 response but not sure where to go after that.
What am I doing wrong? Thank you!
axios
.post(
`https://accounts.google.com/o/oauth2/v2/auth?
scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly&
include_granted_scopes=true&
response_type=token&
state=state_parameter_passthrough_value&
redirect_uri=https%3A//oauth2.example.com/code&
client_id=client_id` //actual values added
)
.then(function (response) {
console.log('RESPONSE', response);
});
Step 2 in that document is titled
Step 2: Redirect to Google's OAuth 2.0 server
You are trying to do an XHR request with POST.
The document provides sample code both with and without their client library. Without the client library, you can see that it is a GET request using a form (which changes the URL in the browser, effectively redirecting):
/*
* Create form to request access token from Google's OAuth 2.0 server.
*/
function oauthSignIn() {
// Google's OAuth 2.0 endpoint for requesting an access token
var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';
// Create <form> element to submit parameters to OAuth 2.0 endpoint.
var form = document.createElement('form');
form.setAttribute('method', 'GET'); // Send as a GET request.
form.setAttribute('action', oauth2Endpoint);
// Parameters to pass to OAuth 2.0 endpoint.
var params = {'client_id': 'YOUR_CLIENT_ID',
'redirect_uri': 'YOUR_REDIRECT_URI',
'response_type': 'token',
'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly',
'include_granted_scopes': 'true',
'state': 'pass-through value'};
// Add form parameters as hidden input values.
for (var p in params) {
var input = document.createElement('input');
input.setAttribute('type', 'hidden');
input.setAttribute('name', p);
input.setAttribute('value', params[p]);
form.appendChild(input);
}
// Add form to page and submit it to open the OAuth 2.0 endpoint.
document.body.appendChild(form);
form.submit();
}
Related
I'm working on google oauth2 request code. I need to click on one button application and open a new window to start google oauth2 flow. I have a redirect uri setted as parameter. I write this function but I have this error into setInterval function:
Blocked a frame with origin from accessing a cross-origin frame
this is the function:
oauth2SignIn() {
// Google's OAuth 2.0 endpoint for requesting an access token
const oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';
// Create element to open OAuth 2.0 endpoint in new window.
let form = document.createElement('form');
form.setAttribute('method', 'GET'); // Send as a GET request.
form.setAttribute('target', 'previewWindow');
form.setAttribute('id', 'googleFormAuth');
form.setAttribute('action', oauth2Endpoint);
// Parameters to pass to OAuth 2.0 endpoint.
const params = {
client_id: environment.GAPI_CLIENT_ID,
redirect_uri: 'https://my-app/services/api/api/google/calendar/callback',
scope: 'https://www.googleapis.com/auth/calendar.readonly',
state: 'try_sample_request',
response_type: 'code',
access_type: 'offline',
prompt: 'consent',
};
// Add form parameters as hidden input values.
for (let p in params) {
let input = document.createElement('input');
input.setAttribute('type', 'hidden');
input.setAttribute('name', p);
input.setAttribute('value', params[p]);
form.appendChild(input);
}
// Add form to page and submit it to open the OAuth 2.0 endpoint.
document.documentElement.appendChild(form);
let newWin = window.open('', 'previewWindow', 'popup');
form.submit();
setInterval(()=> {
if(newWin.location.href.includes('my-app') {
newWin.close();
}
})
}
My focus is to close the newWin when url changed. I have also to read the document because sometimes I have an error printed in the dom, like 401 error by the server. Is it possible to solve this problem?
maybe add the try/catch in setInterval is available.
setInterval(()=> {
try {
if(newWin.location.href.includes('my-app') {
newWin.close();
}
} catch (e) {
// somthing
}
})
Hi Andy88 I hope I am not too late,
since google has changed its authentication-mode to one-touch from Google-Identity, I was in a similar position like you.
I am using as redirection URL my backend-endpoint /auth/redirected/:google to retrieve the refresh token, access token, id token ... and needed to send the access token to the client. So far so good. When the access token received at my popup I did not got out the data. As I wanted to grab the data from the popup I got the following error: Blocked a frame with origin from accessing a cross-origin frame angular. This obviously has its security reason.
Then I figured a way out, which is working. I hope it is not the worst way, if so, just let me know a better way.
From my backend I am sending an res.redirect('https://hereismyURL/login?status=200&token='+JSON.stringify(access_token));
When the popup gets redirected to this link I am looking for the params and save this to local storage.
While this my waiting page has a setinterval on the localstorage variable and if the variable is set I window.close the popup, read the token and to what is needed (if authentication works -> proceed, if not -> make error handling)
I hope this helps..
I have an API accepts POST request while no parameter or data is required. The following post request in Javascript works fine.
function ban () {
let user_id = $('#user_id').val();
let form = document.createElement('form');
form.setAttribute('method', 'post');
form.setAttribute('action', 'myApiUrl');
form.style.display = 'hidden';
document.body.appendChild(form)
form.submit();
}
But with Python requests.post("myApiUrl"), it gives 401 Response. What did I miss?
I guess I figured it out.
I think this API does require authorization but it could be a Cookie-based auth. The JS code worked because I have logged in before in the same browser and JS code read the authorization content from the cookies.
I am trying to develop a two-tier web application with MarkLogic-9 employing server side JavaScript and HTTP app servers. I have a simple page that prompts for username/password and sends a GET request via Ajax to the app server (application-level authentication).
My login.sjs script:
//generate object with field names from Request params
var params ={}; //JSON parsed URL parameters
var field_names = xdmp.getRequestFieldNames().toArray();
for(var fname_idx in field_names){
params[field_names[fname_idx]] = String(xdmp.quote(xdmp.getRequestField(String(field_names[fname_idx]))));
}
//get username and password from passed paramters
var username = params.username;
var password = params.password;
var ret = xdmp.login(username,password);
ret;
I have tested this and verified that it works by printing the xdmp.currentUser().
The login page then redirects to a home page that displays basic user info. My problem is that I cannot figure out how to preserve the current user's session after the client-side redirect to the homepage.
The app server has application-level authentication and a default user called Login-User, which is a custom user that has only the privileges necessary to log in (xdmp:login). The app server is hosted on localhost:8601. I have found that when I run login.sjs directly from the browser (i.e. typing localhost:8601/login.sjs?username=test_user&password=test_password), my browser gets a cookie with the sessionID. However, when I run the login.sjs via an Ajax GET request, my browser does not get any cookies. I don't know if this is the issue but I though it might be worth mentioning.
I am still a MarkLogic novice so I may be going about this the completely wrong way. Basically, how do I go about continuing a single user's session after redirecting to a new page? Do I use cookies to save the sessionID? Should I preserve the username and password in local storage and log in every time the website invokes a new .sjs file?
For completeness, here is the client side js I use to make the Ajax call to login. Pretty self-explanatory. The login.sjs file just returns true/false if the login was successful.
function createLoginEar(){
$("#login-button").click(function(event){
var un = $("#username").val();
var pw = $("#password").val();
if(un){
params.username = $("#username").val();
}
if(pw){
params.password = $("#password").val();
}
event.preventDefault(); //prevent form from clearing
console.log("input entered");
$.ajax({
type: "GET",
url: url,
data: params,
success: function(data){
if(data == "true"){
console.log("worked");
window.location.href = "homepage.html";
} else{
invalidLogin();
}
},
error: function(data){
invalidLogin();
}
})
})
}
The problem is that once the page redirects to homepage.html, there seems to be no memory of the user having logged in and when homepage.html calls any .sjs file, the user resets to the default which is "Login-User".
Thanks in advance.
I suggest you look at Chapter 15 of the security guide.
There is a sample of application level authentication using Custom Login Pages.
Lastly, the sample of IP-based login is not what you need, but shows you how to use xdmp.Login to switch users from the default application user.
I think that with all of that covered (not much to it really), you will be able to walk backthrough your setup and re-work it.
The issue was that my browser was not collecting cookies from the login because of issues that are over my head, but I found the answer in another post so this may be a duplicate.
Get and store cookie (from Set-Cookie) from an AJAX POST response.
I just had to include the following line in my ajax request:
xhrFields: { withCredentials: true },
Since this will throw an error if you have a wildcard in you Access-Control-Allow-Origin header, I also had to change this line:
xdmp.addResponseHeader('Access-Control-Allow-Origin', '*');
to this:
xdmp.addResponseHeader('Access-Control-Allow-Origin', 'http://localhost:8010');
And now my browser collects cookies.
I need to retrieve some data from Google Search Console (Webmaster Tools) using a service account.
So far I've been able to retrieve an access_token for the service account which I need to append to the url of the request. The problem is that I can't find a way to do so, this is the code i'm using:
function retrieveSearchesByQuery(token)
{
gapi.client.webmasters.searchanalytics.query(
{
'access_token': token,
'siteUrl': 'http://www.WEBSITE.com',
'fields': 'responseAggregationType,rows',
'resource': {
'startDate': formatDate(cSDate),
'endDate': formatDate(cEDate),
'dimensions': [
'date'
]
}
})
.then(function(response) {
console.log(response);
})
.then(null, function(err) {
console.log(err);
});
}
This is the url called by the function:
https://content.googleapis.com/webmasters/v3/sites/http%3A%2F%2Fwww.WEBSITE.com/searchAnalytics/query?fields=responseAggregationType%2Crows&alt=json"
Instead it should be something like this:
https://content.googleapis.com/webmasters/v3/sites/http%3A%2F%2Fwww.WEBSITE.com/searchAnalytics/query?fields=responseAggregationType%2Crows&alt=json&access_token=XXX"
The gapi.client.webmasters.searchanalytics.query doesn't recognize 'access_token' as a valid key thus it doesn't append it to the url and that's why I get a 401 Unauthorized as response.
If I use 'key' instead of 'access_token' the parameter gets appended to the url but 'key' is used for OAuth2 authentication so the service account token I pass is not valid.
Does anyone have a solution or a workaround for this?
If your application requests private data, the request must be authorized by an authenticated user who has access to that data. As specified in the documentation of the Search Console API, your application must use OAuth 2.0 to authorize requests. No other authorization protocols are supported.
If you application is correctly configured, when using the Google API, an authenticated request looks exactly like an unauthenticated request. As stated in the documentation, if the application has received an OAuth 2.0 token, the JavaScript client library includes it in the request automatically.
You're mentioning that you have retrieved an access_token, if correctly received, the API client will automatically send this token for you, you don't have to append it yourself.
A very basic workflow to authenticate and once authenticated, send a request would looks like the following code. The Search Console API can use the following scopes: https://www.googleapis.com/auth/webmasters and https://www.googleapis.com/auth/webmasters.readonly.
var clientId = 'YOUR CLIENT ID';
var apiKey = 'YOUR API KEY';
var scopes = 'https://www.googleapis.com/auth/webmasters';
function auth() {
// Set the API key.
gapi.client.setApiKey(apiKey);
// Start the auth process using our client ID & the required scopes.
gapi.auth2.init({
client_id: clientId,
scope: scopes
})
.then(function () {
// We're authenticated, let's go...
// Load the webmasters API, then query the API
gapi.client.load('webmasters', 'v3')
.then(retrieveSearchesByQuery);
});
}
// Load the API client and auth library
gapi.load('client:auth2', auth);
At this point, your retrieveSearchesByQuery function will need to be modified since it doesn't need to get a token by argument anymore in order to pass it in the query. The JavaScript client library should include it in the request automatically.
You can also use the API Explorer to check what parameters are supported for a specific query and check the associated request.
If you need to use an externally generated access token, which should be the case with a Service Account, you need to use the gapi.auth.setToken method to sets the OAuth 2.0 token object yourself for the application:
gapi.auth.setToken(token_Object);
I am using AngularJS and trying to work with Google's reCAPTCHA,
I am using the "Explicitly render the reCAPTCHA widget" method for displaying the reCAPTCHA on my web page,
HTML code -
<script type="text/javascript">
var onloadCallback = function()
{
grecaptcha.render('loginCapcha', {
'sitekey' : 'someSiteKey',
'callback' : verifyCallback,
'theme':'dark'
});
};
var auth='';
var verifyCallback = function(response)
{
//storing the Google response in a Global js variable auth, to be used in the controller
auth = response;
var scope = angular.element(document.getElementById('loginCapcha')).scope();
scope.auth();
};
</script>
<div id="loginCapcha"></div>
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script>
So far, I am able to achieve the needed functionality of whether the user is a Human or a Bot,
As per my code above, I have a Callback function called 'verifyCallback' in my code,
which is storing the response created by Google, in a global variable called 'auth'.
Now, the final part of reCAPCHA is calling the Google API, with "https://www.google.com/recaptcha/api/siteverify" as the URL and using a POST method,And passing it the Secret Key and the Response created by Google, which I've done in the code below.
My Controller -
_myApp.controller('loginController',['$rootScope','$scope','$http',
function($rootScope,$scope,$http){
var verified = '';
$scope.auth = function()
{
//Secret key provided by Google
secret = "someSecretKey";
/*calling the Google API, passing it the Secretkey and Response,
to the specified URL, using POST method*/
var verificationReq = {
method: 'POST',
url: 'https://www.google.com/recaptcha/api/siteverify',
headers: {
'Access-Control-Allow-Origin':'*'
},
params:{
secret: secret,
response: auth
}
}
$http(verificationReq).then(function(response)
{
if(response.data.success==true)
{
console.log("Not a Bot");
verified = true;
}
else
{
console.log("Bot or some problem");
}
}, function() {
// do on response failure
});
}
So, the Problem I am actually facing is that I am unable to hit the Google's URL, Following is the screenshot of the request I am sending and the error.
Request made -
Error Response -
As far as I understand it is related to CORS and Preflight request.So what am I doing wrong? How do I fix this problem?
As stated in google's docs https://developers.google.com/recaptcha/docs/verify
This page explains how to verify a user's response to a reCAPTCHA challenge from your application's backend.
Verification is initiated from the server, not the client.
This is an extra security step for the server to ensure requests coming from clients are legitimate. Otherwise a client could fake a response and the server would be blindly trusting that the client is a verified human.
If you get a cors error when trying to sign in with recaptcha, it could be that your backend server deployment is down.