Error 500 backendError with Gmail API and Google APIs Node Client - google-api-nodejs-client

I'm trying to use the new Gmail API with the Google API Node client. I created a new project from the developer console, set up a new "Service Account" Client ID, and enabled access to the API.
As a proof of concept, I am simply trying to list the threads in my inbox. When I enable the OAuth 2.0 toggle for the API explorer and enter my email address, the request succeeds and I see a JSON response with data.
Now I try to do the same in Node:
var googleapis = require('googleapis');
var SERVICE_ACCOUNT_EMAIL = '...SNIP...';
// generated by: openssl pkcs12 -in ...SNIP...p12 -out key.pem -nocerts -nodes
var SERVICE_ACCOUNT_KEY_FILE = 'key.pem';
var jwt = new googleapis.auth.JWT(
SERVICE_ACCOUNT_EMAIL,
SERVICE_ACCOUNT_KEY_FILE,
null,
['https://www.googleapis.com/auth/gmail.readonly']);
googleapis
.discover('gmail', 'v1')
.execute(function(err, client) {
jwt.authorize(function(err, result) {
if(err) console.error(err);
else console.log(result);
client.gmail.users.threads.list()
.withAuthClient(jwt)
.execute(function(err, result) {
if(err) console.error(err);
else console.log(result);
});
});
});
First I print the results of the authorize() call, which looks like it returns a token, so I think I have all the OAuth stuff setup properly:
{ access_token: '...SNIP...',
token_type: 'Bearer',
expires_in: 1404277946,
refresh_token: 'jwt-placeholder' }
Then I try to actually use the API, but I get an error:
{ errors:
[ { domain: 'global',
reason: 'backendError',
message: 'Backend Error' } ],
code: 500,
message: 'Backend Error' }
At this point, I don't know what else to try. I think the OAuth stuff is working properly, because I haven't gotten any authentication errors. I also think the API itself is working and my account is fine, because I can use it through the API Explorer. I don't see any indication that the Node library is at fault either. In short, I have no idea what the problem is. Any ideas?

You are using the Service Account to authenticate your requests to GMail. Your Service Account will not have a Gmail as far as I know, only users have GMail. For this reason you will need to do the OAuth2 flow with the user (see here for example).

Related

Flutter google sign in serverAuthCode

I'm using this flutter plugin google_sign_in 5.2.1
works good I get the response
{displayName: Mario Mc, email: myemail#gmail.com, id: 117816213074325689769, photoUrl: https://lh3.googleusercontent.com/a-/AOh14GgyaPy7ik693hjyIBmtW5IRXUdCXluaeI=s96-c, serverAuthCode: 4/0AX4XfWiqJ1DMfPaDbgf6gOFVMCfgMicyPqGk25bxjKfA4wq7bJCCu-TWRB8c3rAz_g}
My question is: what is serverAuthCode?
And what's id: 117816213074325689769 used for
And after I send that serverAuthCode to the server, how do I verify that code is legit by calling google servers(as you know any person can send a fake serverAuthCode, so we need to verify it before saving it or do something with it)
I want to use PHP in the server side
thanks
What you can do is use this to get the accessToken then send it to the server
// sign In With Google
_googleSignIn.signIn().then((userData) {
userData.authentication.then((googleKey) {
setState(() {
// Retrieve the email from Google auth
googleEmail = userData.email;
googleId = userData.id;
googlePhotoUrl = userData.photoUrl;
googleServerAuthCode = userData.serverAuthCode;
googleAccessToken = googleKey.accessToken;
googleIdToken = googleKey.idToken;
});
print("googleAccessToken");
print(googleAccessToken);
// call your login function if no errors
checkInternetAndLoginWithGoogle();
}).catchError((err) {
print('inner error');
});
}).catchError((err) {
print('error occurred');
print(err);
});
in the server side, you can verify that access token by calling
https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=<access token>
you will get an answer if that token is valid or expired
Also, you can call this to get user info such as name, email, photo etc.
https://www.googleapis.com/oauth2/v3/userinfo?access_token=<access token>

How to specify redirectUrl after logout for Ambassador OAuth2 Filter with Keycloak?

I'm using the Ambassador OAuth2 Filter to perform OAuth2 authorization against Keycloak.
For the logout I use the the RP-initiated logout as described in the Docs of Ambassador
The logout works fine. However I could not figure out how to provide the redirect url needed for Keycloak to redirect to the Login page after successfully logged out. As a result the user stays on the blank logout page of keycloak.
The RP-initiated logout looks as follows
const form = document.createElement('form');
form.method = 'post';
form.action = '/.ambassador/oauth2/logout?realm='+realm;
const xsrfInput = document.createElement('input');
xsrfInput.type = 'hidden';
xsrfInput.name = '_xsrf';
xsrfInput.value = getCookie("ambassador_xsrf."+realm);
form.appendChild(xsrfInput);
document.body.appendChild(form);
form.submit();
I expected that Ambassador provides a way to add the redirect url as a query param or something, but I couldn't find a solution.
Are there any suggestions or workarounds?
I found this in the Ambassador documentation that could be overlooked as I did several times:
Ambassador OAuth2 Settings
protectedOrigins: (You determine these, and must register them with your identity provider) Identifies hostnames that can appropriately set cookies for the application. Only the scheme (https://) and authority (example.com:1234) parts are used; the path part of the URL is ignored.
You will need to register each origin in protectedOrigins as an authorized callback endpoint with your identity provider. The URL will look like {{ORIGIN}}/.ambassador/oauth2/redirection-endpoint.
So it looks like ambassador hard codes the redirection-endpoint (redirect_uri) that you need add to your OAuth2 client in Keycloak.
I found a solution for that, is not the best solution but you will logout using a button.
async function logout() {
const data = new URLSearchParams("realm=keycloak-oauth2-filter.ambassador")
data.append('_xsrf', getCookie("ambassador_xsrf.keycloak-oauth2-filter.ambassador"));
fetch('/.ambassador/oauth2/logout', {
method: 'POST',
body: data
})
.then(function (response) {
if (response.ok) {
return response.text()
} else {
throw "err";
}
})
.then(function (text) {
console.log(text);
})
.catch(function (err) {
console.log(err);
});
}

Using Axios as an Alternative to request in nodejs

I am building a flutter application that requires oauth 1 authorization for one of the third party services I am using. Because flutter oauth 1 package is restricted I decided to use the oauth 1 package that npm provides. This is the code that is used to access the user generated access token from the site.
I previously used request to make a call to the api endpoint first, to access the token and secondly to use the token recieved to make another call to a different resource endpoint
How can I use axios to make the same request, emphasis on the fact that each request needs a hmac-sha1 signed signature in the header.
Thank you.
consumer: {
key: CONSUMER KEY,
secret: CONSUMER SECRET,
},
signature_method: 'HMAC-SHA1',
hash_function(base_string, key) {
return crypto
.createHmac('sha1', key)
.update(base_string)
.digest('base64')
},
})
const request_data = {
url: 'https://www.instapaper.com/api/1/oauth/access_token/',
method: 'POST',
data: { x_auth_username : USERNAME , x_auth_password : PASSWORD , x_auth_mode : 'client_auth' },
}
request(
{
url: request_data.url,
form: request_data.data,
method: request_data.method,
headers: oauth.toHeader(oauth.authorize(request_data)),
},
function(error, response, body) {
// Process your data here
console.log(error);
console.log(response);
console.log(body);
}
)
Finally found the answer for this link to the issue created on github
https://github.com/axios/axios/issues/2771

Passport and SailsJS, how to get isAuthenticated() test to pass

My code keeps failing here when the user tries to login:
isAuthenticated: function (req, res) {
if (req.isAuthenticated()) { return res.json(req.user); }
else { return res.send(401); }
},
It FAILS and I get GET http://localhost:1337/user/authenticated 401 (Unauthorized) in the console, even though the user has entered in a correct email and password.
Where in the code makes that test pass?
I have the related StackOverflow question with more info HERE.
The problem was that my frontend application has a different origin than my backend application, so the AJAX requests will not include the session cookie and req.isAuthenticated() will never return true.
Use the withCredentials options to force it.
$http({ withCredentials: true, ... })

How do I prevent the user from being asked to grant permissions for a Google Apps Marketplace app that the domain administrator has approved

I've been trying to convert an old style Marketplace app to the new way of doing things, but I keep having the user prompted for authorization when they first follow the Universal Navigation link (the app was installed onto the domain via the 'Test Install Flow' link on the marketplace SDK).
When I request the 'openid email' scopes, the prompt appears, but if I specify a separate scope (instead of, not in addition to 'openid email') that is part of the app, it doesn't prompt the user for auth (unless I request offline access, which I want for parity of features with the previous version, but one problem at a time...).
Sample code using the google api nodejs client:
var http = require('http');
var googleapis = require('googleapis');
var url_parse = require('url');
var OAuth = googleapis.auth.OAuth2;
var client = new OAuth(
CLIENT_ID
CLIENT_SECRET
REDIRECT_URL
);
http.createServer(dispatcher).listen(80);
function dispatcher(req, res){
res.writeHead(200, {'Content-Type': 'text/html'});
var url = url_parse.parse(req.url, true);
if(url.pathname === '/auth') {
auth_time(res);
} else if (url.pathname === '/callback') {
console.log('looking good');
client.getToken(url.query.code, function(err, tokens){
console.log(tokens);
client.setCredentials(tokens);
res.end(render_page('yep'));
});
} else {
res.end(render_page('different page'));
console.log(req.url);
}
}
function auth_time(res){
res.end(render_page('<a href="'+client.generateAuthUrl({
scope: 'openid email https://www.google.com/m8/feeds', //Prompts
// scope: 'https://www.google.com/m8/feeds', //Doesn't prompt
include_granted_scopes: 'true',
access_type: 'online'
})+'">Click for auth</a>'));
}
function render_page(contents){
return '<html><head><title>The page</title></head><body>'+contents+'</body></html>';
}
This similar question has an answer suggesting that https://www.googleapis.com/auth/plus.me is added to the list of scopes used on the Marketplace SDK, but that scope is removed on page refresh (I'm guessing it is treated as identical to one or both of https://www.googleapis.com/auth/userinfo.email & https://www.googleapis.com/auth/userinfo.profile which are automatically set as scopes and are not removable.