In Part 1 of this series we took a look at the high level user stories for a niche directory site that I'm putting together. In this article I'll be discussing how I mapped those stories to microservices, and tracking some of the architectural decisions that were made.
The first thing all users need is to be able to sign up or login to the site. While some content is available to the public, features such as following an org would require a user to be identified.
There's a couple of ways of approaching this.
Once i'd embarked down the Node.js Cognito implementation, I found the documentation and examples lacking for Node.js. AWS does provide the Amplify javascript library but this is geared towards front-end or mobile clients, and skips a back-channel validation step that is specific to servers doing authentication. There is also the AWS SDK but as far as I could tell, it does not allow use of the hosted login page provided by Cognito.
I wound up having to write my own code to retrieve and validate oauth tokens. This is not a secure practice and I'm hoping more standardized libraries become available soon.
So okay here we have our Authentication or 'Account service' - provided by Cognito, and below are the actions associated with it.
Now users can sign up and login to the site. With Cognito we can define roles for our users site-wide e.g. 'Site Administrator' and assign this role to users ourselves through the Cognito management interface. The web application can use the roles to authorize users to perform actions on the site.
However there is a slight complication. We do want that this site be 'multi-tenant' - that is we have multiple organizations and users that can have differing access levels across different orgs.
My research into Cognito and Auth0 support for fine-grained authorization pointed to neither of them supporting this model; they both support just application level roles or some sort of hacky way to achieve object level authorization.
Options
I went with defining a local repo of "users vs objects vs roles". E.g. User #01 has Admin access to Org #003. This keeps the authorization configuration separate from the authentication provider - i.e. no need to customize the authentication configuration based on the application level specifics of org ids and event ids.
Below is the Authorization micro-service and associated functionality. I called it the 'claims service' because it returns user claims to objects that are specific to the site.
The problem now is that when we login, our site needs to check with two different sources to authenticate and authorize a user.
We need to pass these to each microservice for them to know the identity and access. Also each service needs to validate these tokens to ensure they're not expired nor tampered with.
Here is a sequence diagram of the login flow and a microservice call.
Now that we have our authentication and authorization figured out, lets move to the core microservices in Part 3 of this series.