SPIFFE Meets OAuth: Federated Identity for Cloud Native Workloads - Yoshiyuki Tabata, Hitachi, Ltd.
About this talk
This talk explores the integration of SPIFFE and OAuth to establish Federated Identity across Cloud Native platforms where workloads and authorization decisions must span multiple trust domains. The speaker, Yoshiki Tabata, explains the common misconception that modern platforms operate under a single security boundary, highlighting that they often involve multiple Kubernetes clusters and clouds. The session outlines the challenges associated with user identity propagation and the importance of preserving authorization semantics across these domains. By focusing on the roles of SPIFFE signing authorities and OAuth authorization servers, the talk details a systematic approach to secure federated identity using token exchange and JWT-based authorization grants. The session is complemented by a demonstration using Keycloak and SPIRE to illustrate how user authorization can be safely managed across trust domains.
Full transcript
Okay, so hello everyone. Thank you for joining my session. My session is titled SPIFFE meets OAuth Federated Identity for Cloud Native Workload. I'd like to talk about how SPIFFE and OAuth can work together to build Federated Identity across modern Cloud Native platforms, especially when workloads and authorization decisions spans multiple trust domains. Here's today's agenda. First, I'll explain why modern platforms naturally end up with multiple security authorities,
or what I'll call trust And then, we'll look at what breaks when we try to federate identity across those domains, especially for user authorization. After that, I'll introduce the standard standard space approach to securely building Federated Identity across domains. And finally, I'll show demo using Keycloak and SPIRE. Before we dive in, let me briefly introduce myself. My name is Yoshiki Tabata, and I'm a senior OSS consultant
at Hitachi. I've spent over 10 years designing and consulting on secure payment platforms and identity systems both in enterprise and Cloud Native environments. And I'm active in the CNCF community, uh currently serving as a tech lead for tag security and compliance, and I'm also a long time long time contributor to Keycloak. So, let's start with the first topic. Modern platforms have multiple security authorities, or trust domains.
A common way of thinking is that a platform has a single security boundary. In reality, this is a myth. Modern applications span multiple Kubernetes clusters, multiple clouds, and often multiple organizations. On top of that, uh we now have tools and AI agents acting as first-class participants. Each of these environments performs its own authentication and authorization instead of relying on one central authority. In practice, uh both workload
identities and user authorization data are issued and evaluated independently in different parts of the system. SPIFFE signing authorities issue workload identities. And OAuth authorization servers issue tokens that contain scopes and claims. In this talk, I'll treat workload identity and user authorization as separate concerns. To keep things simple, I'll use a term trust domain. A trust domain is the boundary where identity and authorization decisions are made. Across
trust domains, nothing is trusted by default. OAuth and SPIFFE define trust domains slightly differently. OAuth focuses on the authorization server as the anchor of trust. SPIFFE focuses on the identity namespace and its issuing authority. In real platforms, however, there these are usually operated and governed together. So, for the rest of this talk, I'll intentionally treat them as the same boundary for simplicity. To reflect how systems are
commonly built and operated in practice. Now, let's move to the second topic. What breaks federated identity across trust domains? The good news is that workload identity federation already works quite well. With SPIFFE, trust is anchored in signing authorities and distributed via trust bundles. Workloads can authenticate each other across trust domains using SVIDs without shared secret. From a workload to workload authentication perspective, this problem is largely solved.
As shown in this figure, trust is established through SPIFFE trust bundles and workloads can verify each other's identities across The hard part is user identity and Preserving who a request request is for and what that user is allowed to do across multiple service hops is much harder. Simply forwarding an access token across trust domain is strongly discouraged. It breaks audience constraints and makes it unclear where authorization
decisions were actually made. The next idea people usually try is token exchange. At first glance, this seems reasonable. We can change the audience and issue a new token. But, this raises a fundamental question. Why should the old authorization server in trust B trust an access token that was issued in trust domain A? Token exchange by itself does not answer this question. So, what actually makes this trust
possible? This brings us to the third topic. How to securely build federated identity The OAuth identity and authorization chaining across domains draft uh defines a clear answer. By combining token exchange defined by defined in RFC 8693 and JWT-based authorization grants defined in RFC 7523, we can preserve both identity and authorization semantic semantics across trust domains, while allowing each trust domain to reissue authorization independently. This figure shows
the identity chaining flow. In this flow, a request crosses and is ultimately allowed to access an API in another trust domain. To do this, the flow uses two key elements, token exchange and JWT authorization grants. I'll explain how each of these works in the next slides. Let's look at token exchange. In the identity chaining flow, token exchange is used at the boundary between It takes an access
token issued in one trust domain and produces a JWT that is explicitly intended to be consumed by This step changes the audience and the token context and allows the request to cross the trust domain boundary. The output of token exchange is a JWT authorization grant. This JWT is a cryptographically verifiable statement issued by an authorization server. It allows the receiving authorization server to understand and verify the
upstream authorization decision before issuing its own access token. This is what enables authorization to be reissued when accessing an API in There's still one missing piece, identifying the actor that performs delegation. All of these flow require services to authenticate when requesting or exchanging tokens. Using static client secrets does not scale and introduces operational risk. We need a way to authenticate workloads securely without manual secret distribution. This
is where SPIFFE meets OAuth. The OAuth SPIFFE client authentication draft defines how SPIFFE credentials can be used for OAuth client authentication. Instead of shared secret, the authorization server verifies a cryptographically strong workload identity. This makes it cryptographically explicit which workload is performing delegation. In the figure, this is a step where the authorization server verifies the calling workload using its SVID. To securely support user delegation an authorization
server must understand three things. What is being delegated, for whom it is delegated, and which workload performed the Identity chaining covers the first two. SPIFFE-based client authentication covers the third. Together, they form a completely picture. Now, let's move on the demo. We use Keycloak as an authorization server, SPIRE as a SPIFFE signing First, let me briefly introduce Keycloak is an open source identity and access management solution.
It provides OAuth 2.0 authorization server capabilities and single sign-on features. And is currently an incubating project under the CNCF. Keycloak's major features include support for industry standards such as 2.0, Open ID Connect 1.0, SAML 2.0, and more. This include support for token exchange, JWT-based authorization grant, OAuth identity chaining, and OAuth specific client authentication. Authentication. Next, I'll briefly introduce SPIRE. SPIRE is an open source system for workload
identity management. It implements the SPIFFE standard and issue SVIDs to workloads. SPIRE is a graduated project under the In this demo, SPIRE provides the workload identities used for OAuth client authentication via JWT SVIDs. The goal of this demo is to show how user authorization can be safely propagated across trust domains. In particular, I want to highlight that authorization is not simply passed through, but reissued in each
trust domain. that all OAuth related requests are authenticated using SPIFFE SVIDs. This figure shows the overall system architecture. In this demo, we have two Kubernetes clusters, each representing each representing a separate trust domain, Minikube A and Minikube B. Each cluster runs Keycloak, SPIRE, and a sample API server. As a client, we simply use the curl command. This figure shows the details request The two SPIRE servers are
federated, which allows workload identities to be verified across trust domains. Keycloak Keycloak retrieves SPIFFE trust bundles from the SPIRE servers in order to verify the JWT SPIFFE used for OAuth client There's one important point here. Although the SPIRE server in both clusters are federated, for OAuth SPIFFE client authentication, an authorization server only trusts SPIFFE authority that are explicitly configured. Because of this, Keycloak B is configured with
two SPIFFE identity providers, one for the SPIRE server in Minikube A and one for the SPIRE server in Minikube The sample API server consists of two main parts. First, an Envoy authorization filter that validates incoming access tokens using token introspection. And second, another Envoy That No, another authorization filter is used for cross-domain calls when an API needs to call a service in a different trust domain, this
filter converts the access token into a token intended to intended for that domain. Following the OAuth identity and specification. Now, let's walk through the flow step by step. First, the client, which is car government, obtains an access token using the OAuth authorization code When the API request reaches the Envoy authorization filter, Envoy first fetches a JWT SVID from the Spire Spire agent, and then validates the received
access token using token introspection. At this time, JWT SVID is used for OAuth client when the API implementation determines that it needs to call an API in another the Envoy authorization filter again and exchanges the access token for a JWT Here as well, JWT SVID is used for Then, Envoy fetches a JWT SVID once more and uses the JWT authorization grant to obtain a new access token
intended for trust domain B. Client authentication is again performed JWT SVID. Now, in Minikube B, when the API request arrives, the Envoy authorization filter performs the same steps as in Minikube B Minikube A. It fetches It fetches a JWT SVID via the Spire agent, validates the access token using token introspection, and authenticate itself using SPI based you can you may notice that JOT SVID is fetched three
times in mini cube A. This is done purely for clarity. In a real system, JOT SVIDs can of course be cached and reused. Now let's move to the demo. these are the pods in mini cube A. They are Keycloak pods, SPIRE pods, and a sample API server pod. The sample API server pod includes a simple API server implementation, a token introspection implementation, and a token brokering implementation.
Now these are the pods in mini cube B. They are also Keycloak pods, and token introspect token introspection There is no token brokering implementation because sample API server B does not need to call another trust domain's API. And this is the API implementation in the sample API server. There are two APIs, the hello API and the Corbi API. The Haro API returns a Haro response. The Corby
API calls the Haro API on Next, let me explain the token introspection implementation. It first obtained an access token from API call. Then, obtains a jot S bit. it sends an introspection request with the jot S bit. let me explain the token brokering It first obtains an access token from the received API call. The first step is token exchange. It then obtains a jot S bit. And
sends a token exchange request using that jot S bit. After that, it receives a jot >> The second step is job authorization It obtains a job as bit and request using the job authorization grant. let me walk you through the Keycloak configuration. Realm A is hosted on Keycloak in Minikube A and Realm B is hosted on Keycloak in Minikube B. Realm A has two clients. One is
client app, which is used by the The other is SPIFFE TD1.theexample API server A. This is for sample API For the sample API server A client, we enabled And we set all SPIFFE grant authentication for it. We set a SPIFFE identity provider for And a SPIFFE ID as the federated subject. Next, we'll review the SPIFFE identity provider setting. This is for all SPIFFE grant authentication. We set
SPIFFE trust domain for it and OIDC JWKS endpoint for Realm A has two client scopes. Core B and API B. The Core B client scope is used to call the Corb API. The API B client scope is used to call the sample API server B's Each scope sets a value to the audience claim. The Corb scope sets the sample API server A as the audience claim and
the API B scope set Keycloak base token point as the audience claim. Next, let me explain the realm B Realm B has two clients. Why is spiffy td1.example.apiserverA? This is for sample API server A. The other is spiffy td2.example.apiserverB. This is for sample API server B. We enable OAuth spiffy client authentication for both client. For sample API server A, we use a spiffy identity provider. And for
sample API server B, we use a different spiffy Let's check identity provider settings. We have two identity provider settings for spiffy. One for trust domain A. The other for trust domain B. We set spiffy trust domain and OIDC JWKS endpoint for each. And there is one more identity provider setting. This is for Jot authorization grant. We set issuer and JWKS URL using realm A information. So, let's
start the demo. First, we obtain an original access token. We send authorization request to the Keycloak realm. We set the core base scope to the authorization request. When we send authorization request, the login screen is displayed. We sign in as a pre-registered user. Then, we can get an authorization code from the authorization response. So, let's send token request to obtain Now, we can get an access token.
So, let's inspect that access token. The obtained access token is like this. The client ID of sample API server A is set to the audience claim. This is because we set the core base scope in the authorization request, which allows sample API server A to perform token So, next, we call hello API with the We can see a hello response that sample API server A returns. We
are capturing the interaction, so let's look at the background interaction. We can see token introspection request in the capture. this is a job aspect that is used for all specific grant authentication. So, let's inspect the job aspect. the authorization server identifier for domain A set as the audience claim. And OIDC discovery issuer of trust domain A set as issuer claim. And the SPI ID of sample API
server A is set as the subject claim. Then we get the successful introspection response including active is true. As a result, the how API call succeeds. Then next, we call core B API. We can see a how response that sample API server B returns via sample API We are also capturing this interaction and we can see token exchange request in it. API B scope is set. And
here a job aspect is set. And as a result, we obtain a job So, let's inspect the job. the token and the point of realm B in the audience claim. This is because we set the API B scope in the token exchange request, which enables Keycloak B to issue an access token using a JWT authorization grant. And we can also see the token request of JWT authorization
grant in the capture. JWT SB tier two. Then we get a new access token for trust correctly. And the capture also shows that token introspection was performed again using a JWT SB. And we get the successful As a result, the core B API request So, what you've just seen is that user authorization is reissued in each trust domain and all OAuth interactions are authenticated using SPIFFE without
any static grant secrets. And that's the end of the demo. Thank you. Yeah, these are the key takeaway of this session. And that's all for today. Thank you very much for your time. And are there any questions? Hello. Hello. Oh, yeah. Uh was just wondering in Hitachi, do you actually manage uh what you presented today? Is that how guys manage it in terms of having a federated
um spire and then managing the rest in Keycloak? Uh, this is currently experimental phase, so we just uh how to say, confirm that this can be done. So, yeah, that is uh kind that kind of phase, so this is not uh yet uh adapted to the production environment or something like that. Thanks. Thank you. Hello. Uh, one question about uh I couldn't see that in Keycloak you
need to have policies in both of. If you have 10 trust domains, means that you need to have in 10 trust domains policy. What's the gain on this because what I can see is just STS that you're being using, nothing more. You could have uh OPA and that would be centralized when because the identity comes from the anyway, so you have the workload identity. If you have
a centralized policy uh behind your STS, Mhm. uh you can define and be much more scalable, I believe. Because in the end is just OIDC, right? Uh the uh in the MinKube, you can go just to the Keycloak STS OIDC to validate that token anyway. If you have a federation, means that you will allow, otherwise not. And also in the STS, when you ask the token exchange,
you can be blocking uh to create the access token because you have a policy engine behind. So, I I'm a bit lost there. Mhm. Is clear or I wonder I can understand what you say but maybe you mentioned the regarding OAuth and so that is kind of authorization. Yeah. And this is uh not to mention about that kind of authorization but only propagate the authorization and data
with authorization data and also worker ID. So yeah, we focus is only that kind of phase so that kind of aspect so you you can implement authorization using something like OAuth or Open FG so but yeah, today's demo we yeah, remove that kind technology. All right. Thank you. Okay, so time is up so thank you again and have a rest of your day.
More from this event
See all 436 talks →
Best of KubeCon + CloudNativeCon Amsterdam 2026
2:17
The Quiet Work of Forever: Sustaining Open Source Communities - O. Hope Amaechi-Okorie, JSON Schema
26:24
Evolving KServe: The Unified Model Inference Platform for Both Predictive and... F. Spolti & J. Lee
32:40
Preventing S3 Cost Storms: Applying Cortex’s Efficiency Lessons to I/O-Heav... A. Fishman-Lichterman
5:32