Empty request from Google Smart Home SDK - node.js

I'm creating web server to connect my DIY smart home devices to Google Home app.
After authorisation and token request Google server makes POST request to fullfillment URL, but request is empty for some reason. According to documentation, it must be SYNC request, but it does not contain any values, even request ID.
There is error "Couldn't update the setting. Check your Internet connection." on my phone after request.
So why does it happen and how I can fix it?
const app=smarthome({ debug: true, });
app.onSync( async (body) => {
return {
requestId: body.requestId,
payload: {
agentUserId: "agentUserId",
devices // devices list
}
};
});
server.post("/request", app);

Oh, I forgot to include body-parser 'cause I'm using general web server instead of actions-on-google API. LOL
Case closed.

Related

Trying to use oauth flow in Electron desktop app (with spotify API)?

I have a React app in Electron, and I'm trying to access the spotify API using the spotify-web-api-node library. However, I'm not sure exactly how the oauth flow is meant to work inside of an Electron app... Firstly, for the redirect URL, I used this question and added a registerFileProtocol call to my file. Then I added a specific ipcMain.on handler for receiving the spotify login call from a page, which I've confirmed works with console logs. However, when I get to actually calling the authorizeURL, nothing happens?
This is part of my main.js:
app.whenReady().then(() => {
...
protocol.registerFileProtocol(
"oauthdesktop",
(request, callback) => {
console.log("oauthdesktop stuff: ", request, callback);
//parse authorization code from request
},
(error) => {
if (error) console.error("Failed to register protocol");
}
);
});
ipcMain.on("spotify-login", (e, arg) => {
const credentials = {
clientId: arg.spotifyClientId,
clientSecret: arg.spotifySecret,
redirectUri: "oauthdesktop://test",
};
const spotifyApi = new SpotifyWebApi(credentials);
console.log("spapi: ", spotifyApi);
const authorizeURL = spotifyApi.createAuthorizeURL(
["user-read-recently-played", "playlist-modify-private"],
"waffles"
);
console.log("spurl: ", authorizeURL);
axios.get(authorizeURL);
}
I'd expect the typical spotify login page popup to show up, but that doesn't happen. I'd also expect (possibly) the registerFileProtocol callback to log something, but it doesn't. What am I meant to be doing here? The authorization guide specifically mentions doing a GET request on the auth url, which is what I'm doing here...
In a desktop app it is recommended to open the system browser, and the Spotify login page will render there, as part of creating a promise. The opener library can be used to invoke the browser.
When the user has finished logging in, the technique is to receive the response via a Private URI Scheme / File Protocol, then to resolve the promise, get an authorization code, then swap it for tokens. It is tricky though.
RESOURCES OF MINE
I have some blog posts on this, which you may be able to borrow some ideas from, and a couple of code samples you can run on your PC:
Initial Desktop Sample
Final Desktop Sample
The second of these is a React app and uses a Private URI scheme, so is fairly similar to yours. I use the AppAuth-JS library and not Spotify though.

get json from electron BrowserWindow

I am trying to create a login for my app using discord's Oauth2 currently I am displaying a separate BrowserWindow for the API call since discords Oauth2 requires that the user clicks authorize. my API call returns the raw JSON of the acess_tokens. In my app's current state the separate window only displays the JSON. I need a way to get the JSON from within the window or from the request in a variable. I can't seem to find any way to access the raw content.
function createAuthWindow(){
var authWindow = new BrowserWindow({
width: 400,
height: 600,
show: false,
'node-integration': false,
'web-security': false,
icon: getFile('f','/src/asset/instance.png'),
});
// This is just an example url - follow the guide for whatever service you are using
var authUrl = 'http://localhost:3001/api/discord/login'
authWindow.loadURL(authUrl, (res) => {
console.log(res)
console.log(authWindow);
});
authWindow.show();
// 'will-navigate' is an event emitted when the window.location changes
// newUrl should contain the tokens you need
authWindow.webContents.on('will-navigate', function (event, newUrl) {
// More complex code to handle tokens goes here
console.log(event.code);
authWindow.webContents.session.webRequest.onCompleted({ urls: [newUrl] }, (details) => {
// Access request headers via details.requestHeaders
// Access response headers via details.responseHeaders
console.log(authWindow.webContents.code)
});
});
Sounds like your auth URL is not correct, and that you should be sending an Authorization Code Flow message, so that you can get tokens back to your app.
The usual technique for a desktop app is to:
Format the Authorization Redirect URL
Open this URL in the System Browser, which will handle redirects for you
Receive the response via a Private URI Scheme or Loopback Notification
Swap the authorization code for tokens, which your app can then use to call APIs
The redirect URL will be a value something like this, though I have not used Discord as a provider, so this may not be 100% right:
https://login.discord.com/oauth2/v2.0/authorize?
client_id=y792f434f
&response_type=code
&redirect_uri=com.mycompany.myapp:/callback
&scope=...
&state=...
&code_challenge=...
&code_challenge_method=S256
If it helps I have a couple of blog posts on OAuth for Electron Desktop Apps. It is a tricky flow to implement though ...
Initial Desktop Code Sample
Final Desktop Code Sample

Not able to set cookie from the express app hosted on Heroku

I have hosted both frontend and backend in Heroku.
Frontend - xxxxxx.herokuapp.com (react app)
Backend - yyyyyy.herokuapp.com (express)
I'm trying to implement Google authentication. After getting the token from Google OAuth2, I'm trying to set the id_token and user details in the cookie through the express app.
Below is the piece of code that I have in the backend,
authRouter.get('/token', async (req, res) => {
try {
const result = await getToken(String(req.query.code))
const { id_token, userId, name, exp } = result;
const cookieConfig = { domain: '.herokuapp.com', expires: new Date(exp * 1000), secure: true }
res.status(201)
.cookie('auth_token', id_token, {
httpOnly: true,
...cookieConfig
})
.cookie('user_id', userId, cookieConfig)
.cookie('user_name', name, cookieConfig)
.send("Login succeeded")
} catch (err) {
res.status(401).send("Login failed");
}
});
It is working perfectly for me on my local but it is not working on heroku.
These are the domains I tried out already - .herokuapp.com herokuapp.com. Also, I tried out without specifying the domain field itself.
I can see the Set-Cookie details on the response headers but the /token endpoint is failing without returning any status code and I can't see the cookies set on the application tab.
Please see the below images,
I can't see any status code here but it says it is failed.
These are cookie information that I can see but it is not available if I check via application tab.
What am I missing here? Could someone help me?
May you should try secure as:
secure: req.secure || req.headers['x-forwarded-proto'] === 'https'
You are right, this should technically work.
Except that if it did work, this could lead to a massive security breach since anyone able to create a Heroku subdomain could generate a session cookie for all other subdomains.
It's not only a security issue for Heroku but also for any other service that lets you have a subdomain.
This is why a list of domains has been created and been maintained since then to list public domains where cookies should not be shared amongst the subdomains. This list is usually used by browsers.
As you can imagine, the domain heroku.com is part of this list.
If you want to know more, this list is known as the Mozilla Foundation’s Public Suffix List.

Invalid credentials problem when consumig tone analizer from a nodejs app

I'm trying to consume a tone analyzer service from a nodejs app. I get unauthorized access problem, but these credentials work fine when I use them in a curl.
Running locally, in my app.js file I've included the data of the tone analyzer as follows:
var ToneAnalyzerV3 = require('watson-developer-cloud/tone-analyzer/v3');
var toneAnalyzer = new ToneAnalyzerV3({
version: '2017-09-21',
iam_apikey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
});
Then I've added this, so my app listens for post requestes in the /tone url:
app.post('/tone', function(req, res, next) {
var params = {'tone_input': req.body}
toneAnalyzer.tone(params, function(err, data) {
if (err) {
return next(err);
}
return res.json(data);
});
});
But when I call it I get "Unauthorized: Access is denied due to invalid credentials".
The thing is that these credentials work fine in curl:
curl -X POST -u "apikey:XXXXXXXXXXXXXXXXXXXXXXXXXXXXX" --header "Content-Type: application/json" --data-binary #tone.json "https://gateway-lon.watsonplatform.net/tone-analyzer/api/v3/tone?version=2017-09-21&sentences=false"
{"document_tone":{"tones":[{"score":0.6165,"tone_id":"sadness","tone_name":"Sadness"},{"score":0.829888,"tone_id":"analytical","tone_name":"Analytical"}]}}
The reason you are getting unauthorised errors when running locally is that your service is hosted in https://gateway-lon.watsonplatform.net. If you don't specify an endpoint / url in your ToneAnalyzerV3 constructor then the API / SDK defaults to Dallas. So although your credentials may be correct for London, they are not correct for Dallas.
When you deployed your app to the cloud (which I guess was to the London location), you probably bound the service into your application. This sets environment variables allowing the SDK to determine the endpoint.
You constructor should look like:
var toneAnalyzer = new ToneAnalyzerV3({
version: '2017-09-21',
iam_apikey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
url: 'https://gateway-lon.watsonplatform.net/tone-analyzer/api',
});
I don't see problem with code (also never used watson stuff), but you may check the following point :
How the request you really send is formated : Because I see that you send param that is not present in your curl request.
Is your function using POST aswell (you don't provide much detail on what the call to toneAnalyzer.tone does exactly) ? Maybe it's a conflict of headers or Content-Type.
Do you use a proxy (enterprise settings or stuff like that) ? If you do, you may check that node is correctly using it.
You should also provide a bit more details on what exactly your tone object do, and try to find where the call to the IBM API is done.

How to authorize for Amazon's Alexa API?

I want to send a request to this Amazon Alexa API.
That page contains the last 50 activities I made with my Amazon Echo. The page returns JSON. Before you can request that page, you need to authorize your account, so the proper cookies are set in your browser.
If I do something simple as:
const rp = require("request-promise");
const options = {
method: "GET",
uri: "https://alexa.amazon.com/api/activities?startTime=&size=50&offset=-1",
json: true
};
rp(options).then(function(data) {
console.log(data);
}).catch(function(err) {
console.log(err);
});
I can send a GET request to that URL. This works fine, except Amazon has no idea it's me who's sending the request, because I haven't authorized my NodeJS application.
I've successfully copied ~10 cookies from my regular browser into an incognito tab and authorized that way, so I know copying the cookies will work. After adding them all using tough-cookie, it didn't work, unfortunately. I still got redirected to the signin page (according to the error response).
How do I authorize for this API, so I can send my requests?
I have been looking for a solution for this too. The best idea I have is to use account linking, but I haven't try it yet. Looks like ASK-CLI has interface for this also, but I can't figure it out how to use it (what is that URL?). For linking account to 3rd party server is not easy, but link it back to Amazon for the json API should not be that complicated.

Resources