- 06 Sep, 2018
- Majid Fatemian
We have been benefiting from migrating our architectures to MicroServices for a few years. These fine grained services have helped make us technology and protocol agnostic with resilience and elasticity. Apart from all the benefits (and challenges), one aspect remains really important as we grow in number of different services: security.
It is extremely critical for us to control who has access to what and who is allowed to perform actions.
Authentication vs. Authorization
When we are talking about security, it’s important to distinguish between these two:
Authentication is verifying the identity of a user or process. Authorization is permitting performing of a specific action to a user or a process.
Identifying a user or process is a very critical operation that involves checking credentials and other sensitive data. A simple mistake could result in disasters. Luckily, there are numerous solutions to solve this problem and our approach for such sensitive matter is: reuse instead of build. Encouraging to reuse what’s already built by experts. Our Authentication solution consists of the followings:
1 — Active Directory
Provides simple and easy to use authentication for our internal users. But we have encountered other scenarios, like internal users want to use external SaaS applications and Cloud providers. Also our external partners are willing to have access to some of the internal tools provided for them. Therefore we have to extend out Authentication using:
2 — Okta
Okta syncs with our ActiveDirectory system and provides access for our internal and external users by providing SAML (Security Assertion Markup Language). The SAML is signed using a private key by the identity provider (Okta) and each application has access to the corresponding public key, so they can assure if the SAML is legitimately signed or not.
SAML is XML based and we have long passed dealing with XML and its human unfriendliness, that’s why we introduced:
3 — JWT
JSON Web Token has made authentication a lot easier by providing an access token which asserts some claims in it. The same signing/verification process as above ensures that the token and the claims are legitimate, and they can be used by different services to verify the validity of the user or process.
The structure of JWT is base64 encoded values of following three sections.
Header provides some meta data about the token. Signature ensures the validity of the identity provider and the payload itself. Payload after being decoded gives a JSON object similar to the following:
Keep in mind that no sensitive data should be included in the JWT, all the data is easily decodable.
After ensuring that each MicroService is aware of the identity of the user or process accessing it, we need to ensure that the action is permitted to be performed on that service by the user. We have multiple partners which may use the same MicroService but we have to ensure that they have only access to the parts and actions they are allowed and nothing more.
Historically, each of our services implemented Authorization differently. Some were using ActiveDirectory groups to permit/block users and actions. Some were having their own custom implementations. It was scattered, inconsistent and very difficult to monitor.
The desired Solution
We wanted our Authorization to have the following criteria to make it adaptable company-wide, as well as improving our security:
- Centralized Management
The majority of existing solutions would make a round trip to a central authorization server for each request to verify permissions. Due to the large number of round trips, we experienced significant network traffic and latency in our applications. We knew this could not scale and needed a self-contained service to avoid any round trips. Besides that, many of the existing solutions support Roles, Groups and Access Levels and having flexibility of fine grained management and control comes with a price of additional complexity.
Unnecessary complexity makes the application prone to errors and therefore vulnerabilities.
As mentioned earlier, each MicroService could serve different business partner so access and performed actions on each should be controlled. We have defined this level of control within each MicroSearch as an Area. A user or a process that has access to all the Areas within the MicroService has a Global access.
Looking at the organization of these access controls we can easily fit them in to a JSON structure.
JWT goes hand in hand with JSON, therefore we embedded the Authorization data inside the JWT.
Having Access Control data inside JWT provides a lot of flexibility. After logging in, each user gets assigned a JWT which includes Access control data. Each MicroService is not only able to Authenticate the user, but also to Authorize the user as well and allow/block certain actions based on the permissions. This validation happens as a self-contained process, there is no need for a central service to verify an access therefore there is no round-trip.
The Big Picture
Overall the architecture looks like the following:
After logging in with Okta, user gets redirected to a JWT identity provider, before generating the token, providers communicates with an Authorization service and gets all the permissions for the user, merges it with other data and then generate the token and send it back to the user.
Ease of integration by providing SDKs
With the current integration as is, we have checked 3 out of 5 checkboxes that we defined above. It is a decentralized system which is simple and scalable. To make integration easier we provided the SDKs for major languages being used in our technology stacks, including C#, Go, NodeJS and PHP.
In the SDKs we have embedded the Monitoring on who is accessing which resource within which Area and what’s the action they are taking. By shipping all the logs to ElasticSearch we could also put alerts on top of suspicious attempts to draw attention to dig in to the activities of that particular user or process.
Reuse > Recreate (Buy vs. Build)
Security is a sensitive topic and very important. But there are plenty of tools already built which have been proven over time and they are actively maintained and patched. Try to reuse what is already built and proven rather than recreating it.
Token based Auth
Token based Auth like JWT is scalable, decentralized, stateless and easy to use. Also you can embed custom data in to parts of it which could be retrieved and used in any application. Keep in mind that no sensitive data should be embedded in the token based authentication as it is decodable easily. Signature is only to verify the validity of data.
Simple does not mean Insecure
When it comes to security try to keep things simple. Security does not necessarily need to be complex. Over complicating the case might work counter productive and make the setup prone to vulnerabilities. Keeping security simple makes it easier to monitor, trace and debug.