url signature missing or invalid getstream - javascript

var client = stream.connect('my-client-id', null, '7723');
var user1 = client.feed('flat', 'my-client-id', 'NuAW6yHVQ2sr9RQvBE-cCuewUlo'); // What is this token param (3rd one)? How is this generated?
var acticity = {
actor: 'QUXdERFPto',
tweet: 'Hello world',
verb: 'tweet',
object: 1
}
user1.addActivity(acticity).then(null).catch(function(e) {
// Error object is
// code: null
// detail: "url signature missing or invalid"
// duration: "10ms"
// exception: "AuthenticationFailed"
// status_code: 403
});
What is the signature that I am missing?

Stream-JS client-side feed tokens
When using the stream-js library on the client you should initiate the connect without your secret key, to avoid sharing your private key with the world (its secret).
var client = stream.connect('api-key', null, 'app-id');
Initiating the client this way does not allow you to read or write from any feed created from this client, thus the following feed will return 403 errors when you try to read or write from it.
client.feed('flat', 'user-id');
But if you generate a read/write token on server side you can initiate a feed with this token and allow read/writes from the client-side:
client.feed('flat', 'user-id', 'read/write token');
To generate a read/write token on the server initiate a client with your secret key and call the following methods:
var client = stream.connect('api-key', 'api-secret', 'app-id');
var readToken = client.getReadOnlyToken('flat', 'user-id');
var readWriteToken = client.getReadWriteToken('flat', 'user-id');
Supply one of these tokens to your client and create a feed instance with this token.
When to use Stream-JS on the client
In most use-cases however you would want to use the stream-js client on your server side and retrieve/publish activities there, enrich these activities with data stored in your local database and send this to the client. One use-case for using stream-js on the client is for realtime notifications

Related

Nodejs - encode to base64 with 'private key' and be able to decode only in server

My use case is as follows:
Incoming http request to server to login
User token is generated. The token is a Json object build from various fields. Then convert to String and Base64.
const stringObject = {
elementA: stringA,
elementB: stringB
};
const bufferString = new Buffer(`${JSON.stringify(stringObject)}`);
const encodedAccessToken = bufferString.toString('base64');
The generated string can now be decoded anywhere.
Is there a way I can encode it in such as way that only my server will be able to decode it? Like encoding it with some sort of a key.
Thanks.
You can use JWT token Node Module : link
Encode data and generate token :
var jwt = require('jsonwebtoken');
var token = jwt.sign({ foo: 'bar' }, 'shhhhh');
{ foo: 'bar' } is your feilds that you encrypt
Decode by same key shhhhh
// verify a token symmetric
jwt.verify(token, 'shhhhh', function(err, decoded) {
console.log(decoded.foo) // bar
});
This does not directly answer your question, but I think your overall approach is wrong. What you are trying to achieve is to have session data. You don't need to send this data to the client and back. That is not a very good practice. Instead you should store this data on the server, preferably in a database.
What you would do is to create a unique key, something randomly generated. You would store the user data using this key, and send the key to the client to be used on requests. You do it by setting it as a cookie variable as well.
The user data can have additional fields variables for more secure access. Like the IP of the client, and possibly a expiry time for cleanup.
use jsonwebtoken instead. it can encrypt your object with a secret phrase. library like node-jsonwebtoken is very easy to use.

Unable to query Google Search Console API using a Service Account

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);

Socket.io - Implementing a user-socket association map for private messaging

I'm trying to create a private messaging system using socket.io
In order to associate the users with their sockets, most sites are suggesting something like this:
var people = {};
client.on('connection', function(socket) {
//join the server
socket.on('add user', function(user_id) {
//create user-socket map
people[user_id] = socket.id;
});
});
In my opinion, the problem with the above code is that the user_id is sent from the client side, so if the user somehow modify it and send another user_id an impersonation will take place.
Also, I can't access req.user._id under client.on('connection'... so how am I supposed to get the user_id from the server's side? I'm using node.js, passport and express.
I would use jsonwebtoken and socketio-jwt modules for solving this security issue.
Server:
var secret = 'shhhhhh';
app.get('/getJWT', function(req, res) {
/* Do your authentication here using header and get the userId */
var userId = 'someId';
var jwt = require('jsonwebtoken');
var token = jwt.sign({ 'userId': userId }, secret);
res.json({
token: token
});
});
var socketioJwt = require('socketio-jwt');
io.use(socketioJwt.authorize({
secret: secret,
handshake: true
}));
io.sockets.on('connection', function (socket) {
var userId = socket.decoded_token.userId;
/* your logic */
Client:
var token = "token you got from the /getJWT"
var c = io.connect('http://localhost:3000/', { query: "token=" + token });
As the token is encoded with a secret, client cannot change and send it.
Refer this article to know why this is better.
You're missing the most important part... Your code has to verify the usr is who he says he is. Plain and simple. I've done this multiple ways:
If users are logging in via PHP code, I move the session data to a mysql database. I then use a string on the PHP side to generate a response for a challenge to the client, who sends it to my web socket server. The WS server will challenge the client and look up the session information in the mysqldb. Done.
In my more recent developments, the actual login process is done via the web socket server. I verify the user credentials via whatever DB (in my instance, MySQL) and tie the username to the socket. Finished...
Do not purely rely on the javascript-based site to say "My name is." Otherwise, as you said, user impersonation becomes a walk in the park. You MUST validate that the user is who he says he is IF you're implementing a system where that matters. "Web sockets" themselves are not magical components that do this for you.
var people will be accessible on the same process.
When you want to scale with multiple socket server and balancing between them, then this idea for keeping people object locally will be not helpful.
Create authenticate event for authentication and set socket.userId and socket.isAuthenticate = true flag. In other events if socket.isAuthenticate is false, kick them out.
Make use of 'socket.io-redis' adpater for communication among many socket.io server. ( So when user1 from server1 send message to user2 which is in server2, will work ).
For socket - user association with multiple process, you can join Room with their userId, on authentication, join room like socket.join('myuserId');
and when to send message to that user, you can use io.to('myuserId').emit('message', "Hi How are you?"):
you can Send additional data on socket connection like this:
client side :
var c = io.connect('http://localhost:3000/', { query: "userId=value01" });
server side :
// extract userId param from connected url
io.on('connection', function(socket) {
var socket_param_userId = socket.handshake['query']['userId'];
console.log(socket_param_userId); //show value01
});

How can I authorize and identify users with a websocket connection request?

When I set up a websocket connection from JavaScript, how can I authorize that it is a legit user on the serverside? I am using JSON Web Tokens and when doing regular calls to REST backend I automatically add an Authorization: Bearer (JWT..) header on AngularJS and then check that on the server side to see if a user is logged in. How can I do that when upgrading the connection to a websocket connection? I am afraid that some people with connect to the server requesting a websocket connection and spoof some of the users id's and receive their messages without being logged in to the service.
I request a websocket connection like this:
var conn = new WebSocket("ws://localhost:8080/api/ws");
conn.onclose = function (e) {
console.log("disconnected");
};
conn.onopen = function (e) {
console.log("connected");
};
conn.onmessage = function (e) {
console.log(e.data);
};
On the first part, is that a GET request or a POST request? Can I add parameters to the url and check them on the serverside? For example:
var conn = new WebSocket("ws://localhost:8080/api/ws/token/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ");
Or is this not a good idea? I also thought sending a JWT would be a good idea because I would be able to extract the user_id from the JWT and associate a websocket connection to a specific user.
How can I solve this problem?

Where to store Access and Refresh tokens

I'm making an OAuth2 call from my ASP.NET MVC web application to my Web API through JavaScript to authenticate my user and get a token. The Web API accesses a SQL Server database where the user's login is stored using Identity and the typical AspNetUsers tables. My API call returns a 20 min access token and a 2 week refresh token. The API and consuming apps are products that we are developing that our customers will register with. In other words, all the code is on our side.
I know I must refresh the access token before it expires by passing the API the refresh token. My question is...where do I store the access and refresh tokens on the client for use in my JavaScript to make subsequent API calls or to refresh the token? People online are saying that storing anything client-side is bad, cookies are unsecure, etc and without offering any solutions. Local storage? But of course these are Ajax calls in JavaScript that we are making to the API, so the tokens needs to exist somewhere on the client side! It's driving me crazy trying to figure this out. I know I need to at least use HTTPS.
I'd suggest you to create a table in database to store the refresh token and access token.
Table structure will look like below
ID,Access_Token,Refresh_Token,LastUpdated_Time
Whenever you're calling a API with access token , please check the current time and LastUpdated_Time of token , if it is more than one hour your token will become invalid, so you need to get another valid token using your refresh token.
In my application , I had 55 minutes lifespan of toke, after that time token gets invalid.
Code
if (dateTimeDiff > 55) {
var request = (HttpWebRequest) WebRequest.Create("https://www.googleapis.com/oauth2/v3/token");
var postData = "refresh_token=your refresh token";
postData += "&client_id=your client id";
postData += "&client_secret=your client secret";
postData += "&grant_type=refresh_token";
var data = Encoding.ASCII.GetBytes(postData);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
request.UseDefaultCredentials = true;
using(var stream = request.GetRequestStream()) {
stream.Write(data, 0, data.Length);
}
var response = (HttpWebResponse) request.GetResponse();
string responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
}
The response will contain new access token, don't forget to update your table with LastUpdated_Time and new token.
The auth token is something that you can always obtain using the refresh token. So, have an interceptor that validates the incoming and outgoing requests and store the auth-token there. Coming to the refresh-token, in our application we were initially storing in cookies, later moved to local storage.
You can tried to store the token on the localStorage, what I am doing right now is(I am using typescript and react, but I think it will give a clue for your web app):
const msg = await login({ ...values, type });
let accessToken = msg.accessToken;
window.localStorage.setItem("x-access-token", accessToken);

Categories