How Do I Actually Protect My Web Application With OAuth2? - Part One

2023-01-08

So you want to use OAuth2? You’ve heard all about this widely-used security protocol and want to use it to secure your application. You think “How do I protect my application with oauth2?” So you google “oauth2 tutorials ” and are met with a bunch of results, some of which look like what you’re after and some not so much. What gives?

Welp, in some cases, “oauth2” is a way to defer user login on their own apps to some other service. The classic examples being using Github or Google as an authorisation service. You log in to the demo app, and see your name in shiny HTML, but the logging in was all handled on Github, or Google. Which is all well and good, but really only a small part of the picture, and quite possibly not what you were looking for at all. But let’s for now assume that’s what you’re trying to do.

In this instance, you aren’t really protecting your application with OAuth2.

I’m going to try and clear this up, and hopefully shed light on a few things over the next few posts. Where I’m showing actual code, it’s going to be Java and Spring Boot because that’s my bread and butter, but none of this is specific to Java or Spring Boot.

Some Definitions

You will often see the phrase “OAuth2 is an authorisation standard, not an authentication one”. What does this mean? Well, put simply authorisation is all about what an actor is allowed to do, and authentication is all about who an actor is. You can probably already see a source of confusion here, because in the “log in with github” tutorials, who you are is the essence of what’s going on. So what’s going on?

Well, here are the basic actors in an oauth2 interaction:

  • resource owner

  • resource server

  • authorisation server

  • client application

Ok so in our “log in with github” app, who is what?

Well, the application itself if the client application. Pretty obvious.

The authorisation server is, of course, Github’s authorisation server. Or Google’s, or whoever.

The resource owner is the person who wants to log in to your application. To clarify, the “resource” is some data held on a resource server, and the resource owner is the entity whose data it is. That’s in the purest sense, and can’t be confounded by any T & Cs which state that data held on such and such’s server remains the property of the people providing the server. In OAuth2 terms, whoever the data is about, effectively, is the resource owner.

So what’s the resource server? Welp, in this case it’s Github, or Google, or whatever. Let’s forsake the usual interaction diagrams these articles always throw up at this point because they’re a bit obtuse. Let’s discuss the flow in terms of who is asking for what.

  1. The user visits your nice shiny client application and hits the “log in with github” button

At this point your client application will redirect the user to Github’s authorisation server. What’s actually happening here is that your client application is generating an authorisation request. It’s saying “Hey, I want to access a user’s data, can you ask them for their permission?” You will hear this referred to as an authorisation code flow, or auth code flow. You may also hear it referred to as an authorisation code grant, but it isn’t. That comes later. Grants are how clients get tokens, and that’s not happening yet. So. The authorisation request contains a few details, which may be posted as a form, or in a request as URL parameters. (Or pushed via back channels, but we’re not talking about that here). What data is in this? At an an absolute minumum it will be:

  • a client id - this identifies which oauth2 application was set up in Github by the person who built the client app

  • a redirect_uri - a uri in the client app to which the user will be returned once they’ve authorised the request. These are defined up-front in the oauth2 client so rogue requests can’t trivially steal auth codes by forging requests and having them sent back to their own server

The other largely vital bit, which technically aisre optional but typically never omitted is:

  • scopes - these are essentially free bits of text which represent something to the client application. They aren’t defined anywhere in the oauth2 spec, other than “they exist”. This is how resource servers decide whether or not to allow a particular operation

Upon receiving all of this, the auth server validates a bunch of things. Does this client exist? Is the requested redirect uri actually configured? That sort of thing.

So at this point the auth server knows that your client app is asking for permission to do things to resources, and where to send the user once they’ve given that permission.

  1. The user grants permission

If the user isn’t already logged in to github they’ll be asked to at this point. Now, the auth server will show them a consent page detailing which application is asking for access to their data, and what data it’s asking for. It knows this all from the client id and the scopes.

Once they click “yes” their browser will be redirected back to the client application. This redirect will contain a few query params. At the very least, it will have an authorisation code. This is a bit of text which represents the fact that the user has granted permission for your application to access their data.

Now the magic happens.

The client application has an authorisation code. That can be used for one thing only: it can be exchanged for an access token.

We will discuss access tokens shortly.

  1. The client application swaps the auth code for an access token

How this happens depends on the type of client. For now, we’re assuming a private client, with a secured back-end. That is, our server-side app which has been configured with oauth2 credentials.

Notice that when we requested authorisation, the client app said who it was, but did nothing to prove that. Obviously sending client secrets in a browser-based flow is a dumb idea. So it’s done in the back channel. At this point, the client app contacts the auth server on it’s token endpoint, and asks to exchange the auth code for an access token. This is the authorisation code grant. In most common cases, it does so by sending a client id, client secret and auth code in a POST request. It’s so common that it’s broadly undserstood to be how the auth code grant works but in fact, the client secret part is a client authentication method, not really part of the grant. Other methods exist, but we’re getting ahead of ourselves.

Assuming the client successfully authenticates itself with the auth server, the auth server then looks at the auth code, which it will have stored somewhere, against the authorisation sent in step 1. If it exists, and has been authorised by the user, and hasn’t expired, the auth server then issues an access token.

  1. The client application uses the access token to request a protected resource

This relates to the question above about what the resource server is in all of this. It’s Github itself, like I said. And the resource is /user, which is an info endpoint about the logged in user.

Now you’re probably sat there saying “Duh” because this isn’t new information at all. But if we think about the quuestion posed a the start of the article

How do I protect my application with oauth2?

Well we’ve done that, it seems, right? Well yes, and no. OAuth2 isn’t what’s protecting your application here. It’s a client app. It has been configured to defer login to another service, and to look to that service for user information. But make no mistake, in OAuth2 terms, the protected resource is not your application. To understand this, let’s look at the topic of access tokens.

What’s an access token?

Access tokens are bearer tokens. They’re bits of text which are passed by a user agent - the client application - along with a request for some data, to say to the server in question “Hey, I’m allowed to do this. See?”

So that’s what they are. But what are they not? Interestingly, the OAuth2 specs do not say anything about the following:

  • what format an access token has

  • how resource servers validate an access token

  • how resource servers decide whether or not to allow a request

These all all, quite deliberately, and explicitly beyond the - lol - scope of OAuth2. Sure. there are RFCs for JWT and so forth, but nothing in the OAuth2 RFC states any of this. The spec merely states that

Access tokens are credentials used to access protected resources. An access token is a string representing an authorization issued to the client.

That’s what an access token is. And in our discussion above we saw that our client application obtained an access token in step 3. It then went on to use it in step 4 to access a protected resource. And that resource was not our application. It was something which belonged to, and was hosted by, the same people who hosted the authorisation server we used.

That’s just nit-picking, isn’t it?

Well, yes and no. I happen to think there’s a huge amount of confusion on t’Internet about what OAuth2 is and how it works, and I think the conflation of using the protocols to enable deferred login with “securing with oauth2” is highly misleading. In the next post, we’ll actually secure an application with OAuth2 for reals, and examine how the two scenarios differ.

Just so I don’t feel left out, I’ve slapped together a basic Spring Boot application which is, as we’ve seen, very much not ‘protected by oauth2’. You can find it here