 My name is Eddie Zaneski. I work for a company called ChainGuard, where we do software supply chain security for containers and all the cool Kubernetes stuff that's out there. But today we're going to talk about an awesome thing called OIDC. And of course, I had to come up with a fun title for this talk. So I can't sing it or else they will cease and desist the LF. So just sing the song in your head for a minute. So a quick bit about me. I'm a software engineer at ChainGuard. I mentioned that. You can find me on the internet at Eddie Zaneski. I live in Denver, Colorado, and I like to climb big mountains. I'm a maintainer for the Kubernetes and SigStore projects, so I work on Kube control and all things CLI, so you can at me. In SigStore, I work on a tool called GitSign, which we'll take a look at in a bit. And I am not a cryptographer or a security engineer, just throwing that out there, right? So take all of your advice as it will. And again, I need to do a disclaimer. I know nothing about this except that it's fun and cool, and I've spent a lot of time playing with it. But again, don't take advice from me in a production world. So the first thing we have to talk about is, I'm sure I've seen slides in almost every talk for this, is authentication versus authorization. A folks familiar with the difference here, we don't spend too long on it. Okay, we'll mention it. So authentication is generally who you are. Authorization is what you're allowed to do. Akta has some really good docs on this, if you want to check those out. And I have a link to the slides at the end. But it's that specification. So we usually call this authN and authC. And so rewind back to 2007. This is also from an Akta blog post. But this was how Yelp would check your contact list for if your friends were also signed into Yelp, right? This is before the days of OAuth, and you would check your little email service provider, give them your username and password, which they would just make requests directly and scrape a bunch of stuff, probably. We've come away since then. There's obviously a problem of doing it this way. And so that's what led us to something we like to call OAuth, right? And so you've all probably used OAuth before, right? I'm assuming everyone has. You've signed in with the service provider before. So here I am signing in with my Google account. And you can see the scopes and all the things that this app is requesting. So access to my calendar. All these things are just scopes in a docs file that this app pulled in and said, I would like these permissions on behalf of this user. Same thing here with Spotify and GitHub. Spotify is asking for permission to read my email and my play history in there. And GitHub can request all the orgs that I have access to, right? So basic stuff that we've all worked through. OAuth, to me, was scary for a long time, was one of those things that you touched as a kid in a hackathon and didn't really want to understand or have to deal with again. But it's really not that bad. But the real thing behind OAuth is that it's designed for authorization. It was not designed for authentication, which is what a lot of providers and a lot of websites have used it for. The idea is that there is some resource provider that has a resource, typically owned by another user. And OAuth allows you to access that resource on behalf of that user, right? So this user grants you permissions to do that. There's different flows as part of OAuth, and some of these I didn't actually know about till I started researching it. Authorization flows, the one that you're most familiar with. This is where you go through that consent form where you can get a code back. And that code either has like a refresh token that can be used to refresh and make long-lived requests. Client credentials are machine to machine identity. So this is where your OAuth application would authenticate just to the API and get a token it can use, so not accessing user's data. Device flow, so if you've ever had to type a code in from a thing on the command line which says open this thing and type this code in. Or if you've signed into Netflix or Hulu on your Google TV or something and it shows you the code that you can go to netflix.com slash activate and type in, that's actually the OAuth device flow behind the scenes. And then there's the implicit flow which is for client side applications. This is typically discouraged because it's all client side. This is where you don't have secrets that you can keep from the user. So the industry is trying to move away from this implicit flow. All these OAuths requests have these things called scopes. They are just arbitrary strings to the provider that they make up whatever the hell they want and they represent certain permissions. So the user read email is from Spotify. That's one of the scopes you can request. If you're using octa, you can octa.users.read as a scope. Or if you're using Google and the Google Sheets API, they just throw a URL at you. So completely arbitrary things that the provider decides on. And if we look at this, these are the Google Docs. They have all of their scopes in a single doc which is really cool. So you can come in and find the service that you want. See the different scope permissions that you can get. You can access read-only, fitness data. So it's pretty cool that it's all in there. And then a OAuth URL, so that authorization flow that you've all probably used at some point. This is what one of those URLs looks like. So this is kind of the magic behind the scenes is your web server would construct this URL. You can see that there's the path in there OAuth 2 slash V2 auth client ID, which is kind of a username for your OAuth client. It sticks in a redirect URI. That's one of the other confusing things when you're a kid at a hackathon, you don't understand how it works. So that's kind of just where Google is going to redirect the request with your actual code that can be pulled out. And response type code, some other scopes in there, right? So if I click this link, you'll see that it will bring me to a very similar pop-up. I have to pick a Google account, and then I have to allow permission to use my Sheets app. Cool, right? And so if I click allow, this is going to 404, because it's redirecting me to a web server that's not actually listening. But in the URI here, there is a code that my web app would grab and trade for an actual token. So kind of the back end part of this secret state. Cool, makes sense. You can't talk about OAuth without talking about OAuth. And so if you ever go and create an OAuth app somewhere, this is creating one in Okta, you get that client ID, which is kind of your username and identifies your client. That's public. And then the client secret, which is locked and kept on the back end. That's what you would use along with that front-end browser code to trade for an actual access token. And you can see here, there's some of those callback URIs that you'd have to specify. So this would be your production web application callback. This is a documentation of what that flow looks like. Also from Okta, they have some really great docs. Glance over it pretty quickly. You hit that authorized endpoint, redirects you back. You get that consent, which is the pop-up I showed. You got that authorization code. Server will post that to token with your secret from the back end. You'll get that access token back and refresh token, so you can renew it once it expires. And then you can use that access token to make API requests on behalf of that user. So that's what you would save. Pretty straightforward. And so I have a quick demo of that. So I have a Spotify Webflow here. And any Go developers in the room? Yeah, quite a few. Go has some really great, unique OAuth libraries that are part of the standard library for the most part. I got secrets in here that I'm going to delete afterwards, so whatever. And so it's pretty simple. I hit that OAuth URL on my slash login. I hit that redirect. It's going to redirect you to a URL that's constructed from my provider. That's going to take me to the browser. Browsers, after I consent, is going to send me to this OAuth callback one. I grab the code out of the URL parameters. Do some error checking. Call this magic exchange method, which is what takes my secret and posts it for that token. Cool, cool. And then I'm hitting the slash me endpoint on Spotify, right? So very straightforward. So I'm just going to run this. So we'll hit local host. Log in. Right, there's that prompt. I'm going to agree and allow. And then we get my profile information. Cool. Very straightforward, right? So that's a regular OAuth flow. That's a regular OAuth application. So what is OIDC? That's the flow we went through. Just a quick diagram of it if you want to look later in the slides. Oh, one more thing. Git sign demo, right? So Git sign is a project from Sigstore, which is artifact signing. And so I have a Git repo here that I can commit. And it's going to use Git sign, which will sign my Git commit with Sigstore. And this is something you'll see in a lot of developer tools, where you'll do something. It'll pop out a URL, open your browser. What it's actually doing is running a local web server listening for that callback, which is really cool. So you use OAuth a ton. You might not realize it. So I'm going to authorize it at Google. It's going to, well, that's a new page. That's nice. And it's going to give it back to Git sign, sign that commit. Cool, right? GCloud does this, too, if you log in with the GCloud CLI. A lot of tools use this mechanism to get a code to a CLI tool from your browser. Very neat. And then, so there's issues with OAuth. It was, again, originally designed for authorization, not authentication, which is what a lot of people started using it for. I don't actually want to access resources on your behalf. I just want to know who you are and give you an easy way to log into my application. There are no standard scopes, as I showed before, right? Arbitrary strings. You don't know what one provider is going to do. Some have different crazy stuff in there. There was no standard who am I endpoint, right? That endpoint that I hit from Spotify was like slash api slash v1 slash me. There was no standard way for you to know who you had an API token for. And again, these are long-lived credentials, right? So the root of all evil, we all know this. We don't want to provision these things and have them sitting around, right? I already leaked my Spotify credentials by showing you that file. And so discovery is another mechanism in here. There is actually OAuth discovery, which I'll show you a bit later, but that matters a bit more with OIDC. Spotify has this endpoint that you can hit. It's a well-known OAuth authorization server, and this kind of tells you about their OAuth provider, which is neat. This isn't used as much, honestly, as it is in the OIDC world, but here you can know that's where you send a user to get a token. And there's a slash token endpoint somewhere here that tells you where you would post this to. So a bit of an afterthought. Cool. So enter OIDC. So OIDC stands for OpenID Connect. OpenID was another form of authentication that kind of got shut down by a lot of people. It was a way to have federated logins, but it kind of was replaced with OAuth and then brought back again through OIDC. It is an extension to OAuth, so it's a thin layer built on top of OAuth. It uses JSON web tokens. It has this concept of an ID token. So along with that access token you would get back, you would get back an ID token. And this is supposed to contain information about the user by itself as a standalone. It also introduced a standard user info endpoint. So everything that's OIDC compatible should have a user info endpoint that you can hit with that access token to get back that identity token and information about a user. It comes with a standard set of scopes. OpenID is the scope that you introduced. That's pretty much the only magic behind getting an OIDC token from an OAuth provider usually is you have to stick in the OpenID scope, the profile scope. And I mentioned discovery before. So OIDC has well-known discovery. Which I'll show in a second. Quick breakdown of JSON web tokens you may have seen before, usually called Jots. There's three big sections in it. It has a header. The header contains the algorithm, the key ID, what it was signed with, all that jazz. It has a section called payloads or claims. This is actually what is assigned. So this holds the information about the token, who it's for, who created it, how long it lasts for. And then there's that signature, which you can see together and kind of sign with an RSA signature. The claims section. There's some pretty standard claims that we use everywhere. Subject or sub, this is the client ID that you would usually use for OAuth. So this identifies who this token is for, who owns this token. The audience field, this is who the token is intended for, so that OAuth server. So who should accept this token? The issuer. This is who minted this token, so accounts.google.com, accounts.spotify.com. Issue that timestamp. Expire this timestamp. And then this jsonwebtoken.io site is pretty great. So you can come in here and paste the token in and see the breakdown of what it looks like to code it. So you can see the algorithm that this is signed with, the type of token, the subject, arbitrary field called name, and then when this was issued. And then you can verify the signature. So, cool. That's the jsonwebtoken. There's also step CLI, which is rad. If you want to do this on the command line, you can use a step CLI, the folks over at step. Crypto, jsonwebtoken, and inspect, insecure, because we're not going to verify the signature, and it will print out the same thing. So those are tools that we're going to use. And then I have an octa demo to show what this looks like. So I stood up a development environment for octa, very straightforward stuff. So this is a full proper two-spec OIDC flow, right? So this shows the OAuth endpoint URL. It's got my client ID in there, response type, blah, blah, blah, callback. And then the scopes. It shows profile, open ID, and email. Start up a server. Same thing as before. Callback at that code. Trade it out. More credentials to leak. Grab the access token. Grab the ID token, right? So you get both back when you have these in your scopes. And then I'm making a request to that standard user info endpoint. So let's run that. And we're going to go localhost login. It's got a semi to octa. This is just a development instance. So there's nothing actually in here. Don't actually leak this one. This is important. There we go. Sign in. Callback. Cool. So here we have the access token that came back. So we can take a look at that. Octa actually uses a JSON Web token as their access token. There's nothing wrong with doing that. It's just using that as a mechanism instead of sending you some arbitrary API key. So we can pop that in here. See the key ID that it was signed with. The algorithm. Information about me. Here's the issuer. The scopes that are on it. The subject. That's my email. Cool. And then we can look at the actual ID token that came back. Which we'll have a bit more. I sat down and compared the two. Again, octa is a special case where they used the JSON Web token for both. But this has a bit more information about me as a person. So it has my preferred user name. I was trying to get like my city to identify me as a user. Same kind of stuff. There's the audience. That's my user ID in octa. The subject. So, cool. And then below was that user info endpoint. So pretty much the same stuff that's in there. Just turned into a JSON Web token. See this one has more info like my time zone stuff. So, oh IDC. I mentioned this concept of discovery. This is where it's really cool and how it's pretty compatible for anybody. This is where you can go check out the open ID configuration for any sort of provider. It's just public information. I like showing Googles. So Google shows their issuer. Authorization endpoint. Same thing with the OAuth I showed before. But it's really cool. So I'm going to go ahead and I'm going to go ahead and I'm going to go ahead and change my idea I'll use a bit more but it's used a lot more in OIDC so if you go to attach this to a service or provider and say trust my identity provider it's going to grab this and take a finger print of it and save and register if anything changes. A couple other bits. There's a token endpoint. The claims that it supports. Some providers don't do this and some don't respect the claims in the first types that your hands out, JSON web baritoken, right, all that stuff. Where's the field I'm actually looking for? Ah, here you go. The JSON web keyset ID, right? So that's the next field that's in here. So the JWKS, this is what the, basically the public key ring of that provider. So it has to tell inside of that well-known configuration point where the consumer should go to get their keys, and that's how it does it. So Google's is actually at this URL, same kind of deal. This is, again, a JSON web keyset as a key type, key ID. These are the parameters to generating that key, right? The exponent, all that jazz. These rotate, there's a whole bunch of magic and procedure behind how this rotates. You're supposed to keep the old one around for a little bit, so I think that there's two, yeah, there's two in here. So here's the one that they're probably using right now to sign, and then here's the one that's being deprecated and phased out. All this is tied under an umbrella called Josie or Jose. JSON object signing and encryption, so if you want to know more about how this works, check out those RFCs and specs. I actually had to implement a few of them from scratch in Python, it was miserable. But that's where all this is umbrella under. And so why do we care about OIDC? And so this is the point of the talk, is this concept of federation. Are folks familiar with federation? Yeah, maybe a little bit. So federation is just kind of like a magic trust relationship between an issuer and a resource provider, right? Google provides a service called Workload Identity Federation, that's rad. But what it really is, is just like a trusted person sitting in a cardboard box that takes a token from this one and hands it out this side. There's no like actual, you know, spec or RFC behind any of this works. It could be solved by a person in a box. There's no long-lived credentials with federation, which is great. And so this would be, I'll show you a demo in a second, where your CICD provider injects a unique OIDC token into every pipeline run. That can be traded out for a GCP credential, an AWS credential. So you no longer have to provision those long-lived identities, store them as environment secrets inside of your CI provider, and have to worry about them leaking or anything. There's no certs. It's just trust. Usually the provider, what will happen is it will take that OIDC token, hand it to the trusted source, so go to Google Cloud, and usually assume a roller identity. So this is tied to an actual service account. It's super auditable. So all of this is auditable. You can track who assumed that identity, where it came from. In the case of GitHub, when they issue tokens, you know the pipeline ID that ran. You know the repo, the time. What caused that pipeline to start? Where can you do this, and where will you find these? So I mentioned CI CD, GitHub GitLab, CircleCI, I think Semaphore, a bunch of different providers. So this is becoming way more prominent now. Injecting this token into every run, which is cool. There's a Jenkins plugin, if you're using Jenkins to get OIDC tokens issued. Cloud resources will accept this. I mentioned that. GCP, AWS, Azure, they all do some sort of workload identity federation. Google got lucky and claimed the service name that makes a ton of sense. But calling a workload identity federation is like a good umbrella name there. Kubernetes clusters do this. So I have a Kubernetes demo somewhere. I showed you SigStore signing. With SigStore signing, I showed doing it manually, and I had to go through the OAuth flow. But if you're doing, say, a GitHub action to release a binary and you want to sign a binary as part of an action pipeline, you can take that OIDC token, trade that for a SigStore signing certificate without any pop-up or OAuth flow, and it gets tied back to that actual workload. So you know the provenance of where that artifact was built, and all that's signed in a testable. So this is a GitHub action token. This is injected. If you set a, I think it's like token write true or something in your permissions for your GitHub manifest. Same kind of deal. You can see the subject here. That's going to identify the repo, the environment. Actions has a concept of different environments that you can use for different secrets. Audience, who this is, the repo is for, the ref of that thing. So very unique stuff to a CI provider, but they can all get put in there. Actor ID, bunch of other stuff. So very cool. The issuer is issuer or tokens.actions.githubusercontin.com. So your cloud would trust this. So I have a GitHub demo. Find it. Does anyone know how long I have? Let's see. I have no person or timer. Thank you. Perfect timing, actually. So I can come into my GitHub here for this project, show you what this manifest looks like. So this is a manual dispatch. There's that permission. So this is how you get the token injected. I just have that job, blah, blah, blah, runs on. So this is going to check out the repo. It's going to set up that Google off. So that's actually the magic thing that happens here. So I have a trust relationship set up in GCP that it's trusting this particular pipeline and this action. So it has the mapping there. And so this action will set up and trade out for a token, which I have a manual workflow for, too. And then it has the service account that it's assuming inside of GCP. And then it's going to run that G Cloud CLI and just kind of list out who I am. So let's kick that off. Run workflow. Let's see. It's going to start. So you can imagine this being you tagged a release or you cut a release at a certain commit and now you want it to build binary, sign them, and ship them. Setting up G Cloud, this is the longest step. It's got to download the binary. It's already set up the credentials. You can see it created credentials file at this path. So it already has the token exchanged. It's going to run that who am I? There we go. So you can see that it is actually assumed that service account. So that works. Cool. So that's inside of your CI provider. Kubernetes demo real quick. So if I do something like K get raw, QPCTL get raw, slash dot well known, open ID configuration. So every Kubernetes cluster that's in a managed service will have an OIDC provider that's usually backed by their IM. So GCP will tie this to service accounts inside of IM there. Same thing with EKS. You can do this in your own clusters too. You can provide your own OIDC provider or Kubernetes will run one on your behalf and tie it. But you can see here is the issuer for my cluster. It's tied to a specific project in a certain region and the cluster name. There's where you could find the key store for my cluster. Let's see. Is it public? That's a private IP. What's supported, right? So every Kubernetes cluster has this, which is really cool. So if you have your workload run in Tecton actually does this. Does anyone use Tecton for CI CD? Tecton is a open source project for CI CD that's Kubernetes native. And it will use this provider to do all of your federating and signing and testing. So it's really cool. So that's with Kubernetes. So I've been calling this OIDC Wave 2. Everything I just showed you, that GitHub action pipeline, my Kubernetes cluster. None of those are actually attached to OAuth at all. And I'm sure there's someone out there who will have a better explanation or definition of what this is. But for my naive self, this is an extension beyond what OIDC was intended for. This machine-to-machine authentication. GitHub actions was really the first provider to do this, where they are injecting these tokens into those runs. There is no OAuth involved at all with how they mint those tokens. It is a machine identity, right? This is where we're all moving towards. I don't think we'll have time to talk about Spiffy Inspire, but the idea of having your trust relationship between a identity provider and a resource consumer or a producer is just that implicit trust. So there's no long-lived credentials on either side. So it's just interesting to see how this has evolved beyond the need for having OAuth attached to it. And at the end of the day, who do you want signing your releases? Do you want your releases signed to a eddie at chainguard.dev that may have been signed like three years ago, but I haven't worked there for two years. So is that still a valid release or signature? When I leave, do you have to rotate out who's signing it? So having it signed by a service account, a system identity, is a much better approach, right? You can have it signed by release at kubernetes.io. With all things, you need to trust but verify. The fun thing is that anyone can actually mint a valid OIDC token. There's nothing that really says otherwise. The claims actually matter that are inside there. You can put whatever you want in that audience and subject field. Issue subject audience are really important ones that you want to make sure are verifiable. What happens if there was a service who were to issue tokens with whatever? I went and built one for fun. I called it just trust me.dev. And so this is a service that you should not use for anything real, but it's fun. And so it will just mint tokens with whatever you pass in as the URL parameters. So here we have a token that, let me toss this debug parameter in the URL. Right, so I have issued a token that is, the audience is sts.amazonaws.com. Cool. Well, let's see. What if we change the issuer? So let's say this was issued by accounts.google.com. Cool. Still a valid token. Well, what if I also have the subject in here of something like admin at cncf.io? Right. So if I hand this, this is a valid token, you can totally verify the signature. If I hand this off to a provider, what's to stop them from trusting it? What's to stop them from actually knowing this is forged? Well, thankfully, smart people have figured out how to prevent this. Though there are definitely services out there that are not checking the fields and they're just getting a valid token with the issuer and they're definitely not verifying that it's who it's for or where it's coming from. And so I had a quick AWS demo where I can do AWS simple token service, a zoom role with web identity. I have a, a, a role in here, session name, duration, cool. And then I just have to pop in this web identity. So let's say, go back, token, we give it the subject of demo, grab this, I think that's all I need for it to work. No, see, it knows. So it's missing a required claim audience. So let's toss in an audience in there. Incorrect audience token. Yep. audience is supposed to be demo, what did I put? Oh, Eddie. demo, there we go. Try it again. Right. So I have a trust relationship set up. It's just going to trust whatever I said. In this case, this, this is what it's supposed to happen. But the, the way that the cloud provider gets around this is you actually have to specify outside of the token who your audience is for, right? So it's just another step here. The protection it gets. So verify token and claims. You want to make sure you're actually matching that issuer to the issuer's key, right? So the key shouldn't match if you do it in correct way. You want to pass the audience alongside the token. So Google Cloud does it cool too. They have the docs right here. The token Amazon's the same way. So you have the token here that you would pass. But you also specify the audience as well. So the audience and the request should match what's in the token. And that's how you can ensure that it's for the correct project. Dex is a front end for other apps. I don't think I have much time to talk about it. But if you go through that Sigstore OAuth flow and where I showed signing that git commit, that's actually Dex that's running behind the scenes. So Dex can federate those different identity providers to a different application so it can handle all the attaching and plugging in. Spiffy spires for machine identities. I'm not going to try to explain that because I'm running out of time. The one last thing I wanted to show was how this workload federation works behind the scenes. So this is grabbing an OIDC token. This is a valid one for my service. So GCP is set up to trust me. And so if we look at what that token looks like real quick. So you can see that the audience is my GCP identity pool that I set up, issuer and the subject. Google actually takes that and it gets traded for a federated access token. This is kind of like a secret proprietary. This basically marshals into a protobuf. And so your service gets back this identity token after it talks to Google. You have to take that then and you have to take hit another end point that says, Hey, I have a federated identity token. Can I please have an access token on behalf of this user? So Google will go and mint that. And that's actually the token that you can use to hit that who am I endpoint. So that's what this is showing. So with that, I am most likely out of time. If you want the slides, you can take a picture of that. That's the QR code for the slides. And I'd be happy to answer any questions up here. So thanks for listening to me ramble about OIDC.