Today, we are switching to a brand new authentication system based on a modern architecture. This new system comes with a few bonuses like 2-factor authentication aka 2FA. The migration is seamless and the old authentication system will be kept compatible until the 31st of August, at that date it will be discontinued. If you wrote custom API clients, you will need to adapt them.
Until now, the authentication has been handled entirely by our API which acted as an intelligent proxy for other services (for example, Metrics and Logs are independent services). The authentication was made by an API token that was unique to the user's account and had an infinite lifetime.
That's a little bit tautological, but because the token was unique, there was only one token shared by all your API clients: the dashboard, but also the CLI and maybe your own custom API clients. That increased the chance of a token being leaked. And if by any chance that token should have been revoked (regenerated in fact), all your API clients would be unable to authenticate against our services until you replaced the old token with the new one.
Of course using long lived token, even if it is not an issue by itself, greatly increases the impact of leaked tokens.
This new authentication system has been designed to accomplish multiple objectives: it should be multi-datacenters ready, horizontally scalable, convenient for developers, it should allow third-party application authentication, it should mitigate the two problems mentioned above and it should be… secure (obviously).
With the new version, the authentication is totally decorrelated from the API,
to do so, a new service has been deployed in our infrastructure: the
authentication service. This service is publicly available at
https://auth.scalingo.com
and will be responsible for the entire
authentication on Scalingo's infrastructure. Its role is simple: exchange user
authentication information with tokens that can be universally used on the
platform. Scalingo's API will now only accept short lived tokens delivered by
the authentication service. The service is technically based on two well known
web standards: OAuth and JWT.
As for now, if an application needs to list the apps of a specific user, it will need to get a token from the authentication service first. This is done via the OAuth protocol which will redirect the user to the authentication service and then use this token against this API. Once this token has expired, the OAuth dance needs to start again to get a refreshed token.
By decorrelating the authentication process from the resource access, building new authentication and security features is way easier. That's why this new service comes with two handy new features: Access Tokens and 2FA.
Any number of API tokens associated to your account can now be generated and you can assign each of them to any of your custom scripts, tools or integrations. The dashboard will let you see the last usage of your Scalingo account by this service, and more importantly revoke it without impacting any other tools using your Scalingo's account.
Once generated, those tokens will have full access to your account and the resources it can access (ACLs will come later, see the What's next paragraph below). However this token cannot be used to directly query the API, you'll first need to exchange it for a short lived token via the authentication service.
The second new feature is Two-Factor Authentication (2FA in short). It provides an additional security layer to the authentication to your account by combining two different factors: something you know (a username/password) and something you have (a phone or specific hardware). 2FA can be enabled on your profile page. Once enabled, each time you'll try to login to Scalingo, your identity will need to be proven by providing the code given by the chosen device.
For the moment TOTP (Time based One-Time Password) is the only two-factor authentication implemented. Google Authenticator or Authy are built for this protocol. Other standards like U2F will be implemented later. As always, raise your concerns about U2F on the support channel if you want us to boost this item on our internal roadmap.
The software architecture of Scalingo is based on Ruby on Rails (user-facing APIs) and a fleet of microservices written in Go. Our default choice when implementing a new microservice is generally to use Go.
That's why we initially began to write the new auth system with Go. There are really good Go libraries to make OAuth2 clients, manage authentication for authentication or create OAuth2 servers. However the lack of integration between those libraries and the still immature code infrastructure to support web development (in the sense of forms, HTML/CSS, JS, etc) would have considerably affected the development time needed to make this service. We had to change our tune.
When looking at what the Ruby/Rails world has to offer, there is (obviously) a mature implementation of OAuth: Doorkeeper. With the natural capabilities of Rails (building forms and the webpack integration is a breeze) and our current knowledge of Devise and Warden, that was a perfect match. This proved to be a really good choice in the long run because of the maturity of those gems, their community and their interoperability.
The short-lived tokens delivered by the authentication service are JSON Web Tokens (JWT).
One of our technical requirements was to be able to access the following information about a token without querying the authentication service:
As the public API and the authentication service are now two different entities, this is mandatory. Without this, every request made to the public API, would require another request from the API to the authentication service leading to an unwanted overhead and limited scalability.
To address those issues, the chosen solution is JWT. JWT is a standard that defines a compact and self-contained way to securely transmit information between multiple applications. Its internal working is pretty straight forward.
A JWT token is composed of three different parts: header, payload and signature.
The main idea behind a JWT is to store data in the token that can be trusted. JWT achieve that by cryptographically signing the token: the third part of the token is the signature and it is computed with the following method:
HMACSHA512(base64(header) + "." + base64(payload), secret)
If the authentication service and the API share the same secret, the API can verify that the token comes from the authentication service without any need to communicate with the authentication service. This also let us add information in the payload part of the token and ensure that an attacker has not tampered the data.
Please note that there are some security considerations when using JWT. In the
signature example given above, the signature algorithm used is HS512
. The
standard allows the usage of multiple algorithms . They are defined in the
header part of the token itself in the:
{
"typ": "JWT",
"alg": "HS512"
}
This can lead to multiple vulnerabilities like the none
algorithm or some issues
linked to poor
implementation.
That's why on Scalingo the only supported algorithm is HS512
.
Now that we have a sane and modern foundation in place, adding new security features will be easier. Redesigning the authentication layer was the first step. We'll now be able to work on the authorization layer. This will lead to other exciting features like ACLs and Organizations.
Finally those foundations were needed to start having several availability zones available all around the globe (hello North America and Asia).
Our knowledge on all those subjects has been shared by our CTO during the Paris API Meetup of January 2018: "Modern API Authentication 101". You can read the slides on Speakerdeck.
At Scalingo (with our partners) we use trackers on our website.
Some of those are mandatory for the use of our website and can't be refused.
Some others are used to measure our audience as well as to improve our relationship with you or to send you quality content and advertising.