How to fetch access token from OAuth used in google action for smart home - actions-on-google

For my smart home action I used fake auth as shown in codelab- smartwasher application. (For testing purpose ). The app is working fine. I have build my own code to work with my devices(Switches). Now When I am implementing OAuth which uses my own custom OAuth server. I am not able to figure out how to implement it in my code. The OAuth is working as needed when I tested. But I want help in integrating it with google action. I am facing problem fetching access token.
The code is as follows:
exports.fakeauth = functions.https.onRequest((request, response) => {
const responseurl = util.format('%s?code=%s&state=%s',
decodeURIComponent(request.query.redirect_uri), request.query.code,
request.query.state);
console.log('*********'+responseurl);
return response.redirect(responseurl);
});
exports.faketoken = functions.https.onRequest((request, response) => {
const grantType = request.query.grant_type
? request.query.grant_type : request.body.grant_type;
const secondsInDay = 86400; // 60 * 60 * 24
const HTTP_STATUS_OK = 200;
console.log(`Grant type ${grantType}`);
let obj;
if (grantType === 'authorization_code') {
obj = {
token_type: 'bearer',
access_token: '123access',
refresh_token: '123refresh',
expires_in: secondsInDay,
};
} else if (grantType === 'refresh_token') {
obj = {
token_type: 'bearer',
access_token: '123access',
expires_in: secondsInDay,
};
}
response.status(HTTP_STATUS_OK)
.json(obj);
console.log('********** TOKEN **********',response);
});
The above code executes with fake auth.
Why is is not executing when I am implmenting custom OAuth?
Do I need to do any changes for clienID and secret in firebase?
How to fetch access token returned by OAuth?
Kindly help. I am new to node.js.

The authorization code that will come back in requests will be in the header, as an Authorization field. Here's a way to pull it out using Node.js.
function getToken(headers) {
// Authorization: "Bearer 123ABC"
return headers.authorization.substr(7);
}

Related

trying to a send a "string" refresh token in request body using axios post

I am trying to create a post request for refreshing the token using axios.post
my code is the following
I can't seem to pass the parameters in the correct order/way
in the api request its like this
export async function refreshTheCurrentToken() {
const url = RefreshCurrentToken;
const refreshTokenHardCoded = "xxxxxx"
const refreshedToken = await axios
.post(url, {
params: {
string: refreshTokenHardCoded,
},
})
.catch((error) =>
console.log(error + " 🟥error refreshing the current token")
);
// console.log(url + " URL FOR REFRESHING TOKEN");
console.log(refreshedToken + " NEW TOKEN GENERATED ✅");
// return refreshToken;
}
my response is the following
LOG AxiosError: Request failed with status code 400 🟥error refreshing the current token
LOG undefined NEW TOKEN GENERATED ✅
tried setting the refresh token in a static way. on postman it works but on my react native project it doesn't.
tried converting the refresh token a string, tried creating an object to hold the refresh token

How to manage contacts using Contacts API in our website correctly?

I was trying to integrate Google Contacts API to manage the contacts in my website.
I've done the following things:
I've created an application in google developer console and added http://localhost:4200 as URIs & Authorized redirect URIs.
Enabled 'Contacts API'.
I've added the following in my index.html (I've replaced {clientID} with my original client ID (of course):
<script>
function loadAuthClient() {
gapi.load('auth2', function() {
gapi.auth2.init({
client_id: '{clientID}'
}).then(() => {
console.log("success");
}).catch(err => {
console.log(err);
});
});
}
</script>
<script src="https://apis.google.com/js/platform.js?onload=loadAuthClient" async defer></script>
<meta name="google-signin-client_id" content="{clientID}">
Signed in successfully using:
gapi.auth2.getAuthInstance().signIn().then(() => {
console.log("Logged in")
}).catch(err => {
console.log(err);
});
Tried fetching the contacts using the following:
var user = gapi.auth2.getAuthInstance().currentUser.get();
var idToken = user.getAuthResponse().id_token;
var endpoint = `https://www.google.com/m8/feeds/contacts/`;
var xhr = new XMLHttpRequest();
xhr.open('GET', endpoint + '?access_token=' + encodeURIComponent(idToken));
xhr.setRequestHeader("Gdata-Version", "3.0");
xhr.onreadystatechange = function () {
if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
window.alert(xhr.responseText);
}
};
xhr.send();
But I'm getting the error:
Access to XMLHttpRequest at 'https://www.google.com/m8/feeds/contacts/?access_token={I removed the access token}' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Can someone please guide me where I'm going wrong?
My original response was off the mark. The actual answer is much simpler.
In step 4, try changing your endpoint:
var endpoint = `https://www.google.com/m8/feeds/contacts/default/full`;
In my local tests, this resulted in the expected response.
Another suggestion is to add alt=json to your query, so that you get easy to parse JSON payload. Otherwise you'll get a nasty XML payload in the response.
Here's the updated step 4 with these changes:
var endpoint = `https://www.google.com/m8/feeds/contacts/default/full`;
var xhr = new XMLHttpRequest();
xhr.open('GET', endpoint + '?access_token=' + encodeURIComponent(idToken) + '&alt=json');
xhr.setRequestHeader("Gdata-Version", "3.0");
xhr.onreadystatechange = function () {
if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
window.alert(xhr.responseText);
}
};
xhr.send();
Here's my original response, just in case it helps someone else.
I suspect that you'll need to add http://localhost:4200 to your list of "Authorized JavaScript origins" for the OAuth Client that you are using.
Edit your OAuth 2.0 Client ID and add the URI to the Javascript origins as below:
The other section on that page, Authorized Redirect URIs, only permits the OAuth flow to be redirected back to your web app. Often your web app server will actually consume the APIs so Google doesn't automatically permit CORS access to these APIs to keep things secure.

Authentication Service Webhook (endpoint) in MongoDB Stitch

Is there a way to create a service webhook to register new user with Email and Password?
I can see the way via SDK but I am trying to do the same via service webhook function?
for e.g.
exports = function(payload) {
const { Stitch, AnonymousCredential } = require('mongodb-stitch-server-sdk');
var queryArg = payload.query || '';
var body = {};
if (payload.body) {
body = EJSON.parse(payload.body.text());
}
return body.email;
};
I am not able to access mongodb-stitch-server-sdk here. Am I going in the right direction?
So you won't be able to use the SDK inside of a webhook. What you could do is add the user by hitting the Stitch Admin API.
Make an API key in Atlas. Go to the user dropdown in the top right corner > Account > Public API Access. Click on "Generate", and save the API key that gets created.
Make an HTTP service in Stitch.
In your webhook, use the Admin API to authenticate and create a new user. The code will look something like:
exports = function(payload) {
const http = context.services.get("http");
return http.post({
url: "https://stitch.mongodb.com/api/admin/v3.0/auth/providers/mongodb-cloud/login",
body: JSON.stringify({
username: "<atlas-username>",
apiKey: "<atlas-apiKey>"
})
}).then(response => EJSON.parse(response.body.text()).access_token).then(accessToken => {
return http.post({
url: "https://stitch.mongodb.com/api/admin/v3.0/groups/<groupId>/apps/<appId>/users",
headers: {
Authorization: ["Bearer " + accessToken]
},
body: JSON.stringify({
email: "<email-from-payload>",
password: "<password-from-payload>"
})
});
});
};
After Comment:
const http = context.services.get("http"); needs to be the configured ServiceName instead of http as const http = context.services.get("<SERVICE_NAME>");

Disable redirect in fetch request using React native

I'm trying to crawl a web using React Native which has no API. It's written in PHP.
To log an user, a POST request must be sent. The response returns a cookie with a PHPSessid cookie which I must capture to use in subsequent requests.
I would like to capture the cookie value, buy the POST response is a 302 and the redirection is followed automatically, so I can't see the cookie. In node I was able to do it with redirect:manual, but it does not work in react native.
The cookie is sent automatically in subsequent requests, buy I'm trying to manage cookies by hand with react-native-cookie and I'd like to know if it's possible.
Do you know a way to stop the redirection?
I've been checking the code and what I did was the following:
Clear all cookies
Launch an empty login request
Capture the PHPSessID coookie
Launch a login request with that PHPSessID
After that, the subsequent fetch requests would have automatically a PHPSessID cookie with a valid logged in user, so we can use the site with simple fetchs
Here is some code, but the important thing is that you do a first empty login request, capture the PHPSessid and launch the real login request with that PHPSessid.
This would be the main function:
import Cookie from 'react-native-cookie';
// I think this is used only to clear the cookies
function login(user, pass){
// clear all cookies for all domains
// We need to start withouth authorization token
Cookie.clear();
const makeLoginRequest = (sessid) =>
makeLoginRequestForUserAndPass(user,pass,sessid);
return makeInitialRequest()
.then(getSessionIDFromResponse)
.then(makeLoginRequest)
.then(checkIfLoggedAndGetSessionID);
}
The initial request is a request to the login script. Note that I used GET because it worked with my site, perhaps an empty post would be necessary:
function makeInitialRequest() {
const INIT_PATH = '/index.php?r=site/login';
const INIT_URL = site + INIT_PATH;
const request = new Request(INIT_URL, options....);
return fetch(request);
}
We have the session ID in the response. I used a simple regex to extract it. Note that we are not logged in; PHP has created a session and that's what we have here:
function getSessionIDFromResponse(response) {
return getPHPSessIdFromCookie(response.headers.get('set-cookie'));
}
function getPHPSessIdFromCookie(header) {
const regex = /PHPSESSID=(\w*)/;
const match = regex.exec(header);
return match ? match[1] : '';
}
Now the login request. Note that I can't stop redirection here, but I't have to do it because we can have PHPSessid later. Redirection must be set to manual in POST request:
function makeLoginRequestForUserAndPass(user, pass, sessid) {
const request = buildLoginRequest(user, pass, sessid);
return fetch(request);
}
// This is where we build the real login request
function buildLoginRequest(user, pass, sessid) {
const LOGIN_PATH = '/index.php?r=site/login';
const LOGIN_URL = site + LOGIN_PATH;
const fields = [
{name: 'LoginForm[username]', value: user},
{name: 'LoginForm[password]', value: pass},
etc...
];
const data = translateFieldsToURLEncodedData(fields);
const headers = {
'Content-type': 'application/x-www-form-urlencoded',
Cookie: `PHPSESSID=${sessid}`, // HERE is where you put the data
};
const options = { method: 'POST',
headers: headers,
mode: 'cors',
cache: 'default',
agent: proxy,
body: data,
redirect: 'manual' // VERY IMPORTANT: if you don't do it, the cookie is lost
};
return new Request(LOGIN_URL, options);
}
// Simple utility function
function translateFieldsToURLEncodedData(fields){
let pairs = fields.map( (field) => {
return encodeURIComponent(field.name) + '=' + encodeURIComponent(field.value);
});
return pairs.join('&');
}
This is the last part. To see if I was logged in I checked if the response had text belonging to login error's page. I also got the PHPSessid (I think it changed after login, not sure, it was a year ago) but I don't know if I used it, I believe it was included automatically in subsequent requests. I think this part could be simplified an improved:
function checkIfLoggedAndGetSessionID(response) {
return (
checkIfLoggedOK(response)
.then(() => getSessionIDFromResponse(response))
);
}
function checkIfLoggedOK(response){
return getTextFromResponse(response)
.then(throwErrorIfNotLogedOk);
}
function getTextFromResponse(response) {
return response.text();
}
function throwErrorIfNotLogedOk(page) {
if(isErrorPage(page)) throw new Error("Login failed");
}
function isErrorPage(text) {
const ERROR_MESSAGE = 'Something that appears in login failed page of your site';
let n = text.search(ERROR_MESSAGE);
return n !== -1;
}
Hope this can be useful.

google-api-nodejs-client: how to call google+ domain api locally? (by the plusDomains.media.insert)

I am going to use the Nodejs google api client(google-api-nodejs-client) to post a photo to my google+. (I have listed all my code at end of this post.)
Let me introduce a little bit background:
I have created a project on: console.developers.google.com
I have enabled google+ domain API for this project.
I have created credentials for this project as well. (it is a OAuth 2.0 client ID)
I have a little bit experience of using the client (google-api-nodejs-client) and I can post images and files to my google drive by it.
However, posting to google+ photo is different, the auth is the key different. I have tried several different ways, but none of them works.
The api always return me this:
{ [Error: Forbidden]
code: 403,
errors: [ { domain: 'global', reason: 'forbidden', message: 'Forbidden' } ] }
I also found this:
Warning: The Google+ Sign-In button and the plus.login scope used by
Google+ Sign-In, are not currently supported for use with the Google+
Domains API. Requests that are made to the Google+ Domains API using
an authentication token granted for the
www.googleapis.com/auth/plus.login scope, or generated by the
Google+ Sign-In button, will fail.
If it doesn't support the sign-button, what does it support?
This page tell me to add a domain delegation (https://developers.google.com/+/domains/authentication/delegation), but i haven't push my program into any server, i just try to run it locally.
I was wondering if it is possible to use this client to post photo to google+ by run a nodejs program locally?
var CLIENT_ID = "xxxxxxx.apps.googleusercontent.com";
var CLIENT_SECRET = "xxxxxxxx";
var REDIRECT_URL = "https://xxxxxxx";
var readline = require('readline');
var async = require('async');
var google = require('googleapis');
var request = require('request');
var OAuth2 = google.auth.OAuth2;
var oauth2Client = new OAuth2(CLIENT_ID, CLIENT_SECRET, REDIRECT_URL);
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
function getAccessToken (oauth2Client, callback) {
// generate consent page url
var scopes = [
'https://www.googleapis.com/auth/plus.me',
'https://www.googleapis.com/auth/plus.stream.read',
'https://www.googleapis.com/auth/plus.stream.write',
'https://www.googleapis.com/auth/plus.circles.read',
'https://www.googleapis.com/auth/plus.circles.write',
'https://www.googleapis.com/auth/plus.media.upload'
];
var url = oauth2Client.generateAuthUrl({
access_type: 'offline', // 'online' (default) or 'offline' (gets refresh_token)
scope: scopes, // If you only need one scope you can pass it as string,
key: 'p7UALH460Deqodhvb2zESYya'
});
console.log('Visit the url: ', url);
rl.question('Enter the code here:', function (code) {
// request access token
oauth2Client.getToken(code, function (err, tokens) {
if (err) {
return callback(err);
}
// set tokens to the client
// TODO: tokens should be set by OAuth2 client.
oauth2Client.setCredentials(tokens);
console.dir(tokens);
callback();
});
});
}
getAccessToken(oauth2Client, function () {
var plusDomains = google.plusDomains({ version: 'v1', auth: oauth2Client });
var requestObj = request({url:'http://asset1.cxnmarksandspencer.com/is/image/mands/2643f540b32fe8c6cccdec95b3a2c5239166232f?$editorial_430x430$'});
const Readable = require('stream').Readable;
var iamgeStream = new Readable().wrap(requestObj);
plusDomains.media.insert({
userId: 'me',
collection: 'cloud',
resource: {
name: 'testimage.png',
mimeType: 'image/png'
},
media: {
mimeType: 'image/png',
body: iamgeStream
},
access:{domainRestricted :"true"}
}, callbackFn);
function callbackFn(argument) {
console.dir(argument);
}
});
Thanks you very much!
Peter