We've built a javascript module which can be embedded in third-party webpages. When this client module is initialized, it executes a transaction within our web application via a cross-site request.
The transaction consists of an external uuid, an email, and some additional meta properties. This message is signed with an HMAC sha256 digest, using our partner's private API key.
Ruby example:
data = {
uuid: "ABCAFAFDS",
email: "email#gmail.com",
meta: {}
}
private_key = "Qd9fe0y2ezqfae4Qj6At"
signature = OpenSSL::HMAC.hexdigest(
OpenSSL::Digest.new("sha256"),
private_key,
data.to_json
)
Within the third-party webpage, the javascript client is then initialized with the signature and the data:
new Client(signature, data).execute();
Initially, our plan was to allow the client to create a partial / incomplete transaction within our system and then require a subsequent back-end request via our REST API to confirm / finalize the transaction. Assuming that we can secure the front-end, however, it would be preferential to remove the back-end confirmation requirement.
Can we reasonably secure the client code using signed messages in this fashion? If the data and the signed message is available in the client, how difficult would it be for a bad actor to brute force the API private key length, given the length above?
most internet traffic has signed tokens on the client these days. All your gmail logins, facebook logins, etc do this so it is fairly standard. I'd recommend using an existing standard (and 3rd party library) rather than roll your own token though. This will let you leverage other people's expertise in this area.
JWT (json web token) is in common use and there are many libraries for working with JWT's. See https://jwt.io for more information.
Related
I have a login route that returns:
a cookie with a jwt payload: user.id and user.locale
a json response with a user object.
This user object contains sensitive informations such as geolocation, email, etc. This response is stored in a react global state and cached by the browser. It is never exposed in local/session storages.
Do I need to encrypt the user object in a jwt before sending it to the client? Or does it make no difference at all, and sending it in plain json will be enough?
The code looks like this:
const token = AuthControler.generateToken(user);
const encryptedUser = AuthControler.encryptUser(user);
return res
.status(200)
.cookie("myapp", token, {
expires: new Date(Date.now() + msPerDay * 14),
httpOnly: true,
secure: true
})
.json({ user: encryptedUser });
JSON Web Token can be decoded, even without the signing private key / signing secret - it's not encrypted on its own. See here: https://jwt.io/ - paste your JWT (having read the warnings about sensitive data) and get the user info back.
If you want to avoid exposing the data to the user, encrypt it (not JWT) yourself. This technique is often applied to cookies, as well - e.g. to prevent fuzzing by cookies and other tampering. Alternatively, if you do maintain some kind of session state on the back-end, it's a good place to put the data and never have to send it to the client in the first place.
Last but not least, it's important that you have a threat model before setting out to implement security. What's the data that is protected? Who are you protecting against? Is e.g. "another user of the same computer" part of the model? Can the data be obtained in some other way, e.g. by actively making requests to your system? Is it affected by GDPR in any way, and if so, does it achieve minimization of data processing?
Contrary to popular belief JWT tokens may come in both JWS (signed only) or JWE (truly encrypted) formats. JWE is just not a widespread capability of most JWT/JOSE libraries.
If your system is both the issuer and consumer of these tokens than you can use encrypted JWTs, e.g. using the jose's package EncryptJWT module.
The { alg: 'dir', enc: 'A256GCM'} is suited for such a setup, the secret key would be a 256bit random secret. Other enc values may require different sized secret keys.
Resulting JWT looks like so, the only readable portion prior to decryption is the JWE Protected Header.
eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..dHrDXdmJIg9pwujk.ZX69BYgPmnCYpztL9BgdyaElv1wEebfq6dIrhoh6TEFiocGK4uwK6rt6pA6oXEkLd-pVVxtIaSTb6r5On1PU0EG9uqJbk7yGaMkq_OF1ZsbVbsHoGPaggoi5j7PCSLmRJdr1iByp7IJ2yWzTx-yzVgnBJgk.dSsVWFbQYLmr0mUBJVWpfQ
I'm trying to implement Google sign-in and API access for a web app with a Node.js back end. Google's docs provide two options using a combo of platform.js client-side and google-auth-library server-side:
Google Sign-In with back-end auth, via which users can log into my app using their Google account. (auth2.signIn() on the client and verifyIdToken() on the server.)
Google Sign-in for server-side apps, via which I can authorize the server to connect to Google directly on behalf of my users. (auth2.grantOfflineAccess() on the client, which returns a code I can pass to getToken() on the server.)
I need both: I want to authenticate users via Google sign-in; and, I want to set up server auth so it can also work on behalf of the user.
I can't figure out how to do this with a single authentication flow. The closest I can get is to do the two in sequence: authenticate the user first with signIn(), and then (as needed), do a second pass via grantOfflineAccess(). This is problematic:
The user now has to go through two authentications back to back, which is awkward and makes it look like there's something broken with my app.
In order to avoid running afoul of popup blockers, I can't give them those two flows on top of each other; I have to do the first authentication, then supply a button to start the second authentication. This is super-awkward because now I have to explain why the first one wasn't enough.
Ideally there's some variant of signIn() that adds the offline access into the initial authentication flow and returns the code along with the usual tokens, but I'm not seeing anything. Help?
(Edit: Some advice I received elsewhere is to implement only flow #2, then use a secure cookie store some sort of user identifier that I check against the user account with each request. I can see that this would work functionally, but it basically means I'm rolling my own login system, which would seem to increase the chance I introduce bugs in a critical system.)
To add an API to an existing Google Sign-In integration the best option is to implement incremental authorization. For this, you need to use both google-auth-library and googleapis, so that users can have this workflow:
Authenticate with Google Sign-In.
Authorize your application to use their information to integrate it with a Google API. For instance, Google Calendar.
For this, your client-side JavaScript for authentication might require some changes to request
offline access:
$('#signinButton').click(function() {
auth2.grantOfflineAccess().then(signInCallback);
});
In the response, you will have a JSON object with an authorization code:
{"code":"4/yU4cQZTMnnMtetyFcIWNItG32eKxxxgXXX-Z4yyJJJo.4qHskT-UtugceFc0ZRONyF4z7U4UmAI"}
After this, you can use the one-time code to exchange it for an access token and refresh token.
Here are some workflow details:
The code is your one-time code that your server can exchange for its own access token and refresh token. You can only obtain a refresh token after the user has been presented an authorization dialog requesting offline access. If you've specified the select-account prompt in the OfflineAccessOptions [...], you must store the refresh token that you retrieve for later use because subsequent exchanges will return null for the refresh token
Therefore, you should use google-auth-library to complete this workflow in the back-end. For this,
you'll use the authentication code to get a refresh token. However, as this is an offline workflow,
you also need to verify the integrity of the provided code as the documentation explains:
If you use Google Sign-In with an app or site that communicates with a backend server, you might need to identify the currently signed-in user on the server. To do so securely, after a user successfully signs in, send the user's ID token to your server using HTTPS. Then, on the server, verify the integrity of the ID token and use the user information contained in the token
The final function to get the refresh token that you should persist in your database might look like
this:
const { OAuth2Client } = require('google-auth-library');
/**
* Create a new OAuth2Client, and go through the OAuth2 content
* workflow. Return the refresh token.
*/
function getRefreshToken(code, scope) {
return new Promise((resolve, reject) => {
// Create an oAuth client to authorize the API call. Secrets should be
// downloaded from the Google Developers Console.
const oAuth2Client = new OAuth2Client(
YOUR_CLIENT_ID,
YOUR_CLIENT_SECRET,
YOUR_REDIRECT_URL
);
// Generate the url that will be used for the consent dialog.
await oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope,
});
// Verify the integrity of the idToken through the authentication
// code and use the user information contained in the token
const { tokens } = await client.getToken(code);
const ticket = await client.verifyIdToken({
idToken: tokens.id_token!,
audience: keys.web.client_secret,
});
idInfo = ticket.getPayload();
return tokens.refresh_token;
})
}
At this point, we've refactored the authentication workflow to support Google APIs. However, you haven't asked the user to authorize it yet. Since you also need to grant offline access, you should request additional permissions through your client-side application. Keep in mind that you already need an active session.
const googleOauth = gapi.auth2.getAuthInstance();
const newScope = "https://www.googleapis.com/auth/calendar"
googleOauth = auth2.currentUser.get();
googleOauth.grantOfflineAccess({ scope: newScope }).then(
function(success){
console.log(JSON.stringify({ message: "success", value: success }));
},
function(fail){
alert(JSON.stringify({message: "fail", value: fail}));
});
You're done with the front-end changes and you're only missing one step. To create a Google API's client in the back-end with the googleapis library, you need to use the refresh token from the previous step.
For a complete workflow with a Node.js back-end, you might find my gist helpful.
While authentication (sign in), you need to add "offline" access type (by default online) , so you will get a refresh token which you can use to get access token later without further user consent/authentication. You don't need to grant offline later, but only during signing in by adding the offline access_type. I don't know about platform.js but used "passport" npm module . I have also used "googleapis" npm module/library, this is official by Google.
https://developers.google.com/identity/protocols/oauth2/web-server
https://github.com/googleapis/google-api-nodejs-client
Check this:
https://github.com/googleapis/google-api-nodejs-client#generating-an-authentication-url
EDIT: You have a server side & you need to work on behalf of the user. You also want to use Google for signing in. You just need #2 Google Sign-in for server-side apps , why are you considering both #1 & #2 options.
I can think of #2 as the proper way based on your requirements. If you just want to signin, use basic scope such as email & profile (openid connect) to identify the user. And if you want user delegated permission (such as you want to automatically create an event in users calendar), just add the offline access_type during sign in. You can use only signing in for registered users & offline_access for new users.
Above is a single authentication flow.
I have a node.js REST API and I want to restrict POST/PUT/DELETE calls to a predefined list of "sources" (web applications which I do not own the code).
The only way I see to achieve this is to put a token on the client-side (something like Google Analytics in JS files) but I have no idea how to secure this since the token will be accessible in the static files.
What strategy should I use ? JWT and OAuth2 seem not indicated since it requires first user authentication, but what I want to authenticate is not user but webapps.
Your question is slightly unclear. You could mean either (a) that you want to strongly encourage the user to use the app and prevent other code from maliciously making your user perform an action, or (b) that you want to absolutely prevent your user from using other code to access your server.
The first option is possible, and indeed a very good idea. The second is impossible, based on the way the Internet works.
First, the impossibility. Essentially, client-side code is there to make life easier for your client. The real work will always be done on the server side -- even if this only means validating data and storing it in the database. Your client will always be able to see all the HTTP requests that they send: that's the way HTTP works. You can't hide the information from them. Even if you generate tokens dynamically (see below), you can't prevent them from using them elsewhere. They can always build a custom HTTP request, which means ultimately that they can, if they really, really want, abandon your app altogether. Think of your client-side code as merely making it easier for them to perform HTTP requests and abandon any idea of preventing them "doing it wrong"!
The much better option is CSRF protection, which gives the best possible protection to both your server and the client. This means sending a generated token to your client when they first log on and verifying it (either by looking it up or decrypting it) when they send it on every request. This is the basis of JWT, which is a beautiful implementation of a fairly old system of verification.
In the end your API is public, since any random website visitor will have to be able to interact with the API. Even if you use tokens to restrict access somewhat, those tokens by definition will have to be public as well. Even regularly expiring and renewing the tokens (e.g. through a backend API, or by including a nonce algorithm) won't help, since those new tokens will again be publicly visible on the 3rd party's website where anyone can fetch one.
CSRF protection can help a little to avoid cross-site abuse within browsers, but is ultimately pointless for the purpose of preventing someone to write an API scraper or such.
The best you can do is use the tokens to identify individual site owners you granted access to, vigilantly monitor your API use, invalidate tokens when you think you're seeing them abused and contact the site owners about securing their tokens better somehow (which they'll have the same problem doing, but at least you have someone to blame cough cough).
You can use hmac to secure this :
Each client has a unique couple of key public/private (for example "public" and "private").
When client send request, he has to send a nonce + his user public key + the hmac of nonce+public key with his private key.
When server handle request, the server retrieve the client according to his public key, get the secret key of the user, then verify the signature.
Client, sample call on /api
var nonce = "randomstring";
var pk = "aaa";
var sk = "bbb";
var string = "pk="+pk+"&nonce="+nonce;
var crypto = require('crypto');
var hmac = crypto.createHmac('sha512', sk).update(string).digest('hex');
// send this payload in your request in get, put, post, ....
var payload = string+"&hmac="+hmac;
request.post({uri:"website.com/api?"+payload}, ....
And
Server side, security check
var nonce = req.query.nonce;
var pk = req.query.pk;
var hmac = req.query.hmac;
// retrieve user and his sk according to pk
var sk = getUser(pk).sk
// rebuild payload string
var string = "pk="+pk+"&nonce="+nonce;
var crypto = require('crypto');
var hmac_check = crypto.createHmac('sha512', sk).update(string).digest('hex');
if(hmac_check === hmac) { // request valid }else{ // invalid request }
I'm writing angular application that uses implicit grant oauth strategy. If I don't have valid access token in my cookies I am redirected to web interface of authentication server, input my credentials and get redirected to my site with access token in the url. My system parses it and writes down into cookies.
Currently I faced question of unit testing this parse function that consumes the url and returns access token object. Can't think the good way, so writing here:
1. How do you approach unit testing (so I can't make direct request to working oauth server) a function that parses the access token from authentication server?
2. How do you build url params with access token? Will it be secure if I copy current access token and use it in test data?
3. Are there libraries that can aid creation of mock access token object?
You could breakout "just enough" OAuth, like the service linked below. This will give you a super-basic OAuth provider, but is really geared to "integration testing" scenarios (depending on where you draw the lines on these things).
If you want to be a unit-testing purist/zealot, then you could fork this into your app's unit test code.
https://github.com/zalando-stups/mocks/tree/master/oauth2-provider
In lieu of a great answer, here's one to get you out of a hole :)
After approaching authors of my oAuth authentification service I've got insight on the data the server was sending me in case of successful authentication. In my case the response from oAuth server has the following form:
header // encoded algorithm for has creation
access token // base64 encoded object
hash // of the 3 previous items done using algorithm specified in the header.
It has the following structure: header.access_token.hash
My fear for security breach was concerned about putting correct data (that I can get from browser) into the test files. But as it turns out the token will not be valid without correct hash. The hashing of the token data is done in server side using private key, so if I change the hash the token will become obsolete.
As my token-parsing-function that I wanted to check uses only the access_token part of the request, I've changed the header and hash parts and also encoded other user name for sake of testing purposes.
So this is the resolution of my current problem. Hope this will help somebody.
I would like to use Firebase Custom Authentication in my Angular app. This action is realy simple:
var FirebaseTokenGenerator = require("firebase-token-generator");
var tokenGenerator = new FirebaseTokenGenerator("<YOUR_FIREBASE_SECRET>");
var token = tokenGenerator.createToken({ uid: "uniqueId1", some: "arbitrary", data: "here" });
But there is a warning about security of Firebase Secret in the doc page:
Firebase JWTs should always be generated on a trusted server so that
the Firebase app secret which is needed to generate them can be kept
private.
I am wondering how can I keep my Firebase Secret private if everyone can view my JavaScript source code and read the Firebase Secret there? Am I missing something or there is no possibility to do this in JavaScript?
The code you quote is to be run on the your nodejs server (hence - server-side javascript).
The server component FirebaseTokenGenerator takes care for generating the token and sending it back to the JS client, after the client has authenticated to your server, with whatever method you want. That's why it's named custom authentication.