<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>JWT (JSON Web Token) on Digi Hunch</title><link>https://static.digihunch.com/tags/jwt-json-web-token/</link><description>Recent content in JWT (JSON Web Token) on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Tue, 08 Apr 2025 14:55:55 -0400</lastBuildDate><atom:link href="https://static.digihunch.com/tags/jwt-json-web-token/index.xml" rel="self" type="application/rss+xml"/><item><title>Authentication to kube-apiserver via OIDC</title><link>https://static.digihunch.com/2023/07/authenticate-kube-apiserver-via-oidc/</link><pubDate>Fri, 28 Jul 2023 09:20:00 -0400</pubDate><guid>https://static.digihunch.com/2023/07/authenticate-kube-apiserver-via-oidc/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-kubectl-oidc.webp" alt="Featured image of post Authentication to kube-apiserver via OIDC" /&gt;&lt;h2 class="wp-block-heading"&gt;Background&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are many benefits of using OIDC to authenticate to kube-api server, especially with multiple clusters that need consistent log-in experience. With the &lt;a href="https://static.digihunch.com/2023/07/oauth-2-0-and-oidc-2-of-2/"&gt;last post&lt;/a&gt; on how OIDC Authorization Code Flow works, now I will discuss options with authentication for kubectl to connect to kube API server.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To start, let&amp;#8217;s look at the anatomy of kubeconfig file. The full schema is in the &lt;a href="https://kubernetes.io/docs/reference/config-api/kubeconfig.v1/"&gt;documentation&lt;/a&gt;. Looking at my kubeconfig file, there are three sections:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;clusters: each entry specifies a cluster&amp;#8217;s name, server address and certificate authority data (in base64 encoding or a file location). &lt;/li&gt;&#10;&lt;li&gt;users: each entry specifies a username. Some users are identified with client key and certificate. Some specify a command to provide client authentication. Refer to the &lt;a href="https://kubernetes.io/docs/reference/access-authn-authz/authentication/"&gt;authentication strategies&lt;/a&gt;.&lt;/li&gt;&#10;&lt;li&gt;contexts: each entry links a user to a cluster&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Therefore, the key to use OIDC integration, is to use command to provide client authentication. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-vanilla-kubernetes"&gt;Vanilla Kubernetes&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The documentation on authenticating has a diagram on how to use &lt;a href="https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens"&gt;OpenID Connect tokens&lt;/a&gt;. The diagram does not give details on how access_token and id_token were obtained. So it could be any OIDC flow (Authorization Code Flow, Implicit Flow, etc) as we have &lt;a href="https://static.digihunch.com/2023/07/oauth-2-0-and-oidc-2-of-2/"&gt;discussed&lt;/a&gt;.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full is-resized"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="828" src="https://static.digihunch.com/wp-content/uploads/2023/07/oidc-flow.webp" alt="" class="wp-image-12940" style="width:651px;height:auto" srcset="https://static.digihunch.com/wp-content/uploads/2023/07/oidc-flow.webp 1024w, https://static.digihunch.com/wp-content/uploads/2023/07/oidc-flow-300x243.webp 300w, https://static.digihunch.com/wp-content/uploads/2023/07/oidc-flow-768x621.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Although the instruction does not mandate which OIDC flow to use, we should use Authorization Code Flow in this architecture. The API server needs to trust the OIDC issuer, and the &lt;a href="https://kubernetes.io/docs/reference/access-authn-authz/authentication/#configuring-the-api-server"&gt;document&lt;/a&gt; covers how to configure API server.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the diagram, step 2 and step 3 are required by kubectl itself does not perform these activities. All kubectl does is carry the JWT token in the Authorization Bearer. Vanilla Kubernetes does not provide a solution for OIDC integration. It only provides some instructions and we still need some helper scripts to glue all these instruction steps together.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are many open-source project for this purpose. For example, Jetstack has &lt;a href="https://github.com/jetstack/kube-oidc-proxy"&gt;kube-oidc-proxy&lt;/a&gt; and Int128 developed &lt;a href="https://github.com/int128/kubelogin"&gt;kubelogin&lt;/a&gt;. Other projects such as &lt;a href="https://github.com/vimond/k8s-auth-client"&gt;k8s-auth-client&lt;/a&gt;, &lt;a href="https://github.com/micahhausler/k8s-oidc-helper"&gt;k8s-oidc-helper&lt;/a&gt;, and &lt;a href="https://github.com/vmware-archive/gangway"&gt;gangway&lt;/a&gt; are no longer being updated. The kubelogin project remains influential. It has a clear diagram too:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="503" src="https://static.digihunch.com/wp-content/uploads/2023/07/kube-oidc-proxy.webp" alt="" class="wp-image-12941" srcset="https://static.digihunch.com/wp-content/uploads/2023/07/kube-oidc-proxy.webp 1024w, https://static.digihunch.com/wp-content/uploads/2023/07/kube-oidc-proxy-300x147.webp 300w, https://static.digihunch.com/wp-content/uploads/2023/07/kube-oidc-proxy-768x377.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;From the diagram we can see kubelogin proposes authorization code flow. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Also, one design concern with Kubernetes control plane is the placement of endpoint. From this diagram we can see that even if the cluster endpoint is on private subnet, OIDC integration should still work. The control plane (specifically kube-apiserver) initiates outbound connection to OIDC Provider. There is no inbound connection to it from the OIDC provider.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the kubelogin setup, the redirect URI is set to localhost:8080 because it stands up a server on the same host where browser is running. The browser can always resolve localhost. For a full configuration steps, Okta has &lt;a href="https://developer.okta.com/blog/2021/11/08/k8s-api-server-oidc"&gt;this blog post&lt;/a&gt; on how to use kubelogin as helper, and Okta as Authorization Server to authenticate kubectl via OIDC. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Step-by-step with kubelogin&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We&amp;#8217;ll go through an example with int128/kubelogin because it works with any Kubernetes flavour, including managed Kubernetes services. It is also fairly simple. The instruction covers a few types of Authorization Servers (Google Identity Platform, KeyCloak, Dex with GitHub, Okta and Ping Identity). I&amp;#8217;ll take KinD cluster as an example and use Azure AD as Authorization Server.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;First, we&amp;#8217;ll register an App in Azure Portal. Go to &amp;#8220;App Registrations&amp;#8221; and &amp;#8220;New registration&amp;#8221;. Give it a name &amp;#8220;kubeoidc&amp;#8221; and set Redirect URI to &amp;#8220;Web&amp;#8221; with URL &amp;#8220;localhost:8000&amp;#8221;. Click on Register.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="399" src="https://static.digihunch.com/wp-content/uploads/2023/07/kubeoidc-entra-1.webp" alt="" class="wp-image-12942" srcset="https://static.digihunch.com/wp-content/uploads/2023/07/kubeoidc-entra-1.webp 1024w, https://static.digihunch.com/wp-content/uploads/2023/07/kubeoidc-entra-1-300x117.webp 300w, https://static.digihunch.com/wp-content/uploads/2023/07/kubeoidc-entra-1-768x299.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The next page shows the details for this app. The Application (client) ID is important for next steps. Click on &amp;#8220;Add a certificate or secret&amp;#8221;, then &amp;#8220;New client secret&amp;#8221;, put in expiry and description. The secret value is generated and displayed on the next page, which is important for our next step. &lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="204" src="https://static.digihunch.com/wp-content/uploads/2023/07/kubeoidc-entra-2.webp" alt="" class="wp-image-12943" srcset="https://static.digihunch.com/wp-content/uploads/2023/07/kubeoidc-entra-2.webp 1024w, https://static.digihunch.com/wp-content/uploads/2023/07/kubeoidc-entra-2-300x60.webp 300w, https://static.digihunch.com/wp-content/uploads/2023/07/kubeoidc-entra-2-768x153.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We also need to find out the issuer URL. From the App page above, click on &amp;#8220;Endpoints&amp;#8221; and find out the URL from field &amp;#8220;OpenID Connect metadata document&amp;#8221;. My metadata document URL looks like: &lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;https://login.microsoftonline.com/xx8x8xx8-7777-66yy-55b5-444aaaaa3322/v2.0/.well-known/openid-configuration&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The OIDC Issuer URL is the part before .well-known. In this case, it is:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;https://login.microsoftonline.com/xx8x8xx8-7777-66yy-55b5-444aaaaa3322/v2.0&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now we have collected what we need for the next few steps: ClientID, ClientSecret and OIDC Issuer URL. We can then create the KinD cluster, and reference ClientID and IssuerURL in the cluster configuration:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cat &lt;span style="color:#e6db74"&gt;&amp;lt;&amp;lt; EOF &amp;gt; kind-config.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;kind: Cluster&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;apiVersion: kind.x-k8s.io/v1alpha4&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;nodes:&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; - role: control-plane&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; kubeadmConfigPatches:&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; - |&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; kind: ClusterConfiguration&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; apiServer:&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; extraArgs:&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; oidc-issuer-url: https://login.microsoftonline.com/xx8x8xx8-7777-66yy-55b5-444aaaaa3322/v2.0&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; oidc-client-id: 99999e88-e777-6666-c5c5-c444444d3d22&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; - role: worker&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; - role: worker&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; - role: worker&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;EOF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kind create cluster --config&lt;span style="color:#f92672"&gt;=&lt;/span&gt;kind-config.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The command will create cluster and configure kubeconfig file with a user named kind-kind as admin, which isn&amp;#8217;t what we need. Now we use kubelogin helper command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl oidc-login setup &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --oidc-issuer-url&lt;span style="color:#f92672"&gt;=&lt;/span&gt;ISSUER_URL &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --oidc-client-id&lt;span style="color:#f92672"&gt;=&lt;/span&gt;YOUR_CLIENT_ID &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --oidc-client-secret&lt;span style="color:#f92672"&gt;=&lt;/span&gt;YOUR_CLIENT_SECRET&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This command will act as the client in the OIDC flow and prompts you to log in to Azure in a Browser. Once logged on, it gives you the next few commands to run. Since we&amp;#8217;ve already created a cluster with the Issuer URL and Client ID, we can skip creating cluster, and run the steps to:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;bind the Azure user to a ClusterRole&lt;/li&gt;&#10;&lt;li&gt;set up the kubeconfig locally with a user oidc, which needs to execute the oidc-login command&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Lastly, we can test the oidc user with &lt;code&gt;kubectl --user=oidc get nodes&lt;/code&gt;. We can also set the context to use oidc user by default. Voila.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;From this example, we learned how to configure OIDC integration for any Kubernetes distros. The steps that we need to take are:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;On the cluster side, we customize kube-api-server flag with OIDC provider info. Specifically, we provided Issuer URL and client ID in this example. In the OIDC Flow diagram, this step establish a trust from the Resource Server (K8s cluster) to the OIDC provider. &lt;/li&gt;&#10;&lt;li&gt;On the kubectl side, kubectl itself cannot fulfill all the duties of a client app in the OIDC Flow. It needs a helper script and we&amp;#8217;ve made friend with int128/kubelogin.&lt;/li&gt;&#10;&lt;li&gt;On the Identity Store side, we expect it to be an OIDC-compliant Authorization Server. Otherwise, we consider using Dex as a broker in between. &lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When it comes to managed service, many allows us to customize the OIDC related flags for kube-api-server. Let&amp;#8217;s look at how some managed services get this to work.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-azure-kubernetes-service"&gt;Azure Kubernetes Service&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I find the kubectl authentication with AKS highly opinionated in its documentation. The recommendation is using Azure Active Directory as identity store but I don&amp;#8217;t find it work with other OIDC providers.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To work with Azure Active Directory, you would configure the cluster and specify Azure role-based access control with the Group UUID in Azure AD. On the client side, you will need to install &lt;a href="https://github.com/Azure/kubelogin"&gt;Azure&amp;#8217;s kubelogin&lt;/a&gt; utility. Do not confuse it with int128&amp;#8217;s kubelogin, which is for any cluster. This &lt;a href="https://blog.baeke.info/2021/06/03/a-quick-look-at-azure-kubelogin/"&gt;kubelogin&lt;/a&gt; is just for Azure. Once installed, you can use az-cli command to update your kubeconfig file, which call this utility from kubeconfig. To examine details about &lt;a href="https://github.com/digihunch/cloudkube/blob/4735426f5c3e8f3b448bdb4f4b8ef33d340f71eb/azure/modules/bastion/files/bastion_init_sh.tpl#L27"&gt;client configuration&lt;/a&gt; and &lt;a href="https://github.com/digihunch/cloudkube/blob/4735426f5c3e8f3b448bdb4f4b8ef33d340f71eb/azure/modules/aks/main.tf#L43"&gt;AAD integration&lt;/a&gt;, check out the Terraform template in the &lt;a href="https://github.com/digihunch/cloudkube/tree/main/azure"&gt;azure&lt;/a&gt; directory of my &lt;a href="https://github.com/digihunch/cloudkube"&gt;cloudkube&lt;/a&gt; project.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full is-resized"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="620" src="https://static.digihunch.com/wp-content/uploads/2023/07/aks-kubeconfig.webp" alt="" class="wp-image-12944" style="width:782px;height:auto" srcset="https://static.digihunch.com/wp-content/uploads/2023/07/aks-kubeconfig.webp 1024w, https://static.digihunch.com/wp-content/uploads/2023/07/aks-kubeconfig-300x182.webp 300w, https://static.digihunch.com/wp-content/uploads/2023/07/aks-kubeconfig-768x465.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The document only covers &lt;a href="https://learn.microsoft.com/en-us/azure/aks/concepts-identity#azure-ad-integration"&gt;Azure AD integration&lt;/a&gt; and I tried to find if there&amp;#8217;s a way to integrate with third-party OIDC providers. Unfortunately I have no luck. ChatGPT points me to &lt;a href="https://learn.microsoft.com/en-gb/azure/aks/use-oidc-issuer"&gt;a page about enabling OIDC provider&lt;/a&gt; but it is in the context of workload identity and it does not allow you to customize the issuer. So it&amp;#8217;s completely irrelevant. Because you cannot customize OIDC issuer, etc, it simply won&amp;#8217;t work with any third-party OIDC provider. Sure enough, most of Azure&amp;#8217;s client use Azure AD anyways.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Azure makes it streamlined to configure OIDC integration of AKS with Azure AD, its own identity store. To my disappointment, it is currently not possible to integrate with third-party OIDC provider for authentication at cluster endpoint. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-rosa"&gt;ROSA&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;RedHat OpenShift on AWS (ROSA) is a. However it reflects how OpenShift configures third-party identity provider. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Let&amp;#8217;s first create a cluster off custom VPC and private endpoint. This is covered in my previous &lt;a href="https://static.digihunch.com/2023/06/kubernetes-paas-and-red-hat-openshift/"&gt;post&lt;/a&gt; about ROSA. After the cluster creation, we&amp;#8217;re at the point where we can run &lt;code&gt;oc&lt;/code&gt; command against cluster endpoint from Bastion host because it is a private cluster. However, being a private cluster is irrelevant to how we configure OIDC integration. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We&amp;#8217;ll use Azure AD again as OIDC provider. So we need to register an app the same way as I did above in the kubelogin example. We need to have Client ID, Secret and OIDC issuer URL. For RedirectURI, go to your &lt;a href="https://console.redhat.com/openshift/"&gt;OpenShift console&lt;/a&gt;, and under &lt;code&gt;Cluster&lt;/code&gt;, click on the cluster name → access control → identity providers → select OpenID. Note the page clearly states that this is Authorization Code Flow, and the OAuth Callback URL is provided. Use it to regiter App in Azure, and fill in the page with Client ID, Secret and OIDC issuer URL. Also fill in other fields accordingly and click on Add. Now you should have it configured!&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="288" src="https://static.digihunch.com/wp-content/uploads/2023/07/rosa-idp.webp" alt="" class="wp-image-12946" srcset="https://static.digihunch.com/wp-content/uploads/2023/07/rosa-idp.webp 1024w, https://static.digihunch.com/wp-content/uploads/2023/07/rosa-idp-300x84.webp 300w, https://static.digihunch.com/wp-content/uploads/2023/07/rosa-idp-768x216.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Identity Providers for OpenShift cluster&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The next step is trying to login. From Bastion host, run &lt;code&gt;oc login&lt;/code&gt; and it will give me an URL to use. The URL contains the cluster endpoint, which resolvable from the Bastion Host itself. However I need a Browser session here, so I have to run Bastion host as SOCKS5 proxy and tell Chrome on my MacBook to use it:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;~ open /Applications/Google&lt;span style="color:#ae81ff"&gt;\ &lt;/span&gt;Chrome.app --args --proxy-server&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;socks5://localhost:1080&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The browser session redirects to Azure AD for log in. Once completed the webpage will display a token that I can use with &lt;code&gt;oc login&lt;/code&gt; command. Run this command with token from Bastion, I&amp;#8217;m logged in:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;oc login --token&lt;span style="color:#f92672"&gt;=&lt;/span&gt;sha256~3ZT5JGWELOcBzfohftEm9D2UwoOVFvATASuZk3_uxps --server&lt;span style="color:#f92672"&gt;=&lt;/span&gt;https://api.dhc.62q3.p1.openshiftapps.com:6443&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;oc whoami&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;oc get no&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;At this point, if I run &lt;code&gt;oc whoami&lt;/code&gt;, I get the user name. However, this user cannot do anything. This is because it is not associated with a role yet. You grant more permission to this user: go back to OpenShift console, Clusters → ClusterName → Access Control → Cluster Roles and Access → Add user. Here you can map the user name to a role (let&amp;#8217;s say ClusterAdmin). Then this user will have its priviledge:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="346" src="https://static.digihunch.com/wp-content/uploads/2023/07/oc-command.webp" alt="" class="wp-image-12947" srcset="https://static.digihunch.com/wp-content/uploads/2023/07/oc-command.webp 1024w, https://static.digihunch.com/wp-content/uploads/2023/07/oc-command-300x101.webp 300w, https://static.digihunch.com/wp-content/uploads/2023/07/oc-command-768x260.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The whole Flow works with private cluster, because the redirect URI is resolvable from the Bastion host. If you chose to expose cluster endpoint publicly (not recommended), you can perform the above steps directly from your MacBook or Laptop. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;So the ROSA experience has been smooth. Unlike kubectl, the &lt;code&gt;oc&lt;/code&gt; utility can act as the Client App in Authorization Code Flow. The other part of the configuration such as client secret and issuer URL are made in OpenShift console. Good job!&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-elastic-kuberentes-service"&gt;Elastic Kuberentes Service&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;EKS allows you to specify &lt;a href="https://aws.amazon.com/blogs/containers/introducing-oidc-identity-provider-authentication-amazon-eks/"&gt;OIDC issuers&lt;/a&gt; from console or CLI to set up third-party OIDC configuration. There is a blog &lt;a href="https://developer.okta.com/blog/2021/10/08/secure-access-to-aws-eks#add-okta-as-an-oidc-provider-on-your-eks-cluster"&gt;post&lt;/a&gt; from Okta on this, which works for private clusters. In the instruction, the author first manually created kubeconfig file with int128 kubelogin, and then bind ClusterRole with the user.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full is-resized"&gt;&lt;img loading="lazy" decoding="async" width="1021" height="1024" src="https://static.digihunch.com/wp-content/uploads/2023/07/eks-idp.webp" alt="" class="wp-image-12948" style="width:556px;height:auto" srcset="https://static.digihunch.com/wp-content/uploads/2023/07/eks-idp.webp 1021w, https://static.digihunch.com/wp-content/uploads/2023/07/eks-idp-300x300.webp 300w, https://static.digihunch.com/wp-content/uploads/2023/07/eks-idp-150x150.webp 150w, https://static.digihunch.com/wp-content/uploads/2023/07/eks-idp-768x770.webp 768w, https://static.digihunch.com/wp-content/uploads/2023/07/eks-idp-410x410.webp 410w" sizes="auto, (max-width: 1021px) 100vw, 1021px" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The blog post is very detailed. Instead of repeating it, I would like to discuss two SSO models available in AWS. I summarize them as below:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="721px" viewBox="-0.5 -0.5 721 671" style="max-width:100%;max-height:671px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="0" width="720" height="670" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;rect x="30" y="375" width="220" height="235" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 218px; height: 1px; padding-top: 382px; margin-left: 31px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;EKS Cluster&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="140" y="394" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;EKS Cluster&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="60" y="480" width="110" height="40" rx="6" ry="6" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 500px; margin-left: 61px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RoleBinding&lt;br&gt;ClusterRoleBinding&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="115" y="504" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RoleBinding&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;ellipse cx="440" cy="430" rx="60" ry="25" fill="#ffe6cc" stroke="#d79b00" pointer-events="all"&gt;&lt;/ellipse&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 430px; margin-left: 381px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;OIDC compatible&lt;br&gt;Identity Provider&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="440" y="434" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;OIDC compatible&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="30" y="40" width="230" height="220" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 228px; height: 1px; padding-top: 47px; margin-left: 31px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;EKS Cluster&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="145" y="59" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;EKS Cluster&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;ellipse cx="620" cy="95" rx="50" ry="25" fill="#ffe6cc" stroke="#d79b00" pointer-events="all"&gt;&lt;/ellipse&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 95px; margin-left: 571px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;SAML compliant&lt;br&gt;Identity Provider&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="620" y="99" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;SAML compliant&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="280" y="65" width="100" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 95px; margin-left: 281px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;IAM Role via&lt;br&gt;PermissionSet&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="330" y="99" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;IAM Role via&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="410" y="65" width="100" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 95px; margin-left: 411px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;AWS IAM&lt;br&gt;Identity Center&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="460" y="99" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;AWS IAM&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 380 95 L 410 95" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 250 95 L 280 95" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 570 95 L 510 95" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 96px; margin-left: 541px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;SAML&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="541" y="99" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;SAML&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="180" y="75" width="70" height="40" rx="6" ry="6" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 95px; margin-left: 181px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;ConfigMap&lt;br&gt;aws-auth&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="215" y="99" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;ConfigMap&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="80" y="75" width="70" height="40" rx="6" ry="6" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 95px; margin-left: 81px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;group&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="115" y="99" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;group&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="80" y="410" width="70" height="40" rx="6" ry="6" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 430px; margin-left: 81px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;group&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="115" y="434" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;group&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="30" y="260" width="270" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 268px; height: 1px; padding-top: 275px; margin-left: 31px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;EKS SSO Model 1 &amp;#8211; IAM Identity Center&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="165" y="279" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;EKS SSO Model 1 &amp;#8211; IAM Identity Center&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="50" y="610" width="180" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 178px; height: 1px; padding-top: 625px; margin-left: 51px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;EKS SSO Model 2 &amp;#8211; direct OIDC&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="140" y="629" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;EKS SSO Model 2 &amp;#8211; direct OIDC&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 150 430 L 380 430" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 431px; margin-left: 311px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;OIDC&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="311" y="434" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;OIDC&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="60" y="140" width="110" height="40" rx="6" ry="6" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 160px; margin-left: 61px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RoleBinding&lt;br&gt;ClusterRoleBinding&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="115" y="164" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RoleBinding&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 150 95 L 180 95" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;rect x="80" y="210" width="70" height="40" rx="6" ry="6" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 230px; margin-left: 81px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Role&lt;br&gt;ClusterRole&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="115" y="234" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Role&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 115 180 L 115 210" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 115 115 L 115 140" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;rect x="60" y="555" width="110" height="40" rx="6" ry="6" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 575px; margin-left: 61px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Role&lt;br&gt;ClusterRole&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="115" y="579" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Role&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 115 450 L 115 480" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 115 520 L 115 555" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 270 330 L 370 330 L 370 380 L 340 380 L 320 410 L 320 380 L 270 380 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 355px; margin-left: 271px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;OIDC claim:&lt;br&gt;user=john&lt;br&gt;group=admin&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="320" y="359" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;OIDC claim:&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="280" y="160" width="100" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 190px; margin-left: 281px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;AssumeRole&lt;br&gt;WithWebIdentity&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="330" y="194" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;AssumeRole&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;ellipse cx="480" cy="190" rx="60" ry="20" fill="#ffe6cc" stroke="#d79b00" pointer-events="all"&gt;&lt;/ellipse&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 190px; margin-left: 421px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Pipeline&lt;br&gt;User&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="480" y="194" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Pipeline&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 380 190 L 420 190" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 250 115 L 280 190" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank" rel="noopener"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Text is not SVG &amp;#8211; cannot display&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Both are SSO models for EKS. Model 1 (IAM Identity Center) is home grown as AWS using IAM Identity Center (formerly AWS SSO). Users start with an IAM principal (AWS construct) and use the &lt;code&gt;aws-auth&lt;/code&gt; config map to tie them to Kubernetes groups. This AWS &lt;a href="https://aws.amazon.com/blogs/containers/a-quick-path-to-amazon-eks-single-sign-on-using-aws-sso/"&gt;blog post &lt;/a&gt;and this &lt;a href="https://repost.aws/knowledge-center/eks-configure-sso-user"&gt;support article&lt;/a&gt; are based on the IAM Identity Center model. On the other hand, Model 2 (Direct OIDC) is the vanilla Kubernetes approach. It takes group claim from OIDC identity token. The Okta blog &lt;a href="https://developer.okta.com/blog/2021/10/08/secure-access-to-aws-eks#add-okta-as-an-oidc-provider-on-your-eks-cluster"&gt;post&lt;/a&gt; is based on this model.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The IAM identity center model works with SAML compliant identity providers, oftentimes Active Directory, although there seems to be a plan to &lt;a href="https://docs.aws.amazon.com/singlesignon/latest/OIDCAPIReference/Welcome.html"&gt;support OIDC-compliant identity provider&lt;/a&gt; as well in the future. Even if it was supported today, I&amp;#8217;d still prefer model 2 for working with any OIDC compliant identity provider because it&amp;#8217;s simple. Why not leverage K8s&amp;#8217; native capability? For identity providers that do not support OIDC natively, or does not issue group claim (e.g. &lt;a href="https://aws.amazon.com/blogs/containers/authenticate-to-amazon-eks-using-google-workspace/"&gt;Google workspace&lt;/a&gt;), as we &lt;a href="https://static.digihunch.com/2023/07/oauth-2-0-and-oidc-2-of-2/"&gt;discussed&lt;/a&gt;, we can also consider alternatives such as Dex as identity broker. However, this model comes handy when a pipeline user with IAM role needs to authenticate into EKS.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;One should always go for the direct OIDC model, if the upstream identity provider supports OIDC. The provider itself can even be AWS Cognito User pool. Cognito User Pool itself supports federated identity but again, I would directly connect EKS cluster to the OIDC compatible identity provider, instead of going through Cognito User Pool. As a result, the only use case where Cognito user pool is absolutely necessary, is when we need the Cognito user pool itself as the identity provider, as we have in &lt;a href="https://github.com/digihunch/cloudkube"&gt;CloudKube&lt;/a&gt;&amp;#8216;s &lt;a href="https://github.com/digihunch/cloudkube"&gt;eks&lt;/a&gt; directory. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-summary"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Configuring OIDC provider for cluster endpoint can be confusing and we need to understand how OIDC flows work. I dived into OIDC in the previous post and in this post, I explained how to get it to work with vanilla Kubernetes. I summarized the three requirements and looked at the OIDC provider option with some managed services. Then I went through OIDC options for AKS, ROSA and EKS.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2023/07/oauth-2-0-and-oidc-2-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;OAuth 2.0 and OIDC 2 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2023/08/control-tower-aws-landing-zone/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Orchestrate Landing Zone with AWS Control Tower&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>OAuth 2.0 and OIDC 2 of 2</title><link>https://static.digihunch.com/2023/07/oauth-2-0-and-oidc-2-of-2/</link><pubDate>Thu, 13 Jul 2023 21:24:01 -0400</pubDate><guid>https://static.digihunch.com/2023/07/oauth-2-0-and-oidc-2-of-2/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-oidc-oauth-2.webp" alt="Featured image of post OAuth 2.0 and OIDC 2 of 2" /&gt;&lt;p class="wp-block-paragraph"&gt;I wrote a &lt;a href="https://static.digihunch.com/2020/03/oauth-and-openid-connect/"&gt;brief&lt;/a&gt; on this topic a while back. Now I need to configure OIDC in a few occasions I decide to dive deeper into the flows this time. As I stated in the last post, Nate Barbettini&amp;#8217;s &lt;a href="https://www.youtube.com/watch?v=996OiexHze0"&gt;presentation&lt;/a&gt; from 2017 was awesome and I viewed it again. Slides are available &lt;a href="https://speakerdeck.com/nbarbettini/oauth-and-openid-connect-in-plain-english"&gt;here&lt;/a&gt;. Another great reference is &lt;a href="https://www.deepnetwork.com/blog//2019/11/08/oauth2-oicd-pkce.html"&gt;this post&lt;/a&gt; from DeepNetwork Developer&amp;#8217;s blog.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-back-channel-and-front-channel"&gt;Back Channel and Front Channel&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To understand why there are several different flows, it is important to understand the difference between back channel and front end channel.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In web development, traditional architecture involves a frontend (e.g. Browser, or any client-side app) and backend server. The web frontend is written in HTML, CSS, JavaScript, etc. There are also web frameworks such as Django, Angular, to save developers time. Backend (server-side) is responsible for storing and organizing data to ensure frontend can function. There might be multiple backend servers, such as session cache, data store, API server, etc. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In a nutshell, server-to-server communication is back channel, and browser-to-server communication is front channel. From security perspective, we regard front-channel as less secure, because we have less control of the location of the front-end and browser is easy to tamper with.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="550" src="https://static.digihunch.com/wp-content/uploads/2023/07/oauth-channels.webp" alt="" class="wp-image-12934" srcset="https://static.digihunch.com/wp-content/uploads/2023/07/oauth-channels.webp 1024w, https://static.digihunch.com/wp-content/uploads/2023/07/oauth-channels-300x161.webp 300w, https://static.digihunch.com/wp-content/uploads/2023/07/oauth-channels-768x413.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;figcaption class="wp-element-caption"&gt;Back Channel and Front Channel (source https://www.okta.com/blog/2019/04/oauth-when-things-go-wrong/)&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For traditional web applications with client-server architecture, when they communicate with third-party servers, they can initiate the communication from their backends, creating a back channel for better security posture. Single-page applications (SPAs) are applications without their own backends. When building an SPA, the front-end developer deals with frontend frameworks such as React, Angular or Next.js. When SPAs have to communicate with third-party APIs, they have to create a front channel. Also the API must support Cross-Origin Resource Sharing (CORS) for the browser to allow the cross-domain communication.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the web development world, there is also Native App (aka Mobile App). Native App to Server communication is also considered back channel as we consider the client-side (Mobile App) secure. Nate&amp;#8217;s talk makes the following recommendation for the flow (grant type) and I rephrase it as below:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-white-background-color has-background"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Architecture&lt;/th&gt;&lt;th&gt;Recommendation&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Traditional Web Application (client-server architecture)&lt;/td&gt;&lt;td&gt;There are both front and back channels. Use authorization code flow&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Single Page Application (SPA, e.g. JavaScript) with API backend&lt;/td&gt;&lt;td&gt;Front channel only. Use implicit flow &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Native mobile App&lt;/td&gt;&lt;td&gt;Use authorization code flow with PKCE&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Microservices and APIs (Machine-to-Machine)&lt;/td&gt;&lt;td&gt;Back-channel only. Use Client credentials flow&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The message, is that we should use Authorization Code Flow and use back channel, so long as the required component (backend) exists in the architecture. Note that the terminology for these types of applications may differ. For example, when you try to create an OIDC client for Amazon Cognito user pool, here&amp;#8217;s how it categorizes client capability (app type):&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Public client: A native, browser, or mobil-device app. Cognito API requests are made from user systems that are not trusted with a client secret.&lt;/li&gt;&#10;&lt;li&gt;Confidential client: A server-side application that can securely store a client secret. Cognito API requests are made from a central server.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Get used to different terms describing the same concept.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;OAuth 2.0 and OIDC flows&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The original problem that OAuth 2.0 (RFC6749) addresses is delegated authorization. In all OAuth flows, the authorization server issues an Access Token for the client to carry. The Access Token identifies the scope of resources that its carrier is authorized to access. However, third-party services do not always want to delegate authorization to the identity provider. They often just need identity information and want to perform authorization on their own. The OAuth 2.0 Access Token itself is all about permission and does not care about the identify of its carrier. It is not designed for authentication. OAuth 2.0 does not provide a standard way for Authorization Server to keep identity information of the principal. Many developers needs to address identity issue and they started to (mis)use the AccessToken to store identity information in custom fields, until OIDC came about.&lt;/p&gt;&#10;&lt;figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-3 is-layout-flex wp-block-gallery-is-layout-flex"&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="543" height="335" data-id="12936" src="https://static.digihunch.com/wp-content/uploads/2025/04/oauth-access-token.webp" alt="" class="wp-image-12936" srcset="https://static.digihunch.com/wp-content/uploads/2025/04/oauth-access-token.webp 543w, https://static.digihunch.com/wp-content/uploads/2025/04/oauth-access-token-300x185.webp 300w" sizes="auto, (max-width: 543px) 100vw, 543px" /&gt;&lt;figcaption class="wp-element-caption"&gt;OAuth: Access Token only&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="686" height="516" data-id="12935" src="https://static.digihunch.com/wp-content/uploads/2025/04/oidc-identity-token.webp" alt="" class="wp-image-12935" srcset="https://static.digihunch.com/wp-content/uploads/2025/04/oidc-identity-token.webp 686w, https://static.digihunch.com/wp-content/uploads/2025/04/oidc-identity-token-300x226.webp 300w" sizes="auto, (max-width: 686px) 100vw, 686px" /&gt;&lt;figcaption class="wp-element-caption"&gt;OIDC: Access + Identity Token&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;OIDC is a thin layer (5%) on top of OAuth 2.0 and one important addition is the ID token. The resource server can, in addition to being asked to allow access, now can understand the identity of the principal requesting to access resources from the ID token. The OIDC layer also uses standard set of scopes and proposes a userinfo endpoint for client to get more details about user information. The authorization code flows in OIDC and OAuth2.0 are roughly the same except for the additions.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In terms of the flows supported, the OAuth flows are defined in &lt;a href="https://datatracker.ietf.org/doc/html/rfc6749"&gt;RFC6749&lt;/a&gt;, including the following grants:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Authorization Code grant&lt;/li&gt;&#10;&lt;li&gt;Implicit grant&lt;/li&gt;&#10;&lt;li&gt;Resource Owner Password Credentials grant&lt;/li&gt;&#10;&lt;li&gt;Client Credentials grant&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The classic grant type is Authorization Code. After verifying with user, the Authorization Server fires a call-back to the client to pass the authorization code. The client, then takes the authorization code, along with its client ID and client secret, to fire a request to Authorization server in exchange for Access Token. The implicit grant skips the Authorization Code step and the client gets the Access Token in a one-stop shop via callback over front channel, which is less secure. The other two grants are less often used.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the OIDC side, the &lt;a href="https://openid.net/specs/openid-connect-core-1_0.html"&gt;specification document&lt;/a&gt; discusses three flows:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Authorization Code Flow (specification 3.1)&lt;/li&gt;&#10;&lt;li&gt;Implicit Flow (specification 3.2)&lt;/li&gt;&#10;&lt;li&gt;Hybrid Flow (specification 3.3)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The original OAuth2.0 flows should only be used in delegated authorization scenario. In most contexts, if we talk about ID token, and if our use case involves authentication, then we&amp;#8217;re talking about OIDC not just OAuth. Since Authorization Code Flow is the classic one, out of all these flows, in the next section we take a closer look at the Authorization Code Flow in OIDC.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Authorization Code Flow in OIDC&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We consider the Authorization Code Flow the baseline flow and others as variations of it owing to architectural limitations. When we mention OIDC we implicitly refers to the Authorization Code Flow unless the context suggests otherwise. Now let&amp;#8217;s zoom in on it:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="631px" viewBox="-0.5 -0.5 631 581" style="max-width:100%;max-height:581px;"&gt;&lt;defs&gt;&lt;style type="text/css"&gt;@import url(https://fonts.googleapis.com/css?family=Architects+Daughter);&amp;#xa;@import url(https://fonts.googleapis.com/css?family=Architects+Daughter);&amp;#xa;&lt;/style&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="0" width="630" height="580" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 628px; height: 1px; padding-top: 7px; margin-left: 1px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;"&gt;OIDC Authorization Code Flow&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="315" y="19" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle" font-weight="bold"&gt;OIDC Authorization Code Flow&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="20" y="78.14" width="100" height="40" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 98px; margin-left: 21px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Resource Owner&lt;br /&gt;(User)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="70" y="102" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Resource Owner&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="180" y="78.14" width="100" height="40" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 98px; margin-left: 181px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Client App&lt;br /&gt;(front &amp;amp; backend)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="230" y="102" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Client App&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="340" y="78.14" width="100" height="40" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 98px; margin-left: 341px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Authorization Server&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="390" y="102" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Authorization Se&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 70 570 L 70 118.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/&gt;&lt;path d="M 230 570 L 230 118.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/&gt;&lt;path d="M 390 570 L 390 118.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/&gt;&lt;rect x="500" y="78.14" width="100" height="40" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 98px; margin-left: 501px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Resource Server&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="550" y="102" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Resource Server&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 550 570 L 550 118.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/&gt;&lt;path d="M 70 158.14 L 223.63 158.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 228.88 158.14 L 221.88 161.64 L 223.63 158.14 L 221.88 154.64 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 149px; margin-left: 151px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;1. launch client app&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="151" y="152" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;1. launch client app&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 230 178.14 L 383.63 178.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 388.88 178.14 L 381.88 181.64 L 383.63 178.14 L 381.88 174.64 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 171px; margin-left: 311px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;2. Token Request to /authorize&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="311" y="174" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;2. Token Request to /authorize&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 390 218.14 L 76.37 218.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 71.12 218.14 L 78.12 214.64 L 76.37 218.14 L 78.12 221.64 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 208px; margin-left: 180px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;3. 302 redirect to prompt user to log in&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="180" y="211" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;3. 302 redirect to prompt user to log in&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 70 248.14 L 383.63 248.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 388.88 248.14 L 381.88 251.64 L 383.63 248.14 L 381.88 244.64 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 237px; margin-left: 150px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;4. Authenticate and consent&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="150" y="240" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;4. Authenticate and consent&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 390 310 L 236.37 310" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 231.12 310 L 238.12 306.5 L 236.37 310 L 238.12 313.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 290px; margin-left: 261px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;6. Redirect browser to the callback URI&lt;br /&gt;, with Authorization Code as a parameter &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="261" y="294" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;6. Redirect browser to the callback URI&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 230 390 L 383.63 390" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 388.88 390 L 381.88 393.5 L 383.63 390 L 381.88 386.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 362px; margin-left: 311px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;7. Request (HTTP) for Tokens&lt;br /&gt;at /token, using Authorization &lt;br /&gt;Code, ClientID and Client Secret&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="311" y="366" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;7. Request (HTTP) for Tokens&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 390 410 L 420 410 L 420 450 L 396.37 450" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 391.12 450 L 398.12 446.5 L 396.37 450 L 398.12 453.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 403px; margin-left: 420px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;8.Validation&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="420" y="406" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;8.Validation&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 390 468.14 L 236.37 468.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 231.12 468.14 L 238.12 464.64 L 236.37 468.14 L 238.12 471.64 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 451px; margin-left: 311px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;9. Response (HTTP) with &lt;br /&gt;IDToken and AccessToken&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="311" y="454" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;9. Response (HTTP) with&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 207.5 30 L 252.5 30 L 252.5 75 L 207.5 75 Z" fill="#ffffff" stroke="none" pointer-events="all"/&gt;&lt;path d="M 212 55.85 L 219.22 55.85 L 219.22 54.21 L 212 54.21 Z M 212 50.79 L 220.66 50.79 L 220.66 49.16 L 212 49.16 Z M 212 45.74 L 222.11 45.74 L 222.11 44.1 L 212 44.1 Z M 238.79 54.21 L 227.48 54.21 L 228.52 50.07 L 239.82 50.07 Z M 227.24 48.43 L 225.39 55.85 L 240.07 55.85 L 241.92 48.43 Z M 241.71 63.33 C 241.68 63.48 241.54 63.59 241.39 63.59 L 221.35 63.59 C 221.22 63.59 221.14 63.52 221.1 63.47 C 221.06 63.42 221.01 63.33 221.03 63.2 L 224.75 45.7 L 245.45 45.7 Z M 225.92 41.41 L 245.96 41.41 C 246.09 41.41 246.17 41.48 246.21 41.53 C 246.25 41.58 246.3 41.67 246.27 41.8 L 245.8 44.06 L 225.09 44.06 L 225.6 41.67 C 225.63 41.52 225.77 41.41 225.92 41.41 Z M 247.48 40.5 C 247.11 40.04 246.55 39.77 245.96 39.77 L 225.92 39.77 C 225 39.77 224.19 40.43 224 41.33 L 219.43 62.86 C 219.31 63.44 219.45 64.04 219.83 64.5 C 220.2 64.96 220.76 65.23 221.35 65.23 L 241.39 65.23 C 242.31 65.23 243.12 64.57 243.31 63.67 L 247.88 42.14 C 248 41.56 247.86 40.96 247.48 40.5 Z" fill="#232f3e" stroke="none" pointer-events="all"/&gt;&lt;path d="M 60 67 C 60 55 60 49 70 49 C 63.33 49 63.33 37 70 37 C 76.67 37 76.67 49 70 49 C 80 49 80 55 80 67 Z" fill="#f5f5f5" stroke="#666666" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;path d="M 230 518.14 L 543.63 518.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 548.88 518.14 L 541.88 521.64 L 543.63 518.14 L 541.88 514.64 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 509px; margin-left: 391px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;10. Issue API Request with Tokens&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="391" y="512" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;10. Issue API Request with Tokens&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 550 548.14 L 236.37 548.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 231.12 548.14 L 238.12 544.64 L 236.37 548.14 L 238.12 551.64 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 539px; margin-left: 348px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;12. Receive API Response&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="348" y="542" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;12. Receive API Response&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 550 530 L 580 530 L 580 540 L 556.37 540" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 551.12 540 L 558.12 536.5 L 556.37 540 L 558.12 543.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 521px; margin-left: 590px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;11. Validation&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="590" y="524" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;11. Validation&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 105 435 C 105 429.48 109.48 425 115 425 L 200 425 C 205.52 425 210 420.52 210 415 C 210 420.52 214.48 425 220 425 L 305 425 C 310.52 425 315 429.48 315 435" fill="none" stroke="#999999" stroke-miterlimit="10" transform="rotate(270,210,425)" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-end; width: 1px; height: 1px; padding-top: 425px; margin-left: 198px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;"&gt;backend&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="198" y="429" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;back&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="470" y="202.5" width="60" height="30" fill="none" stroke="none" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 218px; margin-left: 471px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Authorization&lt;br /&gt;Endpoint&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="500" y="221" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Authorizat&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 414.75 240 C 414.75 234.48 419.23 230 424.75 230 L 451 230 C 456.52 230 461 225.52 461 220 C 461 225.52 465.48 230 471 230 L 497.25 230 C 502.77 230 507.25 234.48 507.25 240" fill="none" stroke="#999999" stroke-miterlimit="10" transform="rotate(90,461,230)" pointer-events="all"/&gt;&lt;rect x="461.25" y="370" width="60" height="30" fill="none" stroke="none" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 385px; margin-left: 462px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Token&lt;br /&gt;Endpoint&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="491" y="389" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Token&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 411 415 C 411 409.48 415.48 405 421 405 L 451 405 C 456.52 405 461 400.52 461 395 C 461 400.52 465.48 405 471 405 L 501 405 C 506.52 405 511 409.48 511 415" fill="none" stroke="#999999" stroke-miterlimit="10" transform="rotate(90,461,405)" pointer-events="all"/&gt;&lt;path d="M 536.89 70 C 535.63 70 535 69 535 68.21 L 535 37.58 C 535 36.62 535.74 35.79 536.76 35.79 L 558.22 35.79 C 558.93 35.79 560 36.37 560 37.63 L 560 68.13 C 560 69.2 559.19 70 558.19 70 Z" fill="#7d7c7c" stroke="none" pointer-events="all"/&gt;&lt;path d="M 560 66.66 L 560 68.13 C 560 69.2 559.19 70 558.19 70 L 536.89 70 C 535.63 70 535 69 535 68.21 L 535 66.72 C 535 67.61 535.72 68.47 536.8 68.47 L 558.13 68.47 C 559.07 68.47 560 67.75 560 66.66 Z" fill-opacity="0.3" fill="#000000" stroke="none" pointer-events="all"/&gt;&lt;rect x="535" y="35.79" width="0" height="0" fill="none" stroke="rgb(0, 0, 0)" pointer-events="all"/&gt;&lt;path d="M 556.07 42.6 L 538.87 42.6 L 538.87 40.59 L 556.07 40.59 Z M 556.07 46.11 L 538.87 46.11 L 538.87 44.09 L 556.07 44.09 Z M 556.07 49.6 L 538.87 49.6 L 538.87 47.6 L 556.07 47.6 Z" fill="#ffffff" stroke="none" pointer-events="all"/&gt;&lt;rect x="369" y="31.89" width="42" height="42" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 385.81 63.07 L 385.69 60.88 C 385.69 59.99 384.95 59.35 384.22 59.13 C 383.91 59.13 383.61 59.01 383.39 58.81 C 383.27 58.81 383.27 58.68 383.18 58.68 C 383.09 58.68 383.09 58.58 382.96 58.58 C 383.48 58.24 383.91 57.48 383.91 56.71 C 384 56.39 384 55.73 384 55.41 C 383.91 55.09 383.82 55.19 383.82 55.09 C 383.61 55.09 383.48 54.86 383.27 54.86 C 382.96 54.65 382.54 54.74 382.23 54.86 C 382.14 54.86 381.93 54.86 381.8 54.86 C 381.71 54.86 381.59 54.86 381.5 54.86 C 381.4 54.86 381.07 55.09 380.98 55.41 C 380.86 55.85 381.07 56.71 381.07 56.84 C 381.07 57.6 381.4 58.24 381.93 58.58 C 381.93 58.58 381.8 58.58 381.8 58.68 C 381.71 58.68 381.59 58.68 381.59 58.81 C 381.4 59.01 381.07 59.13 380.77 59.13 C 380.03 59.45 379.39 59.99 379.3 60.88 C 379.3 60.88 379.3 60.88 379.08 63.07 C 379.08 63.07 380.55 63.39 382.54 63.39 C 384.34 63.39 385.81 63.07 385.81 63.07 Z M 400.93 63.07 L 400.81 60.88 C 400.81 59.99 400.29 59.35 399.68 59.13 C 399.46 59.13 399.35 59.01 399.16 58.81 C 399.04 58.81 399.04 58.68 398.94 58.68 C 398.94 58.68 398.94 58.58 398.94 58.58 C 399.25 58.24 399.56 57.48 399.56 56.71 C 399.56 56.39 399.56 55.73 399.56 55.41 C 399.56 55.09 399.46 55.19 399.46 55.09 C 399.35 55.09 399.25 54.86 399.04 54.86 C 398.82 54.65 398.39 54.74 398.3 54.86 C 398.09 54.86 398.09 54.86 397.88 54.86 C 397.88 54.86 397.78 54.86 397.78 54.86 C 397.57 54.86 397.47 55.09 397.36 55.41 C 397.26 55.85 397.36 56.71 397.36 56.84 C 397.47 57.6 397.66 58.24 398 58.58 C 398 58.58 398 58.58 397.88 58.68 C 397.88 58.68 397.88 58.68 397.78 58.81 C 397.57 59.01 397.47 59.13 397.26 59.13 C 396.62 59.45 396.1 59.99 396.1 60.88 C 396.1 60.88 396.1 60.88 395.89 63.07 C 395.89 63.07 397.05 63.39 398.39 63.39 C 399.89 63.39 400.93 63.07 400.93 63.07 Z M 396.63 40.09 C 394.95 38.34 392.63 37.26 390.12 37.26 C 384.95 37.13 380.77 41.62 380.77 47.09 C 380.98 49.72 381.93 52.23 383.61 53.88 C 384.86 55.29 386.32 56.18 388 56.62 C 388.13 67.11 388.13 67.11 388.13 67.11 C 388 67.97 388.74 68.64 389.6 68.64 C 390.64 68.64 390.64 68.64 390.64 68.64 C 391.58 68.64 392.22 67.97 392.22 67.11 C 392.22 56.62 392.22 56.62 392.22 56.62 C 396.2 55.51 399.26 51.69 399.26 46.99 C 399.26 44.35 398.31 41.84 396.63 40.09 Z M 389.99 54.53 C 386.12 54.53 382.96 51.15 382.96 46.99 C 382.96 42.95 386.12 39.55 389.99 39.55 C 393.9 39.55 397.05 42.95 397.05 46.99 C 397.05 51.15 393.9 54.53 389.99 54.53 Z M 389.69 40.97 C 389.38 41.07 389.17 40.97 388.96 40.97 C 388.86 40.97 388.65 40.97 388.52 40.97 C 388.31 41.07 387.91 41.41 387.8 42.05 C 387.58 42.72 387.8 44.37 387.8 44.59 C 387.91 45.99 388.43 47.2 389.07 47.64 C 389.07 47.74 389.07 47.86 388.96 47.86 C 388.86 48.09 388.74 48.09 388.65 48.18 C 388.31 48.51 387.91 48.72 387.49 48.85 C 386.84 49.07 386.23 49.49 385.81 50.16 C 386.75 51.81 388.31 52.89 390.12 52.89 C 391.8 52.89 393.26 51.9 394.21 50.25 C 393.9 49.62 393.17 49.07 392.41 48.85 C 392.1 48.72 391.67 48.63 391.37 48.31 C 391.27 48.18 391.15 48.09 390.94 47.96 C 390.85 47.86 390.85 47.74 390.73 47.74 C 391.58 47.2 392.1 45.9 392.22 44.24 C 392.22 43.92 392.22 42.62 392.22 41.95 C 392.1 41.51 392.01 41.51 391.89 41.51 C 391.67 41.41 391.49 40.97 391.15 40.87 C 390.64 40.65 389.99 40.87 389.69 40.97 Z M 369 52.9 C 369 41.3 378.44 31.89 390 31.89 C 401.67 31.89 411 41.3 411 52.9 C 411 64.6 401.67 73.89 390 73.89 C 378.44 73.89 369 64.6 369 52.9 Z" fill="#28a8e0" stroke="none" pointer-events="all"/&gt;&lt;ellipse cx="390" cy="52.89" rx="20.58" ry="20.58" fill="#ffffff" stroke="none" pointer-events="all"/&gt;&lt;rect x="369" y="31.89" width="42" height="42" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 385.81 63.07 L 385.69 60.88 C 385.69 59.99 384.95 59.35 384.22 59.13 C 383.91 59.13 383.61 59.01 383.39 58.81 C 383.27 58.81 383.27 58.68 383.18 58.68 C 383.09 58.68 383.09 58.58 382.96 58.58 C 383.48 58.24 383.91 57.48 383.91 56.71 C 384 56.39 384 55.73 384 55.41 C 383.91 55.09 383.82 55.19 383.82 55.09 C 383.61 55.09 383.48 54.86 383.27 54.86 C 382.96 54.65 382.54 54.74 382.23 54.86 C 382.14 54.86 381.93 54.86 381.8 54.86 C 381.71 54.86 381.59 54.86 381.5 54.86 C 381.4 54.86 381.07 55.09 380.98 55.41 C 380.86 55.85 381.07 56.71 381.07 56.84 C 381.07 57.6 381.4 58.24 381.93 58.58 C 381.93 58.58 381.8 58.58 381.8 58.68 C 381.71 58.68 381.59 58.68 381.59 58.81 C 381.4 59.01 381.07 59.13 380.77 59.13 C 380.03 59.45 379.39 59.99 379.3 60.88 C 379.3 60.88 379.3 60.88 379.08 63.07 C 379.08 63.07 380.55 63.39 382.54 63.39 C 384.34 63.39 385.81 63.07 385.81 63.07 Z M 400.93 63.07 L 400.81 60.88 C 400.81 59.99 400.29 59.35 399.68 59.13 C 399.46 59.13 399.35 59.01 399.16 58.81 C 399.04 58.81 399.04 58.68 398.94 58.68 C 398.94 58.68 398.94 58.58 398.94 58.58 C 399.25 58.24 399.56 57.48 399.56 56.71 C 399.56 56.39 399.56 55.73 399.56 55.41 C 399.56 55.09 399.46 55.19 399.46 55.09 C 399.35 55.09 399.25 54.86 399.04 54.86 C 398.82 54.65 398.39 54.74 398.3 54.86 C 398.09 54.86 398.09 54.86 397.88 54.86 C 397.88 54.86 397.78 54.86 397.78 54.86 C 397.57 54.86 397.47 55.09 397.36 55.41 C 397.26 55.85 397.36 56.71 397.36 56.84 C 397.47 57.6 397.66 58.24 398 58.58 C 398 58.58 398 58.58 397.88 58.68 C 397.88 58.68 397.88 58.68 397.78 58.81 C 397.57 59.01 397.47 59.13 397.26 59.13 C 396.62 59.45 396.1 59.99 396.1 60.88 C 396.1 60.88 396.1 60.88 395.89 63.07 C 395.89 63.07 397.05 63.39 398.39 63.39 C 399.89 63.39 400.93 63.07 400.93 63.07 Z M 396.63 40.09 C 394.95 38.34 392.63 37.26 390.12 37.26 C 384.95 37.13 380.77 41.62 380.77 47.09 C 380.98 49.72 381.93 52.23 383.61 53.88 C 384.86 55.29 386.32 56.18 388 56.62 C 388.13 67.11 388.13 67.11 388.13 67.11 C 388 67.97 388.74 68.64 389.6 68.64 C 390.64 68.64 390.64 68.64 390.64 68.64 C 391.58 68.64 392.22 67.97 392.22 67.11 C 392.22 56.62 392.22 56.62 392.22 56.62 C 396.2 55.51 399.26 51.69 399.26 46.99 C 399.26 44.35 398.31 41.84 396.63 40.09 Z M 389.99 54.53 C 386.12 54.53 382.96 51.15 382.96 46.99 C 382.96 42.95 386.12 39.55 389.99 39.55 C 393.9 39.55 397.05 42.95 397.05 46.99 C 397.05 51.15 393.9 54.53 389.99 54.53 Z M 389.69 40.97 C 389.38 41.07 389.17 40.97 388.96 40.97 C 388.86 40.97 388.65 40.97 388.52 40.97 C 388.31 41.07 387.91 41.41 387.8 42.05 C 387.58 42.72 387.8 44.37 387.8 44.59 C 387.91 45.99 388.43 47.2 389.07 47.64 C 389.07 47.74 389.07 47.86 388.96 47.86 C 388.86 48.09 388.74 48.09 388.65 48.18 C 388.31 48.51 387.91 48.72 387.49 48.85 C 386.84 49.07 386.23 49.49 385.81 50.16 C 386.75 51.81 388.31 52.89 390.12 52.89 C 391.8 52.89 393.26 51.9 394.21 50.25 C 393.9 49.62 393.17 49.07 392.41 48.85 C 392.1 48.72 391.67 48.63 391.37 48.31 C 391.27 48.18 391.15 48.09 390.94 47.96 C 390.85 47.86 390.85 47.74 390.73 47.74 C 391.58 47.2 392.1 45.9 392.22 44.24 C 392.22 43.92 392.22 42.62 392.22 41.95 C 392.1 41.51 392.01 41.51 391.89 41.51 C 391.67 41.41 391.49 40.97 391.15 40.87 C 390.64 40.65 389.99 40.87 389.69 40.97 Z M 369 52.9 C 369 41.3 378.44 31.89 390 31.89 C 401.67 31.89 411 41.3 411 52.9 C 411 64.6 401.67 73.89 390 73.89 C 378.44 73.89 369 64.6 369 52.9 Z" fill="#28a8e0" stroke="none" pointer-events="all"/&gt;&lt;path d="M 390 260 L 420 260 L 420 300 L 396.37 300" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 391.12 300 L 398.12 296.5 L 396.37 300 L 398.12 303.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 253px; margin-left: 420px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;5.Validation&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="420" y="256" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;5.Validation&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank" rel="noopener"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Text is not SVG &amp;#8211; cannot display&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here is the narrative from my own words:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;The user launches client application, which detects that user has not logged in, and redirect to log in page.&lt;/li&gt;&#10;&lt;li&gt;The client app sends an HTTP request for authorization code to the /authorize endpoint of Authorization server. This request consists of the following fields:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Response type: code, indicating it is requesting authorization code&lt;/li&gt;&#10;&lt;li&gt;Scope: openid, standard for oidc&lt;/li&gt;&#10;&lt;li&gt;RedirectURI: my.com/oidc-callback, Authorization Server will use this to call back with code.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;The authorization server redirects the user to a prompt for log-in&lt;/li&gt;&#10;&lt;li&gt;The user completes authentication and consent&lt;/li&gt;&#10;&lt;li&gt;The Authorization server validates user information within its identity provider&lt;/li&gt;&#10;&lt;li&gt;The Authorization server fires an HTTP request call-back at the Redirect URI (on the backend), with Authorization Code. &lt;/li&gt;&#10;&lt;li&gt;The client app issues an HTTP request for ID Token and Access Token to the /token endpoint of Authorization server. This request consists of:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Authorization Code (received from previous step)&lt;/li&gt;&#10;&lt;li&gt;Client ID&lt;/li&gt;&#10;&lt;li&gt;Client Secret&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;The Authorization server validates the information and process the request, and prepare the response with the following fields:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;ID Token: identifies the resource owner.&lt;/li&gt;&#10;&lt;li&gt;Access Token: identifies what the client app can access&lt;/li&gt;&#10;&lt;li&gt;Expiration&lt;/li&gt;&#10;&lt;li&gt;(Optional) Refresh Token&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;The client app receives the tokens above in the HTTP response from the /token endpoint&lt;/li&gt;&#10;&lt;li&gt;With the Tokens, the client app issues API requests to the resource server&lt;/li&gt;&#10;&lt;li&gt;The resource server independently validates the token&lt;/li&gt;&#10;&lt;li&gt;The resource server send API response back the the client app.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are also a few points of configurations. First, the Resource Server needs to trust the Authorization Server. The Authorization Server uses its private key to sign the JWT tokens and the Resource Server needs the public key to validates it. Second, the authorization server needs to know about the client app. We usually configure the Authorization Server upfront, to generate the client ID and secret. The Client app will keep them as part of its configuration. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It is important to note that, when I use the term client app (OIDC calls it client), the word &amp;#8220;client&amp;#8221; is relative to the Authorization server. The client app itself consists of both frontend (browser) and backend (aka relying party). In this flow, the authorization code is not exposed to browser.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Other OIDC Flows&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now we can discuss some flows for other architectures.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Authorization Code flow has a close variation with the use of PKCE (Proof Key for Code Exchange). For native apps, &lt;a href="https://blog.postman.com/pkce-oauth-how-to/"&gt;postman&lt;/a&gt; and Okta recommend Authorization Code flow with PKCE. When client app first requests for authorization code, it also includes a challenge. After the callback, when it sends the authorization code back to authorization server in exchange for tokens, the request now adds a verifier. This way, even if the authorization code may not be securely saved, the authorization server can ensure it is the same client app that requests authorization code and that requests tokens.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;An SPA or JavaScript app does not have a way to a. store Authorization code and b. listen on a call-back URI. As a result, it makes sense for the SPA to just fetch the Tokens directly. This make the implicit flow. The spec doc refers to it as simplified authorization code flow. The grant type is &amp;#8220;implicit&amp;#8221; because there is no intermediate credentials issued. In this flow, the Authorization server does not authenticate its client. The tokens may be exposed to resource owner or other applications with access to resource owner&amp;#8217;s user-agent. This flow improve the responsiveness but we should be wary of the security implications.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="631px" viewBox="-0.5 -0.5 631 421" style="max-width:100%;max-height:421px;"&gt;&lt;defs&gt;&lt;style type="text/css"&gt;@import url(https://fonts.googleapis.com/css?family=Architects+Daughter);&amp;#xa;@import url(https://fonts.googleapis.com/css?family=Architects+Daughter);&amp;#xa;&lt;/style&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="0" width="630" height="420" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 628px; height: 1px; padding-top: 7px; margin-left: 1px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;"&gt;OIDC Implicit Flow&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="315" y="19" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle" font-weight="bold"&gt;OIDC Implicit Flow&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="20" y="78.14" width="100" height="40" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 98px; margin-left: 21px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Resource Owner&lt;br /&gt;(User)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="70" y="102" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Resource Owner&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="180" y="78.14" width="100" height="40" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 98px; margin-left: 181px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;SPA in Browser&lt;br /&gt;(frontend)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="230" y="102" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;SPA in Browser&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="340" y="78.14" width="100" height="40" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 98px; margin-left: 341px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Authorization Server&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="390" y="102" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Authorization Se&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 70 410 L 70 118.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/&gt;&lt;path d="M 230 410 L 230 118.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/&gt;&lt;path d="M 390 410 L 390 118.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/&gt;&lt;rect x="500" y="78.14" width="100" height="40" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 98px; margin-left: 501px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Resource Server&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="550" y="102" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Resource Server&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 550 410 L 550 118.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/&gt;&lt;path d="M 70 158.14 L 223.63 158.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 228.88 158.14 L 221.88 161.64 L 223.63 158.14 L 221.88 154.64 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 149px; margin-left: 151px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;1. launch SPA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="151" y="152" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;1. launch SPA&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 230 178.14 L 383.63 178.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 388.88 178.14 L 381.88 181.64 L 383.63 178.14 L 381.88 174.64 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 171px; margin-left: 312px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;2. Token request to /authorize&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="312" y="174" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;2. Token request to /authorize&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 390 218.14 L 76.37 218.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 71.12 218.14 L 78.12 214.64 L 76.37 218.14 L 78.12 221.64 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 211px; margin-left: 180px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;3. 302 redirect to prompt user to log in&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="180" y="214" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;3. 302 redirect to prompt user to log in&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 70 248.14 L 383.63 248.14" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 388.88 248.14 L 381.88 251.64 L 383.63 248.14 L 381.88 244.64 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 239px; margin-left: 160px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;4. Authenticate and consent&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="160" y="242" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;4. Authenticate and consent&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 391.25 260 L 421.25 260 L 421.25 300 L 397.62 300" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 392.37 300 L 399.37 296.5 L 397.62 300 L 399.37 303.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 281px; margin-left: 462px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;5.Validation&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="462" y="284" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;5.Validation&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 390 320 L 236.37 320" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 231.12 320 L 238.12 316.5 L 236.37 320 L 238.12 323.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 303px; margin-left: 311px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;6. Response (HTTP) with &lt;br /&gt;IDToken and AccessToken&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="311" y="306" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;6. Response (HTTP) with&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 207.5 30 L 252.5 30 L 252.5 75 L 207.5 75 Z" fill="#ffffff" stroke="none" pointer-events="all"/&gt;&lt;path d="M 212 55.85 L 219.22 55.85 L 219.22 54.21 L 212 54.21 Z M 212 50.79 L 220.66 50.79 L 220.66 49.16 L 212 49.16 Z M 212 45.74 L 222.11 45.74 L 222.11 44.1 L 212 44.1 Z M 238.79 54.21 L 227.48 54.21 L 228.52 50.07 L 239.82 50.07 Z M 227.24 48.43 L 225.39 55.85 L 240.07 55.85 L 241.92 48.43 Z M 241.71 63.33 C 241.68 63.48 241.54 63.59 241.39 63.59 L 221.35 63.59 C 221.22 63.59 221.14 63.52 221.1 63.47 C 221.06 63.42 221.01 63.33 221.03 63.2 L 224.75 45.7 L 245.45 45.7 Z M 225.92 41.41 L 245.96 41.41 C 246.09 41.41 246.17 41.48 246.21 41.53 C 246.25 41.58 246.3 41.67 246.27 41.8 L 245.8 44.06 L 225.09 44.06 L 225.6 41.67 C 225.63 41.52 225.77 41.41 225.92 41.41 Z M 247.48 40.5 C 247.11 40.04 246.55 39.77 245.96 39.77 L 225.92 39.77 C 225 39.77 224.19 40.43 224 41.33 L 219.43 62.86 C 219.31 63.44 219.45 64.04 219.83 64.5 C 220.2 64.96 220.76 65.23 221.35 65.23 L 241.39 65.23 C 242.31 65.23 243.12 64.57 243.31 63.67 L 247.88 42.14 C 248 41.56 247.86 40.96 247.48 40.5 Z" fill="#232f3e" stroke="none" pointer-events="all"/&gt;&lt;path d="M 60 67 C 60 55 60 49 70 49 C 63.33 49 63.33 37 70 37 C 76.67 37 76.67 49 70 49 C 80 49 80 55 80 67 Z" fill="#f5f5f5" stroke="#666666" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;path d="M 230 350 L 543.63 350" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 548.88 350 L 541.88 353.5 L 543.63 350 L 541.88 346.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 341px; margin-left: 391px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;7. Issue API Request with Tokens&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="391" y="344" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;7. Issue API Request with Tokens&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 550 390 L 236.37 390" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 231.12 390 L 238.12 386.5 L 236.37 390 L 238.12 393.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 381px; margin-left: 349px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;11. Receive API Response&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="349" y="384" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;11. Receive API Response&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 550 360 L 580 360 L 580 380 L 556.37 380" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/&gt;&lt;path d="M 551.12 380 L 558.12 376.5 L 556.37 380 L 558.12 383.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 352px; margin-left: 591px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;8. Validation&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="591" y="355" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;8. Validation&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 536.89 70 C 535.63 70 535 69 535 68.21 L 535 37.58 C 535 36.62 535.74 35.79 536.76 35.79 L 558.22 35.79 C 558.93 35.79 560 36.37 560 37.63 L 560 68.13 C 560 69.2 559.19 70 558.19 70 Z" fill="#7d7c7c" stroke="none" pointer-events="all"/&gt;&lt;path d="M 560 66.66 L 560 68.13 C 560 69.2 559.19 70 558.19 70 L 536.89 70 C 535.63 70 535 69 535 68.21 L 535 66.72 C 535 67.61 535.72 68.47 536.8 68.47 L 558.13 68.47 C 559.07 68.47 560 67.75 560 66.66 Z" fill-opacity="0.3" fill="#000000" stroke="none" pointer-events="all"/&gt;&lt;rect x="535" y="35.79" width="0" height="0" fill="none" stroke="rgb(0, 0, 0)" pointer-events="all"/&gt;&lt;path d="M 556.07 42.6 L 538.87 42.6 L 538.87 40.59 L 556.07 40.59 Z M 556.07 46.11 L 538.87 46.11 L 538.87 44.09 L 556.07 44.09 Z M 556.07 49.6 L 538.87 49.6 L 538.87 47.6 L 556.07 47.6 Z" fill="#ffffff" stroke="none" pointer-events="all"/&gt;&lt;rect x="369" y="31.89" width="42" height="42" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 385.81 63.07 L 385.69 60.88 C 385.69 59.99 384.95 59.35 384.22 59.13 C 383.91 59.13 383.61 59.01 383.39 58.81 C 383.27 58.81 383.27 58.68 383.18 58.68 C 383.09 58.68 383.09 58.58 382.96 58.58 C 383.48 58.24 383.91 57.48 383.91 56.71 C 384 56.39 384 55.73 384 55.41 C 383.91 55.09 383.82 55.19 383.82 55.09 C 383.61 55.09 383.48 54.86 383.27 54.86 C 382.96 54.65 382.54 54.74 382.23 54.86 C 382.14 54.86 381.93 54.86 381.8 54.86 C 381.71 54.86 381.59 54.86 381.5 54.86 C 381.4 54.86 381.07 55.09 380.98 55.41 C 380.86 55.85 381.07 56.71 381.07 56.84 C 381.07 57.6 381.4 58.24 381.93 58.58 C 381.93 58.58 381.8 58.58 381.8 58.68 C 381.71 58.68 381.59 58.68 381.59 58.81 C 381.4 59.01 381.07 59.13 380.77 59.13 C 380.03 59.45 379.39 59.99 379.3 60.88 C 379.3 60.88 379.3 60.88 379.08 63.07 C 379.08 63.07 380.55 63.39 382.54 63.39 C 384.34 63.39 385.81 63.07 385.81 63.07 Z M 400.93 63.07 L 400.81 60.88 C 400.81 59.99 400.29 59.35 399.68 59.13 C 399.46 59.13 399.35 59.01 399.16 58.81 C 399.04 58.81 399.04 58.68 398.94 58.68 C 398.94 58.68 398.94 58.58 398.94 58.58 C 399.25 58.24 399.56 57.48 399.56 56.71 C 399.56 56.39 399.56 55.73 399.56 55.41 C 399.56 55.09 399.46 55.19 399.46 55.09 C 399.35 55.09 399.25 54.86 399.04 54.86 C 398.82 54.65 398.39 54.74 398.3 54.86 C 398.09 54.86 398.09 54.86 397.88 54.86 C 397.88 54.86 397.78 54.86 397.78 54.86 C 397.57 54.86 397.47 55.09 397.36 55.41 C 397.26 55.85 397.36 56.71 397.36 56.84 C 397.47 57.6 397.66 58.24 398 58.58 C 398 58.58 398 58.58 397.88 58.68 C 397.88 58.68 397.88 58.68 397.78 58.81 C 397.57 59.01 397.47 59.13 397.26 59.13 C 396.62 59.45 396.1 59.99 396.1 60.88 C 396.1 60.88 396.1 60.88 395.89 63.07 C 395.89 63.07 397.05 63.39 398.39 63.39 C 399.89 63.39 400.93 63.07 400.93 63.07 Z M 396.63 40.09 C 394.95 38.34 392.63 37.26 390.12 37.26 C 384.95 37.13 380.77 41.62 380.77 47.09 C 380.98 49.72 381.93 52.23 383.61 53.88 C 384.86 55.29 386.32 56.18 388 56.62 C 388.13 67.11 388.13 67.11 388.13 67.11 C 388 67.97 388.74 68.64 389.6 68.64 C 390.64 68.64 390.64 68.64 390.64 68.64 C 391.58 68.64 392.22 67.97 392.22 67.11 C 392.22 56.62 392.22 56.62 392.22 56.62 C 396.2 55.51 399.26 51.69 399.26 46.99 C 399.26 44.35 398.31 41.84 396.63 40.09 Z M 389.99 54.53 C 386.12 54.53 382.96 51.15 382.96 46.99 C 382.96 42.95 386.12 39.55 389.99 39.55 C 393.9 39.55 397.05 42.95 397.05 46.99 C 397.05 51.15 393.9 54.53 389.99 54.53 Z M 389.69 40.97 C 389.38 41.07 389.17 40.97 388.96 40.97 C 388.86 40.97 388.65 40.97 388.52 40.97 C 388.31 41.07 387.91 41.41 387.8 42.05 C 387.58 42.72 387.8 44.37 387.8 44.59 C 387.91 45.99 388.43 47.2 389.07 47.64 C 389.07 47.74 389.07 47.86 388.96 47.86 C 388.86 48.09 388.74 48.09 388.65 48.18 C 388.31 48.51 387.91 48.72 387.49 48.85 C 386.84 49.07 386.23 49.49 385.81 50.16 C 386.75 51.81 388.31 52.89 390.12 52.89 C 391.8 52.89 393.26 51.9 394.21 50.25 C 393.9 49.62 393.17 49.07 392.41 48.85 C 392.1 48.72 391.67 48.63 391.37 48.31 C 391.27 48.18 391.15 48.09 390.94 47.96 C 390.85 47.86 390.85 47.74 390.73 47.74 C 391.58 47.2 392.1 45.9 392.22 44.24 C 392.22 43.92 392.22 42.62 392.22 41.95 C 392.1 41.51 392.01 41.51 391.89 41.51 C 391.67 41.41 391.49 40.97 391.15 40.87 C 390.64 40.65 389.99 40.87 389.69 40.97 Z M 369 52.9 C 369 41.3 378.44 31.89 390 31.89 C 401.67 31.89 411 41.3 411 52.9 C 411 64.6 401.67 73.89 390 73.89 C 378.44 73.89 369 64.6 369 52.9 Z" fill="#28a8e0" stroke="none" pointer-events="all"/&gt;&lt;ellipse cx="390" cy="52.89" rx="20.58" ry="20.58" fill="#ffffff" stroke="none" pointer-events="all"/&gt;&lt;rect x="369" y="31.89" width="42" height="42" fill="none" stroke="none" pointer-events="all"/&gt;&lt;path d="M 385.81 63.07 L 385.69 60.88 C 385.69 59.99 384.95 59.35 384.22 59.13 C 383.91 59.13 383.61 59.01 383.39 58.81 C 383.27 58.81 383.27 58.68 383.18 58.68 C 383.09 58.68 383.09 58.58 382.96 58.58 C 383.48 58.24 383.91 57.48 383.91 56.71 C 384 56.39 384 55.73 384 55.41 C 383.91 55.09 383.82 55.19 383.82 55.09 C 383.61 55.09 383.48 54.86 383.27 54.86 C 382.96 54.65 382.54 54.74 382.23 54.86 C 382.14 54.86 381.93 54.86 381.8 54.86 C 381.71 54.86 381.59 54.86 381.5 54.86 C 381.4 54.86 381.07 55.09 380.98 55.41 C 380.86 55.85 381.07 56.71 381.07 56.84 C 381.07 57.6 381.4 58.24 381.93 58.58 C 381.93 58.58 381.8 58.58 381.8 58.68 C 381.71 58.68 381.59 58.68 381.59 58.81 C 381.4 59.01 381.07 59.13 380.77 59.13 C 380.03 59.45 379.39 59.99 379.3 60.88 C 379.3 60.88 379.3 60.88 379.08 63.07 C 379.08 63.07 380.55 63.39 382.54 63.39 C 384.34 63.39 385.81 63.07 385.81 63.07 Z M 400.93 63.07 L 400.81 60.88 C 400.81 59.99 400.29 59.35 399.68 59.13 C 399.46 59.13 399.35 59.01 399.16 58.81 C 399.04 58.81 399.04 58.68 398.94 58.68 C 398.94 58.68 398.94 58.58 398.94 58.58 C 399.25 58.24 399.56 57.48 399.56 56.71 C 399.56 56.39 399.56 55.73 399.56 55.41 C 399.56 55.09 399.46 55.19 399.46 55.09 C 399.35 55.09 399.25 54.86 399.04 54.86 C 398.82 54.65 398.39 54.74 398.3 54.86 C 398.09 54.86 398.09 54.86 397.88 54.86 C 397.88 54.86 397.78 54.86 397.78 54.86 C 397.57 54.86 397.47 55.09 397.36 55.41 C 397.26 55.85 397.36 56.71 397.36 56.84 C 397.47 57.6 397.66 58.24 398 58.58 C 398 58.58 398 58.58 397.88 58.68 C 397.88 58.68 397.88 58.68 397.78 58.81 C 397.57 59.01 397.47 59.13 397.26 59.13 C 396.62 59.45 396.1 59.99 396.1 60.88 C 396.1 60.88 396.1 60.88 395.89 63.07 C 395.89 63.07 397.05 63.39 398.39 63.39 C 399.89 63.39 400.93 63.07 400.93 63.07 Z M 396.63 40.09 C 394.95 38.34 392.63 37.26 390.12 37.26 C 384.95 37.13 380.77 41.62 380.77 47.09 C 380.98 49.72 381.93 52.23 383.61 53.88 C 384.86 55.29 386.32 56.18 388 56.62 C 388.13 67.11 388.13 67.11 388.13 67.11 C 388 67.97 388.74 68.64 389.6 68.64 C 390.64 68.64 390.64 68.64 390.64 68.64 C 391.58 68.64 392.22 67.97 392.22 67.11 C 392.22 56.62 392.22 56.62 392.22 56.62 C 396.2 55.51 399.26 51.69 399.26 46.99 C 399.26 44.35 398.31 41.84 396.63 40.09 Z M 389.99 54.53 C 386.12 54.53 382.96 51.15 382.96 46.99 C 382.96 42.95 386.12 39.55 389.99 39.55 C 393.9 39.55 397.05 42.95 397.05 46.99 C 397.05 51.15 393.9 54.53 389.99 54.53 Z M 389.69 40.97 C 389.38 41.07 389.17 40.97 388.96 40.97 C 388.86 40.97 388.65 40.97 388.52 40.97 C 388.31 41.07 387.91 41.41 387.8 42.05 C 387.58 42.72 387.8 44.37 387.8 44.59 C 387.91 45.99 388.43 47.2 389.07 47.64 C 389.07 47.74 389.07 47.86 388.96 47.86 C 388.86 48.09 388.74 48.09 388.65 48.18 C 388.31 48.51 387.91 48.72 387.49 48.85 C 386.84 49.07 386.23 49.49 385.81 50.16 C 386.75 51.81 388.31 52.89 390.12 52.89 C 391.8 52.89 393.26 51.9 394.21 50.25 C 393.9 49.62 393.17 49.07 392.41 48.85 C 392.1 48.72 391.67 48.63 391.37 48.31 C 391.27 48.18 391.15 48.09 390.94 47.96 C 390.85 47.86 390.85 47.74 390.73 47.74 C 391.58 47.2 392.1 45.9 392.22 44.24 C 392.22 43.92 392.22 42.62 392.22 41.95 C 392.1 41.51 392.01 41.51 391.89 41.51 C 391.67 41.41 391.49 40.97 391.15 40.87 C 390.64 40.65 389.99 40.87 389.69 40.97 Z M 369 52.9 C 369 41.3 378.44 31.89 390 31.89 C 401.67 31.89 411 41.3 411 52.9 C 411 64.6 401.67 73.89 390 73.89 C 378.44 73.89 369 64.6 369 52.9 Z" fill="#28a8e0" stroke="none" pointer-events="all"/&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank" rel="noopener"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Text is not SVG &amp;#8211; cannot display&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As stated above, for SPAs (or in-browser JavaScript), we have to choice but the Implicit Flow because the client app is front-end only and we do not consider it able to securely store credentials. Client apps that can securely store client credentials may benefit from &lt;a href="https://openid.net/specs/openid-connect-core-1_0.html#HybridFlowAuth"&gt;Hybrid Flow.&lt;/a&gt; In the hybrid flow, when the authorization server fires callback, the callback includes a single-use authorization code, along with ID token, access token, or both, depending on the provided response_type. Then the client app sends it back to authorization server, along with client credentials, in exchange for a second ID token and access token. The &lt;a href="https://openid.net/specs/openid-connect-core-1_0.html#HybridFlowSteps"&gt;specification&lt;/a&gt; has a good table that compares the three flows:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="255" src="https://static.digihunch.com/wp-content/uploads/2023/07/flow-table.webp" alt="" class="wp-image-12937" srcset="https://static.digihunch.com/wp-content/uploads/2023/07/flow-table.webp 1024w, https://static.digihunch.com/wp-content/uploads/2023/07/flow-table-300x75.webp 300w, https://static.digihunch.com/wp-content/uploads/2023/07/flow-table-768x191.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Apart from the three flows, there are also some &amp;#8220;unofficial&amp;#8221; OIDC flows, that are not discussed in the specification. For example, Auth0 adopted some original OAuth2.0 grants in conformance to OIDC, including &lt;a href="https://auth0.com/docs/authenticate/login/oidc-conformant-authentication/oidc-adoption-client-credentials-flow"&gt;Client Credentials Flow with OIDC&lt;/a&gt; and &lt;a href="https://auth0.com/docs/authenticate/login/oidc-conformant-authentication/oidc-adoption-rop-flow"&gt;Resource Owner Password Flow with OIDC&lt;/a&gt;. The client credentials flow is for machine-to-machine identity and is not concerned with user identity.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Identify the Flow&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A challenge that I faced is to make sense of the Authorization Code Flow in real life. I realized that the components (Client App, Authorization Server and Resource Server) in Authorization Code Flow are conceptual. In real life we do not always find a counterpart that perfectly match their features. When we try to introduce OIDC for authentication, we often need to build our own solution, with additional tools.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Take the client app for example. We would need one or several components in real life to perform the followings in order to qualify as a Client App in the sense of Authorization Code Flow, it needs to:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;know the authorization endpoint and construct the HTTP request for Authorization code;&lt;/li&gt;&#10;&lt;li&gt;stand up an HTTP service (relying party) to listen to call back, and parse the Authorization Code;&lt;/li&gt;&#10;&lt;li&gt;securely store Authorization code, and have access to client ID and client secret;&lt;/li&gt;&#10;&lt;li&gt;construct a request for tokens using client ID, secret and authorization Code received;&lt;/li&gt;&#10;&lt;li&gt;parse the tokens from the response from Token endpoint&lt;/li&gt;&#10;&lt;li&gt;to pass the tokens along&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When developers builds an application with OIDC integration capability, they&amp;#8217;d have to implement all these using the library of their programming language. In addition to application&amp;#8217;s own server, the OIDC module will need its own backend capable of doing all the activities above. The alternative option is to introduce a OIDC capable client proxy service.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As for the Resource Server, it needs to have a trust on the Authorization Server, so that it can cryptographically validate the tokens that the Authorization Server has issued using the well-known public key.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the Authorization Server side, as we discussed. It needs to provision client ID and client secret that itself can later recognize when client app connects to it. It also needs to have both authorization endpoint and token endpoint. Often times, the authorization server contains identity store and we&amp;#8217;d like to call it the identity provider, but that is not always the case. A company may have a home grown identity store that does not support OIDC. In that case, to qualify as an OIDC Authorization server, they need a server proxy.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Open ID Connect Specifications&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Despite the different implementation by different vendors, we often need to resort to the official standard documentation. &lt;a href="https://openid.net/developers/specs/"&gt;This &lt;/a&gt;Open ID connect page lists all the specification if you expand &amp;#8220;OpenID Connect specification&amp;#8221; under Final Specifications. The most commonly used ones are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;OpenID Connect Core specification [&lt;strong&gt;OpenID.Core.Errata2&lt;/strong&gt;], which covers the foundation and three login flows (Authorization Code, Implicit and Hybrid). This was developed early and the current version [OpenID.Core.Errata2] is from Dec 2023 but the two previous versions [OpenID.Core.Errata1] and [OpenID.Core.Final] had been around since 2014;&lt;/li&gt;&#10;&lt;li&gt;Open ID Connect Session Management [&lt;strong&gt;OpenID.Session&lt;/strong&gt;], another core document that stipulates how to manage sessions, finalized in Sept 2022;&lt;/li&gt;&#10;&lt;li&gt;Open ID Connect Discovery 1.0 [&lt;strong&gt;OpenID.Discovery&lt;/strong&gt;], which stipulates the hosting OIDC discovery document, finalized in Dec 2023;&lt;/li&gt;&#10;&lt;li&gt;Open ID RP-Initiated Logout [&lt;strong&gt;OpenID.RPInitiated&lt;/strong&gt;], one of the logout flow specification, drafted in 2020 and finalized in Sep 2022;&lt;/li&gt;&#10;&lt;li&gt;Open ID Front-Channel Logout [&lt;strong&gt;OpenID.FrontChannel&lt;/strong&gt;], one of the logout flow specification, drafted from March 2016 and finalized in Oct 2022;&lt;/li&gt;&#10;&lt;li&gt;Open ID Back-Channel Logout [&lt;strong&gt;OpenID.BackChannel&lt;/strong&gt;], one of the logout flow specification, drafted in 2016 and finalized in Sep 2022;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Do read the specification when you&amp;#8217;re configuring integration. It is worth noting that apart from the Core specification which has been finalized for a decade, most of the other specifications did not finalize until late 2022. Therefore, it is important for integrators to validate the compliance state of the components in the implementation.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;OIDC Proxy&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As a result, with regard to OIDC, there are two categories of proxies: OIDC client proxy and OIDC server proxy. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For example, in my previous &lt;a href="https://static.digihunch.com/2022/02/istio-external-authorization/"&gt;post&lt;/a&gt;, I explained how to configure external authorization via OIDC in Istio. Looking at the diagram, it uses OAuth2 proxy to integrate with GCP as the authorization server. In this use case, GCP is natively OIDC capable, the the Hello Word App isn&amp;#8217;t. Therefore, the OAuth2-proxy that we introduced is an OIDC client proxy. &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="584" height="373" src="https://static.digihunch.com/wp-content/uploads/2023/07/oauthproxy.png" alt="" class="wp-image-9277" srcset="https://static.digihunch.com/wp-content/uploads/2023/07/oauthproxy.png 584w, https://static.digihunch.com/wp-content/uploads/2023/07/oauthproxy-300x192.png 300w" sizes="auto, (max-width: 584px) 100vw, 584px" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For a corporate with Active Directory, the identity store only supports LDAP protocol. In order to quality the identity store as an OIDC Authorization Server, we would need a server-side proxy such as the LDAP &lt;a href="https://dexidp.io/docs/connectors/"&gt;connector in Dex&lt;/a&gt;, with the Active Directory as authentication source. The diagram of dex is a good summary of its role:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full is-resized"&gt;&lt;img loading="lazy" decoding="async" width="760" height="460" src="https://static.digihunch.com/wp-content/uploads/2023/07/dex.png" alt="" class="wp-image-9255" style="width:461px;height:279px" srcset="https://static.digihunch.com/wp-content/uploads/2023/07/dex.png 760w, https://static.digihunch.com/wp-content/uploads/2023/07/dex-300x182.png 300w" sizes="auto, (max-width: 760px) 100vw, 760px" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In some scenarios, we refer to this role as identity broker. Dex is an identity broker. Another important project to know is KeyCloak, which is sponsored by Red Hat and now a CNCF project. Although you can configure KeyCloak as an identity broker, it is much more than a broker. KeyCloadk is a full-fledged identity and access management solution on its own. It can act as the entire Authorization server. The diagram in &lt;a href="https://developers.redhat.com/blog/2019/12/11/keycloak-core-concepts-of-open-source-identity-and-access-management#"&gt;this&lt;/a&gt; blog post summarizes its features well. It is for teams that wants to build their home grown identity store. Think of KeyCloak as a self-managed open-source alternative to IAM solutions such as Okta or Auth0.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Amazon Cognito user pool plays a similar role. A user pool serves as an identity store to an app. The integration (no matter which flow and how Cognito calls them) is supposed to be &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cognito_user_pool_client#allowed_oauth_flows"&gt;OIDC&lt;/a&gt; compliant. However, as of date, the integration with Cognito user pool isn&amp;#8217;t. For example, the &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html"&gt;logout endpoint&lt;/a&gt; requires client_id parameter where as the &lt;a href="https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout"&gt;RP initiated logout specification&lt;/a&gt; has it optional. On the other hand, it can &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-identity-federation.html"&gt;federate&lt;/a&gt; its own identity pool with a third party via standard protocol including &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/federation-endpoints.html"&gt;OIDC&lt;/a&gt;.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="782" height="201" src="https://static.digihunch.com/wp-content/uploads/2023/07/cognito.png" alt="" class="wp-image-9550" srcset="https://static.digihunch.com/wp-content/uploads/2023/07/cognito.png 782w, https://static.digihunch.com/wp-content/uploads/2023/07/cognito-300x77.png 300w, https://static.digihunch.com/wp-content/uploads/2023/07/cognito-768x197.png 768w" sizes="auto, (max-width: 782px) 100vw, 782px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In any use case where we need to bring OIDC integration, we need to start with the flow recommendation for each architecture, then we examine the existing component against the flow diagram. From there, we can identify the missing pieces and determine where and how we should configure the proxy.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The OIDC topic confuses me big time every time I need to configure identity store. With this post, I was hoping to elaborate on the Authorization Code Flow for OIDC. See OpenID &lt;a href="https://openid.net/certification/"&gt;certification&lt;/a&gt; for a list of providers. Further I discussed the two categories of proxies in the OIDC picture. Hopefully, when the OIDC topic comes back again, I will be able to quickly match which is which, and identify the missing piece to build a solution.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2023/06/kubernetes-paas-and-red-hat-openshift/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kubernetes Platform as a Service and Red Hat OpenShift&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2023/07/authenticate-kube-apiserver-via-oidc/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Authentication to kube-apiserver via OIDC&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Istio External Authorization via OIDC</title><link>https://static.digihunch.com/2022/02/istio-external-authorization/</link><pubDate>Fri, 25 Feb 2022 01:29:00 -0400</pubDate><guid>https://static.digihunch.com/2022/02/istio-external-authorization/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-istio-external-auth.webp" alt="Featured image of post Istio External Authorization via OIDC" /&gt;&lt;p class="wp-block-paragraph" id="aa"&gt;Istio service mesh allows application developers to offload non-core features to infrastructure layer. We explored authentication and authorization with &lt;a href="https://static.digihunch.com/2022/02/authentication-and-authorization-with-istio/"&gt;Istio&lt;/a&gt; in a basic &lt;a href="https://static.digihunch.com/2022/02/istio-lab-authentication-and-authorization-in-jwt/"&gt;lab&lt;/a&gt;. In this post we continue to explore its capabilities with OIDC integration. This capability is made available thanks to the CUSTOM action in authorization policy, supported since the &lt;a href="https://istio.io/latest/blog/2021/better-external-authz/"&gt;release&lt;/a&gt; of 1.9. It enables any workload on Istio to integrate with an external IAM solution.&lt;/p&gt;&#10;&lt;p class="has-text-align-center wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="461px" viewBox="-0.5 -0.5 461 261" style="max-width:100%;max-height:261px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="0" width="460" height="260" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;rect x="160" y="10" width="290" height="230" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;rect x="240" y="50" width="90" height="40" rx="6" ry="6" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 70px; margin-left: 241px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Authorization&lt;br&gt;Policy&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="285" y="74" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Authorization&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;ellipse cx="45" cy="56.25" rx="7.5" ry="6.25" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/ellipse&gt;&lt;path d="M 45 62.5 L 45 83.33 M 45 66.67 L 30 66.67 M 45 66.67 L 60 66.67 M 45 83.33 L 30 100 M 45 83.33 L 60 100" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 107px; margin-left: 45px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;"&gt;User&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="45" y="119" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;User&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="150" y="50" width="60" height="40" rx="6" ry="6" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 70px; margin-left: 151px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Istio&lt;br&gt;Ingress&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="180" y="74" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Istio&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="240" y="116.15" width="90" height="40" rx="6" ry="6" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 136px; margin-left: 241px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;External &lt;br&gt;Authorization&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="285" y="140" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;External&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="240" y="190" width="90" height="40" rx="6" ry="6" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 210px; margin-left: 241px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;OAuth2-Proxy&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="285" y="214" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;OAuth2-Proxy&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="360" y="50" width="80" height="40" rx="6" ry="6" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 70px; margin-left: 361px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;App&lt;br&gt;HelloWorld&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="400" y="74" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;App&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 285 109.78 L 285 96.37" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 285 115.03 L 281.5 108.03 L 285 109.78 L 288.5 108.03 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 285 91.12 L 288.5 98.12 L 285 96.37 L 281.5 98.12 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 285 183.63 L 285 162.52" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 285 188.88 L 281.5 181.88 L 285 183.63 L 288.5 181.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 285 157.27 L 288.5 164.27 L 285 162.52 L 281.5 164.27 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 336.37 70 L 353.63 70" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 331.12 70 L 338.12 66.5 L 336.37 70 L 338.12 73.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 358.88 70 L 351.88 73.5 L 353.63 70 L 351.88 66.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 216.37 70 L 233.63 70" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 211.12 70 L 218.12 66.5 L 216.37 70 L 218.12 73.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 238.88 70 L 231.88 73.5 L 233.63 70 L 231.88 66.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 60 70 L 143.63 70" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 148.88 70 L 141.88 73.5 L 143.63 70 L 141.88 66.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;image x="389.5" y="181.5" width="50" height="48" xlink:href="https://app.diagrams.net/img/lib/mscae/Kubernetes.svg"&gt;&lt;/image&gt;&lt;rect x="10" y="190" width="90" height="40" rx="1" ry="1" fill="#000000" stroke="#000000" pointer-events="all" transform="translate(2,3)" opacity="0.25"&gt;&lt;/rect&gt;&lt;rect x="10" y="190" width="90" height="40" rx="1" ry="1" fill="rgb(255, 255, 255)" stroke="#dddddd" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 31.19 223.26 C 30.2 223.26 29.26 222.72 28.77 221.7 L 23 211.3 C 22.48 210.39 22.56 209.33 23 208.58 L 28.82 198.17 C 29.31 197.23 30.2 196.74 31.09 196.74 L 42.78 196.74 C 43.65 196.74 44.51 197.18 45.02 198.07 L 50.82 208.46 C 51.52 209.56 51.28 210.73 50.88 211.38 L 45.12 221.72 C 44.73 222.55 43.86 223.26 42.74 223.26 Z" fill="#5184f3" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 41.44 223.26 L 33.89 215.48 L 32.68 211.94 L 34.3 205.58 L 42.6 205.49 L 49.93 213.1 L 45.12 221.72 C 44.73 222.55 43.86 223.26 42.74 223.26 Z" fill-opacity="0.07" fill="#000000" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="22.48" y="196.74" width="0" height="0" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 36.95 209.34 C 38.04 209.34 38.71 208.38 38.71 207.64 C 38.71 206.52 37.86 205.74 36.98 205.74 C 36.06 205.74 35.16 206.56 35.16 207.52 C 35.16 208.64 36.17 209.34 36.95 209.34 Z M 36.98 216.03 C 38.51 215.44 39.9 214.32 40.31 213.22 L 40.31 212.32 C 40.31 211.22 38.41 210.25 36.97 210.25 C 35.72 210.25 33.63 211.16 33.63 212.28 L 33.63 213.43 C 34.4 214.59 35.59 215.54 36.98 216.03 Z M 36.95 217.2 C 35.55 216.86 34.19 215.98 33.25 214.79 C 32.09 213.42 31.32 211.66 31.32 209.87 L 31.32 205.51 L 36.97 202.92 L 42.6 205.49 L 42.61 209.31 C 42.61 211.3 42.02 213.07 40.9 214.43 C 40.05 215.67 38.84 216.74 36.95 217.2 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 210px; margin-left: 66px;"&gt;&lt;div data-drawio-colors="color: #999999; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(153, 153, 153); line-height: 1.2; pointer-events: all; white-space: nowrap;"&gt;GCP&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="66" y="214" fill="#999999" font-family="Helvetica" font-size="12px"&gt;GCP&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 106.37 210 L 233.63 210" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 101.12 210 L 108.12 206.5 L 106.37 210 L 108.12 213.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 238.88 210 L 231.88 213.5 L 233.63 210 L 231.88 206.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;image x="164.5" y="109.5" width="30" height="46.15" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnY9Imh0dHBzOi8vdmVjdGEuaW8vbmFubyIgd2lkdGg9IjE2MCIgaGVpZ2h0PSIyNDAiIHZpZXdCb3g9IjAgMCAxNjAgMjQwIj4mI3hhOwk8c3R5bGUgdHlwZT0idGV4dC9jc3MiPiYjeGE7CS5zdDB7ZmlsbDojNDY2YmIwO30mI3hhOwk8L3N0eWxlPiYjeGE7CTxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik02MCAyNDBMMCAyMTBoMTYwem0wLTE2MEwwIDIwMGw2MC0xMHpNNzAgMHYxOTBsOTAgMTB6Ii8+JiN4YTs8L3N2Zz4=" preserveAspectRatio="none"&gt;&lt;/image&gt;&lt;rect x="160" y="10" width="140" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 25px; margin-left: 161px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;demo1.digihunch.com&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="230" y="29" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;demo1.digihunch.com&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank" rel="noopener"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Text is not SVG &amp;#8211; cannot display&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph" id="aa"&gt;The rest of this post, provides the step-by-step instruction to configure OIDC integration, based on Istio&amp;#8217;s &lt;a href="https://istio.io/latest/docs/tasks/security/authorization/authz-custom/"&gt;External Authorization&lt;/a&gt; use case. My work is influenced by two blog posts from &lt;a href="https://www.jetstack.io/blog/istio-oidc/"&gt;jetstack&lt;/a&gt; and &lt;a href="https://elastisys.com/istio-and-oauth2-proxy-in-kubernetes-for-microservice-authentication/"&gt;elastisys&lt;/a&gt; on similar topic, with my own additions, simplifications and clarifications. In this lab I use my own DNS hostname &lt;span style="text-decoration: underline" class="underline"&gt;demo1.digihunch.com&lt;/span&gt; and to follow along you need to bring your own hostname as well. The prerequisites of this lab is summarized as below:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;An &lt;strong&gt;identity provider&lt;/strong&gt;: we use Google in the lab but it can be anything with OIDC capabilities (e.g. Azure AD, Facebook). Read my &lt;a href="https://static.digihunch.com/2020/03/oauth-and-openid-connect/"&gt;old post&lt;/a&gt; for more details on OIDC.&lt;/li&gt;&#10;&lt;li&gt;A &lt;strong&gt;public IP address&lt;/strong&gt; for Cluster&amp;#8217;s Ingress service: Since we use Google as identity provider on the Internet, we need our service to be exposed on the Internet because the OIDC integration involves a two-way HTTP redirect. &lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Public DNS resolution&lt;/strong&gt; to the service&amp;#8217;s public IP: during authentication, the identity provider will call back with JWT. So we need our hostname demo1.digihunch.com resolved to the service&amp;#8217;s public IP address. This is also required in other scenarios. For example, cert-manager needs to automatically configure X.509 certificate using Let&amp;#8217;s Encrypt.&lt;/li&gt;&#10;&lt;li&gt;An &lt;strong&gt;application workload&lt;/strong&gt;: this is the tenant workload without its own IAM capability. We use a dummy workload base on a &lt;a href="https://hub.docker.com/r/neilpeterson/aks-helloworld"&gt;hello-world&lt;/a&gt; image, for simplicity.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The files required in this lab is in &lt;a href="https://github.com/digihunch/istio-oidc"&gt;istio-oidc&lt;/a&gt; repo under the &lt;a href="https://github.com/digihunch/istio-oidc/tree/main/envoy"&gt;envoy&lt;/a&gt; directory. It is also important to understand the Authentication Code flow in OIDC authorization code flow to make sense of the integration between OIDC provider and Istio on our platform. For example, the client application needs a one-time registration with identity provider. Let&amp;#8217;s start with this configuration.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="configure-identity-provider"&gt;Configure Identity Provider&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We need a Google account with a GCP project and log on to &lt;a href="https://console.cloud.google.com/apis/"&gt;APIs &amp;amp; Services&lt;/a&gt; to register our application. At the console, from the left side bar, click on &amp;#8220;OAuth consent screen&amp;#8221; and create an App. Provide the followings:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Step 1. OAuth consent screen. Under App Information, provide App name and User support email.&lt;/li&gt;&#10;&lt;li&gt;Step 1. OAuth consent screen. Under App domain, provide Application home page&lt;/li&gt;&#10;&lt;li&gt;Step 1. OAuth consent screen. Under Authorized domains, provide an Authorized domain.&lt;/li&gt;&#10;&lt;li&gt;Step 3. Test users. Add the emails for Test users.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The summary page looks like this:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="1012" height="1332" src="https://static.digihunch.com/wp-content/uploads/2022/02/summary.png" alt="" class="wp-image-3607"/&gt;&lt;figcaption class="wp-element-caption"&gt;OAuth Consent Screen&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Then on the sidebar, click on Credentials. We create new credentials for OAuth 2.0 Client ID. Select Web application and add &amp;#8220;https://demo1.digihunch.com/oauth2/callback&amp;#8221; as an Authorized redirect URI. The credential created includes a Client ID and a Client secret, which can be downloaded as a JSON file. Here is my screen:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="1332" height="818" src="https://static.digihunch.com/wp-content/uploads/2022/02/credentials.png" alt="" class="wp-image-3608"/&gt;&lt;figcaption class="wp-element-caption"&gt;API Credential&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that the redirect URIs cannot be localhost. Because the browser will consume the redirect URI and it has no clue where localhost is. It must resolve to the public IP of the ingress.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We need to keep the Client ID and Client secret for later use. According to Google&amp;#8217;s &lt;a href="https://developers.google.com/identity/protocols/oauth2/openid-connect"&gt;documentation&lt;/a&gt;, there is a &lt;a href="https://developers.google.com/identity/protocols/oauth2/openid-connect#discovery"&gt;discovery document&lt;/a&gt; from which we can understand the keys in their &lt;a href="https://accounts.google.com/.well-known/openid-configuration"&gt;Open ID configuration&lt;/a&gt;. We need the value of two keys (&lt;em&gt;issuer&lt;/em&gt; and &lt;em&gt;jwks_url&lt;/em&gt;) to use later in the configuration.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="configure-infrastructure-services"&gt;Configure Infrastructure Services&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;My real-quicK-cluster &lt;a href="https://github.com/digihunch/real-quicK-cluster"&gt;project&lt;/a&gt; has a few different ways to quickly bring up a cluster. For this lab, I use Azure CLI command to create a simple, three-node cluster:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ az aks create &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -g AutomationTest &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -n orthCluster &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --node-count &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --enable-addons monitoring &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --generate-ssh-keys &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --vm-set-type VirtualMachineScaleSets &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --network-plugin azure &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --network-policy calico &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --tags Owner&lt;span style="color:#f92672"&gt;=&lt;/span&gt;MyOwner&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then we configure kubeconfig credential to connect to the cluster, so that &lt;em&gt;kubectl&lt;/em&gt; and &lt;em&gt;helm&lt;/em&gt; CLI commands to connect to the newly created cluster.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ az aks get-credentials --resource-group AutomationTest --name orthCluster&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then we will install the infrastructure services and workload. We can start with loading environment variables for use later:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;OIDC_DISCOVERY&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;curl &lt;span style="color:#e6db74"&gt;&amp;#34;https://accounts.google.com/.well-known/openid-configuration&amp;#34;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;OIDC_ISSUER_URL&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;echo $OIDC_DISCOVERY | jq -r .issuer&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;OIDC_JWKS_URI&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;echo $OIDC_DISCOVERY | jq -r .jwks_uri&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;COOKIE_SECRET&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;openssl rand -base64 &lt;span style="color:#ae81ff"&gt;32&lt;/span&gt; | tr -- &lt;span style="color:#e6db74"&gt;&amp;#39;+/&amp;#39;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;-_&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;WEB_HOST&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;demo1.digihunch.com&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;CLIENT_ID&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;ThisIsTheClientIDFromGoogle&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;CLIENT_SECRET&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;ThisIsTheClientSecretFromGoogle&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;For WEB_HOST, CLIENT_ID, CLIENT_SECRET, you need to bring your own values. The COOKIE_SECRET value is randomly generated. The variables OIDC_ISSUER_URL and OIDC_JWKS_URI are parsed from Google OpenID configuration and they should remain static. We can validate the value with echo commands. Also, to use helm v3 later, we need to add the repositories we need. These steps can be skipped if they have been performed in the client environment:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ echo $OIDC_ISSUER_URL&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;https://accounts.google.com&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ echo $OIDC_JWKS_URI&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;https://www.googleapis.com/oauth2/v3/certs&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ helm repo add jetstack https://charts.jetstack.io&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ helm repo add istio https://istio-release.storage.googleapis.com/charts&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ helm repo add oauth2-proxy https://oauth2-proxy.github.io/manifests&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ helm repo update&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now, with Helm ready, we can install Cert Manager, Istio CRD, Control Plane, Gateways as well as OAuth2-Proxy using Helm. We run each of the following commands from the &lt;a href="https://github.com/digihunch/istio-oidc/tree/main/envoy"&gt;envoy&lt;/a&gt; directory, where the required files are stored:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ helm install cert-manager jetstack/cert-manager &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --namespace cert-manager &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --create-namespace &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --version v1.7.1 &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --set installCRDs&lt;span style="color:#f92672"&gt;=&lt;/span&gt;true&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ helm install -n istio-system istio-base istio/base --create-namespace&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ helm -n istio-system install istiod istio/istiod -f istiod-values.yaml --wait&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ helm -n istio-system install istio-ingress istio/gateway -f ingress-gateway-values.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl -n istio-system get po&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl create ns oauth2-proxy &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; kubectl label ns oauth2-proxy istio-injection&lt;span style="color:#f92672"&gt;=&lt;/span&gt;enabled&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ helm install -n oauth2-proxy &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --version 6.0.1 &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --values oauth2-proxy-values.yaml &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --set config.clientID&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$CLIENT_ID &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --set config.clientSecret&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$CLIENT_SECRET &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --set config.cookieSecret&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$COOKIE_SECRET &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --set extraArgs.oidc-issuer-url&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$OIDC_ISSUER_URL &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --set extraArgs.whitelist-domain&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$WEB_HOST &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; oauth2-proxy oauth2-proxy/oauth2-proxy&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl -n oauth2-proxy get pods -l app&lt;span style="color:#f92672"&gt;=&lt;/span&gt;oauth2-proxy&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Note that the istiod installation step uses a value file that includes extensionProviders. This is where we tell Istio to connect to external authorization provider:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;meshConfig&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;accessLogFile&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/dev/stdout&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;extensionProviders&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;oauth2-proxy&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;envoyExtAuthzHttp&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;service&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;oauth2-proxy.oauth2-proxy.svc.cluster.local&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;port&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;4180&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;includeRequestHeadersInCheck&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;cookie&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;headersToUpstreamOnAllow&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;authorization&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;headersToDownstreamOnDeny&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;set-cookie&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;When we install istio ingress gateway, we need to expose port 80 as well as 443. Port 80 is used in ACME protocol for certificate configuration. We also installed oauth2-proxy with some configurations from oauth2-proxy-values.yaml as well as some argument set imperatively.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Once configuration is successful, we should be able to confirm that oauth2-proxy service is running. We should also be able to tell the External IP address of the Ingress:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl -n oauth2-proxy get svc&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl -n istio-system get service istio-ingressgateway -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{.status.loadBalancer.ingress[0].ip}&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now we can go to our DNS configuration portal, to populate the DNS A-record for demo1.digihunch.com with this IP address:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="1756" height="434" src="https://static.digihunch.com/wp-content/uploads/2022/02/dns.png" alt="" class="wp-image-3620"/&gt;&lt;figcaption class="wp-element-caption"&gt;Add DNS A-record&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It may take several minutes to a couple of hours, for the A-record update/creation to take effect, depending on the TTL. Sit tight and query DNS with nslookup command, until it is set. In the next step, we will need successful DNS resolution for certificate configuration.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="install-workload-and-configure-certificate"&gt;Install workload and configure Certificate&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now we install the workload, along with the necessary constructs such as Gateway CRD, Virtual Service, and certificates. We can use kubectl kustomize command to preview the changes, then apply the changes with -k:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl kustomize demo&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl apply -k demo&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;When you have your own DNS hostname, modify the &lt;a href="https://github.com/digihunch/istio-oidc/blob/main/envoy/demo/kustomization.yaml"&gt;kustomization.yaml&lt;/a&gt; accordingly before applying. This command uses Kustomization to apply numerous YAML manifests in the &lt;a href="https://github.com/digihunch/istio-oidc/tree/main/envoy/demo"&gt;demo&lt;/a&gt; directory, including the following activities:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Create a Namespace named demo&lt;/li&gt;&#10;&lt;li&gt;Create Deployment and Service&lt;/li&gt;&#10;&lt;li&gt;Set up PeerAuthentication for the mesh&lt;/li&gt;&#10;&lt;li&gt;Configure Ingress class, ClusterIssuer and Certificate using cert-manager&lt;/li&gt;&#10;&lt;li&gt;Configure Virtual Service and Gateway&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This configures everything we need and we need to verify from several aspects. The demo Service is a ClusterIP service exposed within the cluster at port 80. Then we check the status of certificate:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl -n istio-system get certificate&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl -n istio-system describe certificate demo&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The column of READY should have a value of True. Check the logs from cert manager Pods if that&amp;#8217;s not the case. Common reasons include:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;The automatic ACME validation is just not ready &lt;/li&gt;&#10;&lt;li&gt;The automatic ACME validation is still waiting for DNS resolution&lt;/li&gt;&#10;&lt;li&gt;The Istio ingress gateway port 80 is not open for ACME validation&lt;/li&gt;&#10;&lt;li&gt;The let&amp;#8217;s encrypt server applies rate limiting&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that for the ACME server, we can use staging server&amp;#8217;s URL or productions. The former provisions a certificate that may come off as insecure as the CA is not fully trusted by browser. The latter doesn&amp;#8217;t have this issue, but the server applies rate limiting. For more details of how validation with ACME work, check out my &lt;a href="https://static.digihunch.com/2021/08/creating-tls-certificate-kubernetes/"&gt;previous post&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Once the certificate is applied, our website is open to any visitor with HTTPS access:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="748" height="257" src="https://static.digihunch.com/wp-content/uploads/2022/02/image-5.png" alt="" class="wp-image-3636"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Then we will use Request Authentication and Authorization Policy to tighten up the access by requiring visiting user to log in with Google identity.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="requestauthentication-and-authorizationpolicy"&gt;RequestAuthentication and AuthorizationPolicy&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We configure a CUSTOM action in the AuthorizationPolicy, and specified the provider by name oauth2-proxy. The YAML manifests for RequestAuthentication and AuthorizationPolicy looks like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;security.istio.io/v1beta1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;RequestAuthentication&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;istio-ingressgateway&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;istio-system&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;jwtRules&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;issuer&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;https://accounts.google.com&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;jwksUri&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;https://www.googleapis.com/oauth2/v3/certs&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;selector&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;matchLabels&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;istio-ingressgateway&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;security.istio.io/v1beta1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;AuthorizationPolicy&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;istio-ingressgateway&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;istio-system&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;action&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;CUSTOM&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;provider&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;oauth2-proxy&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;rules&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - {}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;selector&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;matchLabels&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;istio-ingressgateway&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Note that we cannot apply Request Authentication and Authorization Policy before we confirm that the Certificate has been created. This is because when letsencrypt use HTTP resolver to provision certificate with ACME protocol, the request from letsencrypt should get to the validating URI without being asked to provide a Google identity. So do not perform this step yet before confirming the previous step:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;$ kubectl apply -f oidc-auth.yaml&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now launch your browser in incognito mode and browse to our URL (https://demo1.digihunch.com) and you will be re-redirected to log on to Google (accounts.google.com). Once logged on, you will have access to the site. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If you run into errors, check the oauth2-proxy log from Pod stdout, which should give a reason of 4xx errors. If oauth2-proxy log indicates no activity, confirm if the request has been forwarded to the proxy. Check the service object of the proxy and make sure it is exposed to the correct port, as indicated in the meshConfig. To check mesh config, examine the configmap named istio in the namespace of istio-system. To check if Istio&amp;#8217;s authorization is unable to speak with oatuh2-proxy, inspect the log of istiod Pod.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To clean up the lab, remove the app registration from Google, and then delete the cluster from Azure:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ az aks delete -g AutomationTest -n orthCluster&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In this lab we used a very open AuthorizationPolicy. We can polish it up with more conditions such as:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;security.istio.io/v1beta1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;AuthorizationPolicy&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;istio-ingressgateway&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;istio-system&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;action&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;CUSTOM&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;provider&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;oauth2-proxy&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;rules&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;when&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;key&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;request.auth.audiences&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;values&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;$CLIENT_ID&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;selector&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;matchLabels&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;istio-ingressgateway&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Under the rules section, we can explore the claims from the JWT and make creative use of it to achieve our specific goals for IAM.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="bottom-line"&gt;Bottom line&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In this lab, we registered our app with Google and obtained client ID and secret for it to identify our application. We configured oauth2-proxy as our IAM application, carrying the credential validated by Google. We also configured Istio to delegate authorization to oauth2-proxy as external authorization provide, making up an entire OIDC integration.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The third party service (IdP) we integrate with is GCP, which natively supports OIDC protocol. However, if the third party IdP does not support OIDC natively, such as &lt;a href="https://static.digihunch.com/2020/02/everything-about-the-domain/"&gt;Active Directory&lt;/a&gt; via &lt;a href="https://static.digihunch.com/2020/03/lightweight-directory-access-protocol-ldap/"&gt;LDAP&lt;/a&gt;, then we would need one more proxy to sit between OAuth2 and the external IdP service. One good choice is the &lt;a href="https://www.cncf.io/projects/dex/"&gt;Dex&lt;/a&gt; connector. Another more complex yet powerful alternative connector is &lt;a href="https://www.keycloak.org/"&gt;Key Cloak&lt;/a&gt;. Consider Dex or KeyCloak as an OIDC proxy, when: &lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;The external IdP does not support OIDC; or&lt;/li&gt;&#10;&lt;li&gt;We choose not to use the OIDC capability of the external IdP because we want to managed OIDC centrally in our own proxy&lt;/li&gt;&#10;&lt;li&gt;The external identity provider does support OIDC, but the configuration is managed by a different team (e.g. IAM team) other than the platform team&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AuthorizationPolicy is the key piece in this integration, and it is executed at the http filter in envoy sidecar proxy. It can be done with alternatives to OAuth2-Proxy such as the &lt;a href="https://github.com/istio-ecosystem/authservice"&gt;authservice&lt;/a&gt; project. A slightly different approach to implement OIDC integration is to use leverage &lt;a href="https://istio.io/latest/blog/2020/wasm-announce/"&gt;extensibility&lt;/a&gt; of &lt;a href="https://istio.io/latest/docs/concepts/wasm/"&gt;WebAssembly&lt;/a&gt; (a sandboxing technology to extend Envoy), which is suggested in this example for &lt;a href="https://istio.io/latest/docs/reference/config/proxy_extensions/wasm-plugin/"&gt;WasmPlugin&lt;/a&gt;. However, Wasm is still considered experimental.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2022/02/istio-lab-authentication-and-authorization-in-jwt/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Istio Lab – Authentication and Authorization&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2022/03/from-nginx-to-envoy-proxy/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Service Proxy – from Nginx to Envoy&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Istio Lab – Authentication and Authorization</title><link>https://static.digihunch.com/2022/02/istio-lab-authentication-and-authorization-in-jwt/</link><pubDate>Sun, 13 Feb 2022 13:21:13 -0400</pubDate><guid>https://static.digihunch.com/2022/02/istio-lab-authentication-and-authorization-in-jwt/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-istio-lab.webp" alt="Featured image of post Istio Lab – Authentication and Authorization" /&gt;&lt;p class="wp-block-paragraph"&gt;My previous &lt;a href="https://static.digihunch.com/2022/02/authentication-and-authorization-with-istio/"&gt;blog&lt;/a&gt; discussed as service mesh what Istio can offer in terms of authentication and authorization capabilities. Istio can authenticate an incoming HTTP request, ensuring the JWT issued has not been tampered somewhere in the middle. The fields in the JWT allows for more flexibilities at the point of authorization. This combination allows Istio to integrate with identity providers that can issue JWT.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We&amp;#8217;ve also discussed how JWT works, and pointed out that the two key element of request authentication is the JWT (payload signed with private key) itself as well as the JWK (carrying public key). In this post, we will test it in a lab. To start with this lab, we need a test cluster (e.g. Minikube) with Istio &lt;a href="https://github.com/digihunch/korthweb/tree/main/manual"&gt;installed&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="preparation"&gt;Preparation&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I have not find a native Bash way to produce JWT. We will do that with python packages python_jwt and jwcrypto in Python3. Let&amp;#8217;s install the modules and import them in Python environment.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ python3 -m pip install python_jwt jwcrypto datetime&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ python3&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; import python_jwt as jwt, jwcrypto.jwk as jwk, datetime&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now we&amp;#8217;re in the Python3 shell with needed modules loaded. We can take the following steps to produce the JWT as well as the JWK:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;RSAkey &lt;span style="color:#f92672"&gt;=&lt;/span&gt; jwk&lt;span style="color:#f92672"&gt;.&lt;/span&gt;JWK&lt;span style="color:#f92672"&gt;.&lt;/span&gt;generate(kty&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;RSA&amp;#39;&lt;/span&gt;, size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2048&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;private_key &lt;span style="color:#f92672"&gt;=&lt;/span&gt; RSAkey&lt;span style="color:#f92672"&gt;.&lt;/span&gt;export_private()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;public_key &lt;span style="color:#f92672"&gt;=&lt;/span&gt; RSAkey&lt;span style="color:#f92672"&gt;.&lt;/span&gt;export_public()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;raw_payload &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#39;iss&amp;#39;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#39;digihunch.com&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;sub&amp;#39;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#39;DIGIHUNCH&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;role&amp;#39;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#39;reader&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;permission&amp;#39;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#39;read&amp;#39;&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## use private key to generate jwt token. HTTP request will bear this token &lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;jwt_token &lt;span style="color:#f92672"&gt;=&lt;/span&gt; jwt&lt;span style="color:#f92672"&gt;.&lt;/span&gt;generate_jwt(raw_payload, jwk&lt;span style="color:#f92672"&gt;.&lt;/span&gt;JWK&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_json(private_key), &lt;span style="color:#e6db74"&gt;&amp;#39;RS256&amp;#39;&lt;/span&gt;, datetime&lt;span style="color:#f92672"&gt;.&lt;/span&gt;timedelta(minutes&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;))&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(jwt_token)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## The JWKS keeps public key and is referenced by Istio RequestAuthentication object&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;jwks&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{&amp;#34;keys&amp;#34;:[&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;+&lt;/span&gt;public_key&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;]}&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(jwks)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Helpful command to print the key in PEM format:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## RSAkey.export_to_pem(private_key=False)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Helpful command to verify JWT token:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## header, claims = jwt.verify_jwt(jwt_token, jwk.JWK.from_json(public_key), [&amp;#39;RS256&amp;#39;])&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;To complete this lab, we need the values of &lt;em&gt;jwt_token&lt;/em&gt; and &lt;em&gt;jwks&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Next in the preparation is a local &lt;a href="https://static.digihunch.com/2021/09/single-node-kubernetes-cluster-minikube/"&gt;cluster&lt;/a&gt;, istio with metallb installed, which is covered in a previous &lt;a href="https://static.digihunch.com/2021/11/istio-ingress-egress/"&gt;post&lt;/a&gt;. We should be able to get the ingress IP address:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ export INGRESS_HOST&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;kubectl -n istio-system get service istio-ingressgateway -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{.status.loadBalancer.ingress[0].ip}&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ echo $INGRESS_HOST&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;We create our own namespace and use the &lt;a href="https://github.com/istio/istio/tree/master/samples/httpbin"&gt;httpbin&lt;/a&gt; application:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl create ns web &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl label namespace web istio-injection&lt;span style="color:#f92672"&gt;=&lt;/span&gt;enabled&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl apply -n web -f httpbin.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl apply -n web -f httpbin-gateway.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ curl -I -XGET $INGRESS_HOST/headers&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The curl command above (without any token) should return HTTP 200 code, indicating that no authorization token is needed to connect to the service. Note that the Pod for httpbin has the label &lt;em&gt;app=httpbin&lt;/em&gt; which will be used in the request authentication. Also note that traffic is served over a named port called http in the Service object for http, which will implicitly enable HTTP based conditions for authorization policies we will build later. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="request-authentication"&gt;Request Authentication&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Let&amp;#8217;s create a request authentication object with the following manifest:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;security.istio.io/v1beta1&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;RequestAuthentication&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;jwt-req-authn&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;web&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;selector&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;matchLabels&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;httpbin&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;jwtRules&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;issuer&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;digihunch.com&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;jwks&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; ## jwks output from previous step ##&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Replace the last line with the jwks output from the preparation step and store it to jwt-req-authn.yaml. It should look like this:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="1589" height="386" src="https://static.digihunch.com/wp-content/uploads/2022/02/image-1.png" alt="" class="wp-image-3442"/&gt;&lt;figcaption class="wp-element-caption"&gt;jwt-req-authn.yaml&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Then apply it to the web namespace:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl -n web apply -f jwt-req-authn.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now the request authentication resource is applied to the httpbin workload. We first test it with a random authentication token and it should be denied of 401 error:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ curl -I -XGET $INGRESS_HOST/headers --header &lt;span style="color:#e6db74"&gt;&amp;#34;Authorization: Bearer randomstring&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HTTP/1.1 &lt;span style="color:#ae81ff"&gt;401&lt;/span&gt; Unauthorized&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;www-authenticate: Bearer realm&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;http://192.168.64.16/headers&amp;#34;&lt;/span&gt;, error&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;invalid_token&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;content-length: &lt;span style="color:#ae81ff"&gt;79&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;content-type: text/plain&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;date: Sun, &lt;span style="color:#ae81ff"&gt;13&lt;/span&gt; Feb &lt;span style="color:#ae81ff"&gt;2022&lt;/span&gt; 16:13:32 GMT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;server: istio-envoy&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;x-envoy-upstream-service-time: &lt;span style="color:#ae81ff"&gt;31&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then we take the jwt_token from the preparation step and present it to the request authentication resource by sending an HTTP request with the appropriate authorization token. It should return an HTTP 200 code this time:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ curl -I -XGET $INGRESS_HOST/headers --header &lt;span style="color:#e6db74"&gt;&amp;#34;Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDQ3NzE0NjUsImlhdCI6MTY0NDc2ODQ2NSwiaXNzIjoiZGlnaWh1bmNoLmNvbSIsImp0aSI6Im1faUhla2pNbWRmUmlsWEdCaTFBR3ciLCJuYmYiOjE2NDQ3Njg0NjUsInBlcm1pc3Npb24iOiJyZWFkIiwicm9sZSI6InJlYWRlciIsInN1YiI6IkRJR0lIVU5DSCJ9.sVppwmvDqKvSsVdB05a_mDHymZq7Okvnwu-caTywXQgsUvOA6HfaySp_WXMyTp1HQ4WcTqKE4frZm7QNtrZsPso4bdD_4mEDYTswTCWhblaPy236NJBEH3ilB2BVySBVQKsjyxd94F1KV24SFWiR6lUxk52wKKE3ipBwR79jPizhAu9xxrfJ2Lfi5ypNa_kjBdJi63KCt2Y0eW94Fjq3PZs4ZalHJyaXYSx5Gxyei5f7QdpEOBpvs13mSdi9RqkgVQOjE0V1uBRpMckMyZs-IijknJcSu4fkrjgfNmXsrm__-vlM9UjUl2Jlj0x8bRC8l20IZ6t1ml-GFkwj39JC0g&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HTTP/1.1 &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt; OK&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;server: istio-envoy&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;date: Sun, &lt;span style="color:#ae81ff"&gt;13&lt;/span&gt; Feb &lt;span style="color:#ae81ff"&gt;2022&lt;/span&gt; 16:13:54 GMT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;content-type: application/json&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;content-length: &lt;span style="color:#ae81ff"&gt;589&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;access-control-allow-origin: *&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;access-control-allow-credentials: true&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;x-envoy-upstream-service-time: &lt;span style="color:#ae81ff"&gt;17&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In the example above, jwtRules can be used with other keys such as jwksUri to reference the jwks by Uri. More fields in JWTRules can be found &lt;a href="https://istio.io/latest/docs/reference/config/security/request_authentication/#JWTRule"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now we have tested three curl commands:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Without authentication token at all: http server (istio-envoy) returns 200 code.&lt;/li&gt;&#10;&lt;li&gt;With an invalid authentication token: http server (istio-envoy) returns 401 code for error.&lt;/li&gt;&#10;&lt;li&gt;with a valid authentication: http server (istio-envoy) returns 200 code.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;So we have the capability to validate token, but it is not yet mandatory to present the token. We can change this behaviour by tweaking authorization policy.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="authorization-policy"&gt;Authorization Policy&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;According to Istio &lt;a href="https://istio.io/latest/docs/reference/config/security/request_authentication/"&gt;documentation&lt;/a&gt;, to restrict access to authenticated requests only, this should be accompanied by an authorization rule. We start with the following policy:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;security.istio.io/v1beta1&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;AuthorizationPolicy&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;auth-pol&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;web&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;selector&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;matchLabels&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;httpbin&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;action&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ALLOW&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;rules&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;from&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;source&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;requestPrincipals&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;*&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt; Applying the manifest above to namespace web to it applies to workload httpbin. Then we &lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ curl -I -XGET $INGRESS_HOST/headers&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HTTP/1.1 &lt;span style="color:#ae81ff"&gt;403&lt;/span&gt; Forbidden&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;content-length: &lt;span style="color:#ae81ff"&gt;19&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;content-type: text/plain&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;date: Sun, &lt;span style="color:#ae81ff"&gt;13&lt;/span&gt; Feb &lt;span style="color:#ae81ff"&gt;2022&lt;/span&gt; 16:29:58 GMT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;server: istio-envoy&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;x-envoy-upstream-service-time: &lt;span style="color:#ae81ff"&gt;35&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The requestPrincipals clause makes it mandatory to present a token. The RequestAuthentication validates the token. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We can beef up the authorization policies by adding claims to the conditions, for example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;security.istio.io/v1beta1&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;AuthorizationPolicy&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;auth-pol&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;web&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;selector&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;matchLabels&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;httpbin&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;action&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ALLOW&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;rules&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;from&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;source&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;requestPrincipals&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;*&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;to&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;operation&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;methods&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;GET&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;when&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;key&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;request.auth.claims[iss]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;values&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;digihunch.com&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;key&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;request.auth.claims[role]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;values&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;reader&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Both the to and when conditions are for HTTP traffic only, and we must tell Istio to inspect the traffic as HTTP, which is done implicitly with named ports on the Service object. Refer to &lt;a href="https://istio.io/latest/docs/ops/common-problems/security-issues/#make-sure-you-are-not-using-http-only-fields-on-tcp-ports"&gt;this&lt;/a&gt; common problem from Istio&amp;#8217;s documentation. The request principals, if a none wildcard value is specified, will be a SPIFFE format identity, the same one used for peer authentication, as discussed in the &lt;a href="https://static.digihunch.com/2022/02/authentication-and-authorization-with-istio/"&gt;previous&lt;/a&gt; post.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The &lt;em&gt;when&lt;/em&gt; clause above contains two key-value pairs. The first looks for the value of a standard claim (iss), the second for a custom claim (role). In the preparation step, we created the claims with those claims in Python and they will match the condition here. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Should any of the conditions above not match, a 403 (Forbidden) error code will be returned by the workload&amp;#8217;s istio-envoy proxy.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="validating-mtls"&gt;&lt;a href="#ValidatingMTLS"&gt;Verify mTLS connection&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In some cases, we need to audit whether the TLS traffic between workloads actually take place in mTLS. As I was developing my &lt;a href="https://github.com/digihunch/korthweb"&gt;korthweb&lt;/a&gt; project, I don&amp;#8217;t find a straightforward way of validating TLS. We can validate that mTLS mode on a workload using the following istio CTL command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ istioctl x describe pod my-workload-pod -n &lt;span style="color:#f92672"&gt;[&lt;/span&gt;namespace&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The output shows Effective PeerAuthentication and Applied PeerAuthentication. This verifies configuration. But how can we ensure that traffic are indeed using mTLS? For most of mTLS traffic, we can use Kiali&amp;#8217;s observability feature. In Graph, we need to ensure &amp;#8220;Security&amp;#8221; is checked in the display drop-down. The pad lock will indicate the traffic is mTLS. To get reliable results, we have to artificially create some live traffic between workloads (e.g. curl from one Pod to another) so Kiali can pick up the update.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="1015" height="723" src="https://static.digihunch.com/wp-content/uploads/2022/02/image-3.png" alt="" class="wp-image-3550"/&gt;&lt;figcaption class="wp-element-caption"&gt;Validate mTLS traffic in Kiali&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Unfortunately, even artificial traffic does not make Kiali the most reliable way to detect mTLS. There are three other approach to verify TLS traffic.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The first approach is to let Envoy proxy emit TLS related traffic. We can apply the following annotation line to a Pod, to tell its Envoy proxy to emit measurements related to tls_inspector:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;sidecar&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;statsInclusionPrefixes&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;tls_inspector,listener.0.0.0.0_15006&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The metrics will be exposed to Envoy&amp;#8217;s admin port (15000 on istio-proxy) with the path /stats. For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl -n orthweb exec orthanc-7f4c9b759-lxnrb -c istio-proxy -- curl localhost:15000/stats | grep tls_inspector&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; % Total % Received % Xferd Average Speed Time Time Time Current&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Dload Upload Total Spent Left Speed&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;22194&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;22194&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 21.1M &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; --:--:-- --:--:-- --:--:-- 21.1M&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tls_inspector.alpn_found: &lt;span style="color:#ae81ff"&gt;13&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tls_inspector.alpn_not_found: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tls_inspector.client_hello_too_large: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tls_inspector.connection_closed: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tls_inspector.read_error: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tls_inspector.sni_found: &lt;span style="color:#ae81ff"&gt;13&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tls_inspector.sni_not_found: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tls_inspector.tls_found: &lt;span style="color:#ae81ff"&gt;13&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tls_inspector.tls_not_found: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;We still need some live traffic to bump up the measurement. Comparing between tls_found and tls_not_found is a good way to determine if a Pod is receiving both TLS and plaintext traffic in PERMISSIVE mode.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The second approach to tell mTLS is via the &lt;strong&gt;connection_security_policy&lt;/strong&gt; metric label. It is set to mtutual_tls if the connection is in mTLS. We need the dashboard for Prometheus:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ istioctl dashboard prometheus&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In the prometheus dashboard, click on &amp;#8220;Graph&amp;#8221; at the top, then search for &amp;#8220;&lt;em&gt;istio_tcp_connections_closed_total&lt;/em&gt;&amp;#8221; for &amp;#8220;&lt;em&gt;istio_tcp_connections_opened_total&lt;/em&gt;&amp;#8220;, the result should include a metrics called &lt;em&gt;connection_security_policy&lt;/em&gt; which is labelled as mtutual_tls, as illustrated below:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="2870" height="754" src="https://static.digihunch.com/wp-content/uploads/2022/02/image-4.png" alt="" class="wp-image-3553"/&gt;&lt;figcaption class="wp-element-caption"&gt;mTLS metric&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For more about collecting and querying metrics from Prometheus, check out Istio&amp;#8217;s documentation &lt;a href="https://istio.io/latest/docs/tasks/observability/metrics/tcp-metrics/"&gt;here&lt;/a&gt; and &lt;a href="https://istio.io/latest/docs/tasks/observability/metrics/querying-metrics/"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The third approach is to utilize the AUDIT feature of Authorization Policy. When a rule in Authorization Policy has a &lt;a href="https://istio.io/latest/docs/reference/config/security/authorization-policy/#Source"&gt;source&lt;/a&gt; with namespace or notNamespace field, it requires the incoming connection to have an SPIFFE identity and use mTLS. We can set the Authorization Policy&amp;#8217;s action to AUDIT and use &lt;a href="https://istio.io/latest/docs/ops/common-problems/security-issues/#ensure-istiod-accepts-the-policies"&gt;RBAC access&lt;/a&gt; logging, the same way we would do to troubleshoot RBAC access issues. From the log we can check RBAC failures due to missing SPIFFE identity.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2022/02/authentication-and-authorization-with-istio/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Istio Authentication and Authorization&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2022/02/istio-external-authorization/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Istio External Authorization via OIDC&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Istio Authentication and Authorization</title><link>https://static.digihunch.com/2022/02/authentication-and-authorization-with-istio/</link><pubDate>Sat, 05 Feb 2022 23:04:00 -0400</pubDate><guid>https://static.digihunch.com/2022/02/authentication-and-authorization-with-istio/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-istio-auth.webp" alt="Featured image of post Istio Authentication and Authorization" /&gt;&lt;p class="wp-block-paragraph"&gt;Applications running on Kubernetes platform seeks to offload common non-business features to the platform. Istio helps Kubernetes bridge that gap. It can enforce mTLS communication, which is known as Peer Authentication. It can help with two other things with the use of JWT token: when a web request presents a JWT token, it can validate whether it is authentic. Then, it can use the claims in JWT token to drive authorization decision on whether the specific request is allowed or denied. Both will use Istio CRDs.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="peer-authentication"&gt;Peer Authentication&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Istio can enforce mTLS for TCP traffic between Pods. According to its documentation, enforcing mTLS at mesh level is as simple as applying a Peer Authentication resource to the root-level namespace:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;security.istio.io/v1beta1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;PeerAuthentication&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;default&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;istio-system&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;mtls&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;mode&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;STRICT&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The role of mTLS is so Pods can validates each other&amp;#8217;s identity and then encrypt the TLS traffic in between. Each workload must first have an identity and Envoy proxy addressed this issue by adopting &lt;a href="https://spiffe.io/"&gt;SPIFFE&lt;/a&gt; framework. It gives each workload an identity in the format of &amp;lt;TRUST_DOMAIN&amp;gt;/ns/&amp;lt;NAMESPACE&amp;gt;/sa/&amp;lt;SERVICE_ACCOUNT&amp;gt;. For example: spiffe://cluster.local/ns/myapp-dev/sa/default. It is also important to understand that only Pods with injected Envoy sidecar have SPIFFE workload identity and therefore is able to speak in mTLS. For new services, this is usually not an issue. For migrating workload without sidecar, a Pod without sidecar may connect with one in the mesh (with sidecar) if the mtls mode is PERMISSIVE in Peer Authentication. Otherwise, the connect is reset at layer 4 with the following error:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl: &lt;span style="color:#f92672"&gt;(&lt;/span&gt;56&lt;span style="color:#f92672"&gt;)&lt;/span&gt; Recv failure: Connection reset by peer&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;command terminated with exit code &lt;span style="color:#ae81ff"&gt;56&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Therefore, it is advisable to start with PERMISSIVE mode for a precautionary migration of workload to mTLS. With mTLS all effective at the mesh level, there is no need to natively configure TLS between services.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The SPIFFE identity used in PeerAuthentication can also be used in Request Authorization as rule conditions. I will discuss request authentication before request authorization. To understand request authentication, let&amp;#8217;s first warm up on JWT.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="json-web-token"&gt;JSON Web Token&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;JSON Web Token (JWT, RFC 7519) is a format to carry JSON payload with optional signature and/or encryption. It can be thought of as a document (in JSON format) with signature for web servers to exchange information. The signature portion makes it friendly for document consumers to validate the authenticity. It is also URL-safe, and thereby adopted in web-browser SSO context, to pass identity of an authenticated user between and identity provider and a service provider.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;JWT enables token-based authentication, a significant improvement from traditional session-based authentication. The traditional session-based authentication can be illustrated as below:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://miro.medium.com/max/1400/1*Hg1gUTXN5E3Nrku0jWCRow.png" alt=""/&gt;&lt;figcaption class="wp-element-caption"&gt;session-based authentication&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This authentication model has major drawbacks. First, a mechanism to validate the authenticity of Cookie is missing. Second, the server has to keep the session information, making itself not stateless, unless a state store such as memcached is introduced.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://miro.medium.com/max/1400/1*PDry-Wb8JRquwnikIbJOJQ.png" alt=""/&gt;&lt;figcaption class="wp-element-caption"&gt;token-based authentication&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In token-based authentication such as using JWT, a token is issued. The authenticity of the token are validated before the server provides data, and it can be validated by any backend server. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The payload of JWT consists of &lt;a href="https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-token-claims"&gt;claims&lt;/a&gt;, which are statements about an identity (such as name, role, email). There are custom claims as well as standard reserved claims, such as iss (issuer), sub (subject), aud (audience), iat (issued at time), exp (expiration time), and jti (JWT ID). When a program produces a JWT, it turns the raw payload into standardize payload by adding the required reserved claims and may sort the claims alphabetically. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The JWT consists of three parts with a period as delimiter:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1308" height="414" src="https://static.digihunch.com/wp-content/uploads/2022/02/image.png" alt="" class="wp-image-3415"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The third part is a signature in the format of JWS (JSON Web Signature, RFC 7515) for the JWT consumer to validate its authenticity. The first and second parts, as you can tell, are the claims in the document. Their base64 encoding can be decoded with no effort and should therefore be considered exposed. Although JWT addresses the authenticity of information, it does not intend to address the confidentiality of the payload at HTTP layer. The payload should not carry sensitive information and should always be used with secure HTTPS port. To tackle this issue, there is JWE (JSON Web Encryption, RFC 7516) which is an implementation similar to JWT which also encrypts the payload.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Some IAM protocols are built on top of JWT. For example, the OpenID Connect specification also defines a set of &lt;a href="https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims"&gt;standard claims&lt;/a&gt; that it uses while still allow custom claims.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="request-authentication"&gt;Request Authentication&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Istio can perform request authentication using its CRD. It is important to distinguish request authentication and user authentication. In user authentication, the identify provider typically looks up an identity store and compares password hash results to check whether the identity of the visiting user is authentic or not. This is outside of Istio&amp;#8217;s capability but many off-the-shelf solution excels at it, such as Azure AD. Once the user&amp;#8217;s identity is validated by identity provider, and a JWT is issued for downstream service providers to consume. Istio&amp;#8217;s CRD can front the service provider and validate that the presented JWT is authentic. It authenticates the identity of a request (as truly issued by the trusted issuer without being tampered). This process does not involve checking user&amp;#8217;s identity, even though user&amp;#8217;s identity could be stored in the payload by the JWT issuer. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Istio uses the RequestAuthentication CRD to perform this function. The JWT issuer signs with its private key and stores the signature in the JWT. When it is presented to Istio, Istio&amp;#8217;s RequestAuthentication CRD needs the public key of the issuer in order to validate the JWT. The public key usually comes in as a JWK (JSON Web Key, RFC7517), a format convertible to and from PEM format. The JWK can be provided either inline in the RequestAuthentication&amp;#8217;s YAML manifest, or via a URI. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Below is an example of a basic RequestAuthentication declaration:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;security.istio.io/v1beta1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;RequestAuthentication&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;httpbin&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;foo&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;selector&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;matchLabels&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;httpbin&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;jwtRules&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;issuer&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;issuer-foo&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;jwksUri&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;https://example.com/.well-known/jwks.json&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In this example (from the &lt;a href="https://istio.io/latest/docs/reference/config/security/request_authentication/"&gt;documentation&lt;/a&gt;), the &lt;a href="https://istio.io/latest/docs/reference/config/security/request_authentication/#JWTRule"&gt;jwtRule&lt;/a&gt; requires that the issuer be issuer-foo, and the JWK (containing public key) is provided by a given URI address. Istio will pass the authentication once the signature in the presented JWT is verified with the JWK.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="authorization-policy"&gt;Authorization Policy&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Istio&amp;#8217;s Authorization Policy by itself can operate at both TCP or HTTP layers and is &lt;a href="https://istio.io/latest/docs/concepts/security/#authorization-architecture"&gt;enforced&lt;/a&gt; at the envoy proxy. The result is an ALLOW or DENY decision, based on a set of &lt;a href="https://istio.io/latest/docs/reference/config/security/conditions/"&gt;conditions&lt;/a&gt; at both levels. If the traffic is HTTP then you should consider use some HTTP level information as it provides a lot more flexibility. Even when operating at HTTP layer, AuthorizationPolicy does not have to work in conjunction with RequestAuthentication. The rules can use path, methods, etc to drive an authorization decision, for example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;security.istio.io/v1beta1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;AuthorizationPolicy&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;authz-policy-orthanc&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;selector&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;matchLabels&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;orth&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;action&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ALLOW&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;rules&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;from&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;source&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespaces&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;istio-system&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;orthweb&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;to&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;operation&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;methods&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;GET&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;PUT&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;HEAD&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;DELETE&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ports&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;8042&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;from&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;source&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespaces&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;istio-system&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;orthweb&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;to&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;operation&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ports&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;4242&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The claims in the JWT payload can also be used to drive authorization decision, as exemplified in the Istio &lt;a href="https://istio.io/latest/docs/reference/config/security/authorization-policy/"&gt;documentation&lt;/a&gt;, by using a &lt;em&gt;when&lt;/em&gt; keyword in a rule and specifying the claim as a key:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;when&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;key&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;request.auth.claims[iss]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;values&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;https://accounts.google.com&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The when clause requires that the &lt;em&gt;iss&lt;/em&gt; claim in the JWT must carry a specific value in order to ALLOW the HTTP request. While the claims in JWT is just an additional factor to drive authorization decision, using authenticated information to drive authorization decision makes the overall workflow more secure, and should therefore be used when applicable.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When using AuthorizationPolicy CRD, keep in mind:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Use correct selectors so it only applies to &lt;a href="https://istio.io/latest/docs/ops/common-problems/security-issues/#make-sure-the-policy-is-applied-to-the-correct-target"&gt;workloads&lt;/a&gt; labelled as such. Without selector the policy takes effect in the entire namespace which is a recipe for issues&lt;/li&gt;&#10;&lt;li&gt;When multiple policies (each with multiple rules) are applied to the same workload, be aware of the policy &lt;a href="https://istio.io/latest/docs/concepts/security/#implicit-enablement"&gt;precedence&lt;/a&gt;. Troubleshooting may get tricky. &lt;/li&gt;&#10;&lt;li&gt;For an AuthorizationPolicy to use &lt;a href="https://istio.io/latest/docs/ops/common-problems/security-issues/#make-sure-you-are-not-using-http-only-fields-on-tcp-ports"&gt;HTTP fields in rules&lt;/a&gt;, it first needs to identify the traffic as HTTP. To tell Authorization Policy to treat the traffic as HTTP, we need to understand &lt;a href="https://istio.io/latest/docs/ops/configuration/traffic-management/protocol-selection/"&gt;Protocol Selection&lt;/a&gt;. In a nutshell, we can name the port as http or http-* in the Service manifest of the workload. Otherwise all HTTP-based rules will be missed.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For troubleshooting, we can check authorization policies effective on a Pod with:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ istioctl x authz check orthanc-6c9679d8c7-2ttlf -n dev-orthweb&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This returns the effective policies but does not necessarily indicate which rule is matched when a request is denied or allowed. To find out further information, you will need to follow Istio &lt;a href="https://istio.io/latest/docs/ops/common-problems/security-issues/#ensure-proxies-enforce-policies-correctly"&gt;FAQ&lt;/a&gt; to set RBAC logging to debug, and then monitor the log in the istio-proxy sidecar.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Apart from HTTP fields, path, authenticated claims in JWT, Istio Authorization can also integrate with an &lt;a href="https://www.openpolicyagent.org/docs/latest/envoy-tutorial-istio/"&gt;Open Policy Agent&lt;/a&gt; (OPA) to drive &lt;a href="https://istio.io/latest/blog/2021/better-external-authz/"&gt;actions&lt;/a&gt;, in advanced &lt;a href="https://blog.styra.com/blog/authorize-better-istio-traffic-policies-with-opa-styra-das"&gt;use cases&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="bottom-line"&gt;Bottom line&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;While Istio itself does not perform user authentication, its support of JWT in RequestAuthentication allows a workload to integrate with external identity provider. This capability, along with creative use of claims in JWT, also empowers authorization capability. &lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2022/01/traffic-segmentation-on-kubernetes-platform/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Traffic Segmentation on Kubernetes Platform&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2022/02/istio-lab-authentication-and-authorization-in-jwt/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Istio Lab – Authentication and Authorization&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>