Performance of Stateless Authentication in SPA and SSO (Single Sign On) - javascript

If I have a SPA (Single Page Application - developed with BackboneJS) and want to have a stateless RESTful backend API for its data. I like how 3rd party single sign on makes things so easy for the user, thus will like it use it.
But I understand in a stateless environment like this, authentication is done on every request? If so, if I am using a 3rd party SSO, eg. GitHub, won't I need to goto GitHub to authenticate the user everytime? Whats the best practice for such situations? I believe its a very common use case? - I allow the user to login via Google/GitHub or something, then get data from some stateless REST API

Disclaimer :)
Having implemented such a thing for my product, and sharing many of your concerns and tech (especially SPA with Backbone using a 100% stateless REST backend) I can tell you what are my thoughs about this, making clear that this doesn't want to be "the answer" but rather a conversation starter to learn from the resulting discussion, as I think I too have quite a bit to learn on the topic.
First of all, I think you should go 100% stateless. And by 100%, I mean 100% :) Not only your API layer should be stateless, but the whole application (except client, of course). Moving sessions on a different layer (eg. redis) just moves the problem a bit, but doesn't solve it. Everything (especially scaling) will be so much easier, and you will thank yourself about this decision later on.
So, yes, you need to have authentication on every request. But this doesn't mean that you have to hit the auth provider every time. One of the things that I learned is that allowing an user to authenticate via FB/GitHub/Whatever (from now on, remote service) is just a mean to ease the pain of signup/signin, nothing else. You still have to grow your personal database of users. Of course, each user will be associated to a "remote" users, but soon after the authentication has been performed, the app should reference "your" user, and not the "remote" user (eg. GitHub user).
Implementation
Here's what I've implemented:
My API methods always need an authentication token. The auth token is a hash that represent an user of my system, so that when I call POST /api/board?name=[a_name]&auth=[my_token], I know who's calling, can check permissions and can associate the newly created entity board to the correct user.
The said token has nothing to do with remote services' tokens. The logic they are computed from is specific to my app. But it maps an user of mine, that is also mapped to a remote user, so no information is lost in case it's needed.
Here's how I authenticate the user via a remote service. I implement the remote authentication as specified in the service documentation. Usually it is OAuth or OAuth-like, this means that in the end I get an authToken that represent the remote user. This token has 2 purposes:
I can use it to call API methods on the remote service acting as the user
I have the guarantee that the user is who it says it is, at least by the means of the remote service
As soon as your user authenticated itself with the remote service, you load or create the matching user in your system. If the user with remote_id: GitHub_abc123 is not present in your system, you create it, otherwise you load it. Let's say this user has id: MyApp_def456. You create also an authToken with your own logic that will represent the user MyApp_def456 and pass it to the client (cookies are ok!!)
Back to point 1 :)
Notes
The authentication is performed at every request and this means that you deal with hashes and crypto functions, that by definitions are slow. Now, if you use bcrypt with 20 iterations, this will kill your app. I use it to store the passwords of the users when they log in, but then use a less heavy algorithm for the authToken (I personally use an hash comparable to SHA-256). This tokens can be short lived (let's say less than the average time to crack them) and are fairly easy to compute on a server machine. There's not an exact answer here. Try different approaches, measure and decide. What I am sure about, instead, is that I prefer to have this kind of problems than session problems. If I need to compute more hashes, or faster, I add CPU power. With sessions and a clustered environment, you have memory issues, load balancing and sticky sessions problems, or other moving pieces (redis).
Obiouvsly, HTTPS is absolutely mandatory because authToken is always passed as a parameter.

The way I would implement it is by introducing a proxy between the client (Backbone) and the RESTful webserver. The proxy manages authentication of users in conjunction with SSO. Therefore no need to change the api and/or client/webserver. Here a quick demo:
var http = require('http'),
httpProxy = require('http-proxy'),
express = require('express');
var proxy = new httpProxy.RoutingProxy();
var app = express();
function ensureAuthenticated(req, res, next) {
if (isLoggedIn) { return next(); }
res.redirect('/');
}
// This should be your (RESTful) webserver
http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('request successfully proxied!' + '\n' + JSON.stringify(req.headers, true, 2));
res.end();
}).listen(9000);
var isLoggedIn = false;
app.get('/', function(req, res){
console.log(isLoggedIn)
res.send('Logged in? ' + isLoggedIn);
});
app.get('/login', function(req, res){
isLoggedIn = true;
res.redirect('/');
});
app.get('/logout', function(req, res){
isLoggedIn = false;
res.redirect('/');
});
app.all('/api/*', ensureAuthenticated, function(req, res) {
return proxy.proxyRequest(req, res, {
host: 'localhost',
port: 9000
});
});
app.listen(8000);
The first time you visit the page, you're logged out and any call to /api/something gets redirected to /. When you're logged (visit the /login page) all requests to /api/* are routed through the proxy to the webserver listening on port 9000.
In particular, when you set app.all('/*', ...) all the calls to your API server stay the same but are augmented with an authentication layer. The concept is trivial to extend with oauth (have a look at passportjs if you are using node).

You could adopt the approach used in Facebook's JavaScript SDK.
This documentation page provides the quick-start to login vith Facebook for web. Not very deep, but explains the basic of how to use their approach. Does not mention the possibility of signature, however.
When registering Facebook logon for your app, you get an app secret from the application dashboard on Facebook.
When the user have logged on to your app via Facebook, your JavaScript gets an authentication object. This object contains a signature. (If you set it up correct in the dashboard.)
You can supply this authentication object with the client call to your RESTful server, and on the server check that the signature is correct. This way you know that the user was authenticated by facebook, is this user, and was authenticated for your application.
This documentation page describes how to use the signed authentication. Do not be intimidated by the "games" in the headline, it works perfectly fine for any web app.
Instead of allowing only Facebook for SSO, you can implement something in the same spirit as the FB login, using other OAUTH providers.
Use the solution suggested by danielepolencic, but modify it such that instead of a proxy in the same node.js instance you have another server for logins. This service does the OAUTH check with the provider, and maintains session with the client. It emits a signed token to the client, with a short time-to-live. The client must ask for a new token before the time-to-live ends.
You then implement a client-side JavaScript with functionality similar to Facebook JavaScript SDK for the login to be used by your application. This function can either poll for new tokens, or retrieve a new token on request, whatever is most efficient for the scenario.
The client supplies this token to the RESTful API on each request, and the server checks the signature. Just as for the Facebook SSO.
There still is a session, but it can be maintained visavis completely different machines. This service can be scaled independently of the servers with your RESTful api.
However, bear in mind that this approach can be susceptible to man-in-the-middle attacks and replay attacks. Probably unwise to use without https.

Related

NextJS auth with an external server

I'm working with auth in Nextjs, I'm wondering what is the best strategy to handle authentication in NextJS ?
Here my services structure :
If I understand well I have to handle the server side rendering in NextJS, so I understand I have to put cookies from my external server to my NextJS client, then handle the server side rendering checkings. To do that I assume I have to create connection between the NextJS server and the other services. Before dive more deeper in the subject I would discuss with you about the possibilities available to me. It seems the NextJS auth is a subject in plain development.
Any hint would be great,
Thanks
I've recently added an example with cookie auth which explains what you are trying to do on the frontend.
For the backend, optimally you'll have your API in an external server, apart from the server you use for rendering your Next.js app. This API will handle the database and the token creation business. Then the basics of the authentication are like this:
The client POST a request with username and password to the server.
The server gets the request and generate a token based on the data received.
If everything went okay validating the data, the server responds with the token, e.g., { token: "secrettoken" }.
The client receives the token and saves it in a cookie. Optionally you redirect the user to the /dashboard or /profile if everything is okay.
The client, on restricted pages, will check if the cookie exists and optionally validate that against the server, you do this last part in getInitialProps. If the cookie validation fails you redirect the user away.
I've created a small library to abstract this logic.
So in the end, your Next.js app shouldn't know what's happening in the server, it only should receive the token, save it, validate it, and redirect the user if something is wrong.
How you want to handle the token creation, on the external server, is up to you.
Check out this thread. There are several examples of how to do Authentication with JWT, OAuth etc throughout the thread. You'll see that the examples are utilizing getInitialProps and there are several examples utilizing cookies throughout to extract the Authentication tokens.
You'll have to write a custom server.js file using express.js to serve the tokens through your route requests. I'm assuming by "external server" you mean some third party Authenticator using OAuth or OpenId protocols to retrieve tokens. If so, you're right to say that you'll need to request the tokens (or Authentication mechanism) from those external services and then decide how you're going to utilize them in your own client. You'll probably be using getInitialProps to do what you need to do with your Authentication tokens in the client once you are rendering to the browser.
More examples of Authentication here -- one for firebase and another for Apollo.
Just to add to the answers if you want to use Auth0 specifically. In the interview on http://www.fullstackradio.com/112 around the 1:06 min mark Guillermo Rauch mentioned that if he were to implement authentication all over again he would use Auth0, so I created a minimal repo using Auth0 and Nextjs with Serverless functions.
Like #jolvera suggested there is an API in an external server, apart from the server used for rendering the Next.js app. This API is located in ./auth/auth.js. It handles the token creation business, and could be extended to handle the database.
The HOC component in ./utils/withAuth.js calls the auth.js lambda for the user information, and is only able to retrieve it if the client side is authorized. Otherwise the user information is undefined. Additionally there is an event listener similar to the one in with-cookie-auth which syncs logouts across tabs.
Also one other note, don't get confused with the Nextjs example on Auth0's blog. That example is extending the Nextjs server, and isn't the solution you want if you are deploying Next to serverless. It doesn't have the separation of concerns with page routing and authentication.

OAuth2 flow in web frontend and REST API

I'm working on a project based on Phalcon which consists in two different stand-alone subprojects: a php + angular frontend and a php REST API.
I protected the API with OAuth2, using PhpLeague OAuth2 Server. The API server is the OAuth2's authorization server AND resource server.
This is the actual flow:
The user can browse the public endpoints of the frontend, and when hits a private page, gets redirected to the login page;
The login page has username and password, POSTs them to the frontend server;
The frontend server calls a public method on the API server, which is expecting a Password Credential Grant: it validates the credentials and sends back an access token and a refresh token;
The frontend server caches both the access and refresh token in session and uses it for some API calls: the first of those is the '/users/me', which gets info about the current user and its ACL on the frontend sections;
The frontend server sends the page to the browser, which loads its javascript files.
Now, OAuth2 states that access tokens should be short-lived and refresh-token should be long-lived: in the frontend server logic, the API calls which receives a 401 (caused by the expired access token) are retried by sending first the refresh token to obtain a new access token via a Refresh Token Grant. If this second call is rejected, I assume the user is no more logged in (refresh token expired / revoked).
The pages are using Angular to perform data and ux/ui management. My question is:
should the Angular code call directly the API server?
Actually the first thing my javascript code does is to get a config object from the frontend server, which contains the access token too, and uses it to make the calls to the API server. The problem with this is that i should rewrite again the "refresh token logic" in javascript (after it expires, i get 401s), and by what I have read on the subject i understood that it is better to not make the refresh token visible to the client (as it can generate new access tokens).
So i was thinking about a "two step approach", where every javascript API call goes to an endpoint on the frontend server which relays it to the API server, but this is obviously slower (JS -> FRONTEND -> API and API -> FRONTEND -> JS).
What is the correct approach? It's not very clear to me if the frontend should be considered as two clients (php + js) which should work separately or not, as I imagine that an hypothetical iOS app would be making calls 100% against the API server.
I have used the same approach in my own projects. The problem that we have is that the client is not secure. In order to generate / refresh a token, you need to pass secure information to the authorization server.
I have done the same as you basically, let the back-end handle the tokens and their temporary storage. You cannot and should not trust the client with important information which lets you generate tokens. In terms of delays, I wouldn't worry about it too much since you're not going to be doing that much extra work, you won't even notice the delays. I have a system like this built and used by hundreds of thousands of users with absolutely no issues.
Now, you have said a few things in here which make me wonder what you are doing.
OAuth2 is not a user authentication system, it's an application authentication system. You don't pass a user and their password and generate a token for them, you pass a ClientID and ClientSecret and they generate a token for you. Then you have an endpoint which gives you the user details for this user, you pass your userid or username and get the details of that user.
A token expired does not mean your user is logged out. Those are two completely different things. How are you going to expire a token for example, when your user wants to log out? You can't, your token will still be valid until it expires after the set amount of time has passed.
A token can be used for let's say half an hour, but your user may use the website for 1 hour. So before you hit any API endpoint, you could check ... has this token expired yet? if yes then you can go and refresh it and keep working without having to bother your user with a new login screen.
The whole point of an OAuth2 system is to make sure that only authorised clients can access it. A client is not a user, it's an application. You can have a website for example and you only want users of that website to access your API.
You can have endpoints like ValidateUser for example, where you take a username and a password and return a yes or no and then you log your user in based on that.
Irrespective of language/framework, second approach is secure and better than first one because to get access token by providing refresh token to Authorization server, it still requires Client ID and Secret which should never be passed to Browser for security reasons.
In first approach, to make a direct call it will not work if your Authz Server is hosted on different domain than your frontend server because of Same Origin policy of browsers. Even if they are on same domain, still you are exposing Client ID and Secret which will compromise your frontend server

Angular app protection and user authentication

Implementation of user authentication in Angular app means that application's javascript must be available to user before he is authenticated. That is the problem. What is the correct way to make this happen with Angular? Do I need to implement a separate app for authentication and then redirect users to the base app?
Authentication should be on server level. The angular app should be responsible for only the capturing of credentials, possibly encrypting it, and sending an authentication request to the server with those credentials. The server response(possibly a token) could be used for any further communications with the server to identify the logged in user and its available services.
If the application itself does not contain any private business logic, I don't see value in keeping the application away from the end user even before authentication. On that note, however, take a look at tasks that are used to minify javascript code. This may be second best :)

Protect API routes in Sails.js with token-based authentication?

I'm new to Sails.js. I created a new project and a few APIs using the sails generate api [name] command. These new APIs have complete CRUD functionality so I can GET, POST, PUT etc. In order to protect them, I've implemented a standard token-based authentication system that will be used by clients accessing my API. The token is also persisted in the database.
I have two questions:
How do I disable certain actions on specific routes (like enable GET requests on the /user API but not on the /account API but allow POSTS on the /account API for example)
I want the client to send me back the generated Access Token for every post-authentication request. When they do, how do I intercept the request and check if the token exists in the database before allowing access to the requested route?
Thank you.
You will need to create policies to prevent non-authenticated user to perform some requests. I consider here that GET /user and GET /account will be routed to a find function. And POST /account routed to a create function.
In config/policies.js :
module.exports.policies = {
'UserController': {
'create': 'isAuth',
'find': true
},
'AccountController': {
'create': 'isAuth',
'find': true
}
}
You will need to add a isAuth.js policy file in api/policies
To authenticate user in Sails, I use (as many others) passportjs. You will define auth strategies. It is a powerful tool and will allow you to create custom auth strategies or use OAuth2 protocol if you need (if you use token you should use it).
Sails does exactly intercept every requests and execute the policy you configured for the routed action.
You will be able to find some documentation on the Internet on Sails, Policies, Passport, Oauth2.
Hope I have helped you.
I've gone ahead and created a guide for you here: https://github.com/carlospliego/sails-token-auth-setup

Moving from Session-based token mechanism to OAuth 2.0 mechanism

I own a Play Framework application acting acting as a backend server providing a set of REST APIs.
At client side, I own an AngularJS application that calls APIs from backend-server through AJAX.
Currently, I make use of a solution based on Session-token mechanism.
Meaning that each time a user logs in successfully, a cookie is retrieved at client side containing an authentication token.
At each request then, the cookie value (the auth token) providing by the client request is extracted on the server and if valid, the request is made.
Now I want to use OAuth 2.0. Reasons are? :
It's a great standard way to secure API, avoiding the use of a datastore (Memcached) to keep auth tokens at server side, as I'm currently providing.
I want to provide a better secure than a sole cookie, by providing some client_secret and nonces to avoid some replay attacks etc...
I want to restrict the amount of clients capable to call even public REST API I provide, meaning API that allows anonymous call, like listing a list of items for instance.
The point is that I don't involve a third party, since all protected resources are on my own.
I came across this article explaining how to secure internal REST API with OAuth 2.0 implementing a 2-legged instead of a 3-legged as usual.
However, I can't figure out how the Client Credentials flow could authenticate a specific user, when calling for a REST API that needs to have a user authenticated.
Indeed, Client Credentials flow seems to be based on a global client_id, client_secret keys (global to the app, so in my case to my Javascript app), and therefore not enough specific to target a specific user and controller its specific rights.
Any help would be great.
Seems like you should use "Resource Owner Password Credentials Grant" (https://www.rfc-editor.org/rfc/rfc6749#section-4.3). It is dead simple - put client ID/secret in Authorization header and put user name/password in query variables. Here is an example from the RFC:
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=johndoe&password=A3ddj3w
Server side you can check for both validity of the client (your javascript app) as well as the user. Just remember that it is impossible to protect the client credentials as it will be embedded in your (downloadable) JavaScript code. The user name/password is entered directly by the end user.

Categories