This blog post gets hands-on about access control, including everything from the difference between authentication and authorization, how this works in a couple of CNCF projects, and how to set it all up.
Specifically, we’ll talk about the basics of OpenID Connect (OIDC). In short OIDC makes it super easy to integrate your application with external services. Users can grant your application access to their data in a secure way. You’ve probably seen this in action: you get a prompt to log in to a new site with you Google account, and boom, you’re in.
One of the benefits of developing an application in a world that is already full of great services is that you don’t need to reinvent the wheel for everything. If you want to create a new frontend for a contacts list, there’s already a lot of services where users already stores their contacts. So why not just piggyback on those services and then you don’t need to worry about how to create that backend.
Alright now that you have created your super awesome frontend, how should we get the contacts from the external service?
- Should we ask the user to input the contact information to its friends again to our site?
- Kind of defeats the purpose, we didn’t want to implement the backend for this.
- Should our app ask for the credentials to the other site, so that we can log in as the user?
- This also gives us full access to the users account and we need to make sure to store the credentials very safe. Maybe not ideal from a security perspective.
- Should we use a standardized way of requesting the users data from that site, so that the user can give explicit consent and allow us to fetch only the users contacts?
- Now we’re talking!
I guess the answer is quite obvious.
With OIDC we can do exactly this. In a standardized way, a user can grant access to some data in one service to a completely separate service. And since it’s standardized, we can use any backing identity provider that supports OIDC. So by implementing this one protocol we can use multiple external services and allow users to log in via a service that they already trust. And if we have our own internal login service, we can utilize this standard so we’re able to develop and update the login service at its own speed, separate to the application itself.
To understand OIDC, we first need to iron out some often confusing concepts, authentication and authorization and get a good understanding of OAuth 2.0, since OIDC is just a small extension to OAuth.
Authentication and authorization
These two terms are often confused or used interchangeably, however as concepts and functions they are different and need to be understood as such.
If we start with authentication, this is simply the process of determining who a user is. This can be done in a lot of ways, like asking the user to provide something that only the user knows (like a password). Or maybe by something they have (like a One-Time Password – OTP – generated by their phone).
Next up is authorization, what are a user allowed to do. This could be that a developer are only allowed to deploy an application to staging, while an operator are allowed to promote it to production.
So once we know who a user is by authenticating them, we can determine what they are authorized to do. This sequence of events is important, we cannot decide what a user can do if we cannot be sure who the user is.
OAuth is a protocol that allows the user to delegate (authorize) a service to do certain actions on its behalf on other services. A great example of this is some thing most of you have seen. A button that says “Login with Google” or the consent screen “Allow app X to your use your contacts”.
This is all part of the OAuth Flow.
Lets have a look at a OAtuh Authorization Code flow.
User clicks on a button to give the super app access to some data that the user has stored in the other service
This sends some information to the foo service such as callback URI, scopes and client ID. Callback URI is used later when the flow is completed on the
foo.comside to redirect the user back to the super app. Scope is defining what the super app should get access to (public profile and contacts in this example). Finally client ID which is an ID that identifies the super app for
foo.comshows a consent screen for the user, so that the user can review the permissions given to super app, i.e., the scopes.
The user gets redirected to the callback URI with an authorization code from
foo.comfor the super app.
The backend of super app contacts
foo.comwith the authorization code and a client secret, that only
foo.comand the backend of super app knows, and asks for an access token.
If everything checks out (the client secret and authorization code are valid)
foo.comresponds with an access token.
The super app can now use the access token and ask for the users profile and contacts.
Again, if everything checks out (the token is valid and the scopes are correct)
foo.comcan now respond with the users’ data.
If you’ve paid very close attention, you might’ve noticed that there were not much talking about authentication in OAuth. This is because OAuth was developed to authorize services to access information on other services, not to authenticate.
OAuth became quite popular and started to be used for Single Sign-On (SSO), but since it was not initially designed for authentication, a lot of solutions were non-standardized and differed between services. This is what OIDC tries to solve.
This new standard adds a new token, namely the ID token. The ID token is a JSON web token (JWT) that holds, as the name implies, information about the identity of the user. And will allow the backend to get access to an ID token in addition to the access token which enables the application to authenticate that the user is who its claiming to be. And since the ID token is standardized and uses known fields, this will work independently of the identity provider (be it Google, Facebook or
To start a OIDC flow, you do exactly the same as previously mentioned with OAuth, with the difference that the scope needs to include at least
openid. With this scope set, the backend can use the authorization code to request an ID token and thus authenticate the user.
To summarize, with an ever growing web with different users using different services to save data on, it’s nice to have a standardized way of authenticating and authorizing users and services. By using OIDC we have a standardized way of exposing identification information and to grant services permission to use data from other services. This allows us to focus our developing time to add business value rather than integrating with external services and maintaining the integration.