In this second blog of the series, I will talk about securing a very basic interaction that is a microservice communicating with another microservice outside the organization’s network boundaries and on a public cloud. Essentially this is what I want to cover:
Assuming that we have access to a Single Page Application (SPA) UI through the firewall rules and ingress setup when a user tries to load the URL of SPA the single page of the UI loads in the user’s browser. Essentially the Single Page Apps are the web apps/websites that load only a single HTML page. That page then gets dynamically updated based on user interaction without a page refresh. This means that as soon as user hits the URL of the SPA UI, our interaction looks something like this:
Now, this is the real format of the problem we are trying to address. Here the microservices deployed in the cloud platform need to trust the requests coming in from another network (Organization’s Data Center). There has to be a way for establishing this trust, let’s take a quick detour and understand it. Every organization has an internal identity provider through which the authentication and authorization (auth*) services are provided and the same is true for every cloud platform as well. Essentially an IDP (identity provider) is a single point of contact for auth* services for all applications and users under the covered domains(and subdomains). Hence here we need to establish trust between the identity providers managing two different networks. There are a few well-established ways to do that, in-depth understanding of this mechanism is a topic for another time. For the purpose of the current discussion, I will assume that the organization is using active directory federation services with SAML(security assertion markup language) based identity provider and Azure has its own Identity platform backed by Azure Active Directory. Both the identity providers need to understand the encrypted metadata shared between each other as part of auth* requests, both are made to know each other through the manual installation of corresponding certificates and this is what is known as “trust“. One key thing to note is that trust doesn’t mean that network ports are open for these trusted parties to interact with each other. User agent (browser) in this case is the Communication channel between Organization’s IDP and Public Cloud IDP.
IDP (Identity Provider) plays multiple roles and hence known through multiple names like Authorization Server, Authentication Server, Single Sign-On Server, or Security Token Service.
Coming back to our topic, once this trust is established the IDPs know each other. The idea here is to use OAuth for authorization and OpenID Connect for authentication. Here is how the overall process would work:
- The client accesses the website through a browser (discussed above) and obtains auth code after SSO
- Authcode is used to obtain OAuth Tokens from the cloud IDP
- Resources are accessed using the access token
- Resource Server validates the Token before serving the request
Let’s understand these steps in detail to see how we can securely access UI in the front channel and APIs as part of the backchannel. Note that right now we are considering only internal users f the organization as the consumer of all the depicted services. Refer to the pictorial representation, OAuth Based Access, below:
The client accesses the website through a browser (discussed above) and obtains auth code after SSO
This is the first most step and a very critical one. In here user types in website URL, say https://website.organization.com, and the DNS server resolves the IP address to the landing gateway of the website on Azure public cloud. Once this happens the application HTML (single page application) with corresponding javascript modules is loaded on the client browser. Thereafter:
- The auth code for accessing the API in the backend channel is requested from Public Cloud IDP through its Authorization Endpoint.
- If the user is not logged in already with Public Cloud IDP, the request for SSO and auth code to access the API 1 endpoints is redirected to the on-premise IDP through the client browser. Public Cloud IDP knows which organization’s on-premise IDP to redirect to through the mapping between the domain name from URL and pre-established trust.
- The On-premise IDP checks if the user is logged in or not.
- If not then the user is prompted with the SSO login screen to enter the enterprise credentials.
- Post successful authentication or if the user was already logged in then the on-premise IDP prepares response metadata with user attributes (Claims) and signs it.
- The encrypted token is then sent back to the public cloud IDP, via the same browser redirection as there is no direct connection between two IDPs, these SAML responses piggyback on the client browser’s capability to connect with each other)
- Public Cloud IDP decrypts and validates the signed token (remember the trust they have established earlier?)
- If everything completed successfully, the IDP fulfills the original ask from the UI application and sends the auth code.
This is the information required to get the auth code:
response_type: Since we are requesting for auth code, this parameter should always have value as "code" client: It is the system recognisable identity of the client, in this case, it is the UI app. In Azure, there needs to be a service principle generated through an App registration which can represent this app in Microsoft Identity Manager. scope: This defines the type of information and access required. redirect_uri=URI of the Microsoft Identity Platform to which the issued SAML metadata has to be sent code_challenge: UUID coming out of PKCE implementations. Generated from Code Verifier, you can use https://tonyxu-io.github.io/pkce-generator/ for your testing. code_challenge_method: UUID encryption algorithm state=Sender verification random code
Auth code is used to obtain OAuth Tokens from the cloud IDP
Since UI is a single page application, hence the recommended OAuth flow is Authorization Code Grant Flow with PKCE extension. PKCE stands for Proof Key for Code Exchange and pronounced as “pixie”. The working mechanism of pixie is beyond the scope of this article but here is a quick summary of the information that needs to be sent in HTTP POST request for tokens:
grant_type: Since we are using authorization code grant type in OAuth, this attribute should always be set to "authorization_code" client_id: It is the system recognisable identity of the client, in this case, it is the UI app. In Azure, there needs to be a service principle generated through an App registration which can represent this app in Microsoft Identity Manager. code: The auth code as obtained in the previous section scope: This defines the type of information and access required (should include only a subset of what was mention while requesting auth code) redirect_uri=URI of the UI to which the issued tokens have to be sent code_verifier: User-defined verifier use to match code challenge at the IDP level state=Sender verification random code
Note that it is an architectural decision if the refresh token has to be used or not. You may not want the inbuilt library to continuously request access tokens even when the user walks away from the laptop.
Resources are accessed using the access token
Here the client UI (SPA) keeps the ID token with itself as ID tokens are meant for front channel only while requesting the API 1 endpoint using access token as bearer for authorization.
Resource Server validates the Token before serving the request
This the moment of truth for which the entire OAuth dance was conducted.
- API 1 would validate the access token by first retrieving the public key from the JSON Web Key Sets endpoint of the authorization server.
- This public key corresponds to the private key with which the access token has been signed.
- Once the token’s integrity is validated the API 1 would hit Introspect endpoint to verify if the token is still valid or the user has revoked it.
- If everything looks good the request is served.
For Microsoft Identity Platform there is a well known open id configuration which can be referred to for more details around token endpoint and supported services at https://sts.windows.net/db05faca-c82a-4b9d-b9c5-0f64b6755421/.well-known/openid-configuration
In my next blog, I would highlight the possible access patterns for APIs from the front end in view of OAuth responsibility sharing in back channel.