Role of Identity in a microservices architecture — Part 1
Recently, I have been involved in rolling out Okta Identity platform and define best practices for authentication, authorisation, and other security aspects to move towards a zero-trust model in a financial organisation.
In this post, you will see how authentication fits in modern application architecture and some key areas of consideration.
Authentication
Authentication is the process of determining user identity. The result of authentication in an OIDC context is an ID Token. Most likely you would also be getting an access token if you plan to call APIs.
The above diagram is highlighting the authentication process and journey of access token in a microservice environment.
There are few things you need to consider when setting up authentication
- Create separate client per application e.g. SPA, iOS app and Android app. Having separate client allows you to configure better security policies, manage blast radius, design better customer experience, configure token lifecycle appropriately and have better tracing & logging segregation.
- Do not store the access token in session storage or local storage for SPA clients. Use PKCE flow which is built on top of authorization code flow to cater for security issues. Keep the token in memory or ideally in a
HttpOnly
cookie. Leverage the identity provider’s SDK to take care of the authentication and authorisation dance instead of building your own. - Enable adaptive MFA, to keep bad actors away by adding friction. Define your rules based on privilege needs, confidence and risks.
- Turn on attack protection, most of the big identity platforms (okta, auth0, ping, etc..) have systems/mechanisms to keep bad actors away from your application. They also provide signals for threats or anomalies, which you can subscribe to analyse and take actions asynchronously. Ensure to capture these events in your log aggregation platform with appropriate meta-data to correlate so you don’t lose observability.
- Set expiry for access tokens, for each client application consider how long the token should be valid. It may be very different for SPA vs native apps as security exposure is very different.
- Cache access tokens, reduce unnecessary roundtrips that extend your application’s attack surface and optimize plan token limits (where applicable) by storing access tokens obtained from the authorization server. Rather than requesting a new token, use the cached token during future calls until it expires.
- Limit what you expose in your access token to external clients (ideally only primary identifier). If you need more information in the token for backend services, it should be enriched once the request is in your network boundary. Generally, API Gateway or middleware or sidecars are good places to do this. Don’t enrich your token with PII or other sensitive information.
- Validate your token, API Gateway is a good candidate to do this. Most of the gateways have integration available with modern Identity Providers to do this easily. Doing it at the gateway level allows you to ensure requests with invalid token are rejected before it reaches your application. In the case of M2M, you may choose to validate in middleware or sidecar or mesh proxy. Again, in a microservices environment observability is key, so ensure logs & traces are captured and can be correlated.
- Ensure to embed the user token when a request is fanned out from external service to internal services. Generally, your internal services get called with a machine token, it is important to have user context to authorise appropriately.
It is important to consult with platform, product, infosec and app sec teams to define authentication best practices and standards. This will enable product engineering teams to focus on solving customer problems and ensure consistent security policies and standards are applied to keep customer data safe.
In the next part, I will cover authorisation and coarse-grained access. Hopefully, you find some of these insights useful.