-
Notifications
You must be signed in to change notification settings - Fork 1
OAuth2 Flows
In this chapter let's learn about the various workflow in much more detail and how they work and in which situations you want to use them. So we, talk about the authorization flow
, the implicit flow
, the resource owner credential flow
and the client credential flow
.
Now the most classic flow of them all is the so called authorization code flow
and the idea here is that we are talking about clients which are server rendered web applications.
Authorization Code Flow
(Web Application Clients)
Resource Owner ---> Web Application(Client) ---> Resource Server
Meaning, you're going, you're opening the browser going to some web application, and the web application wants to access a resource that you own but you don't want to give that the application the password to that resource, while you want to use own of two retrieve an access token for that.
Now the way this works is so that web application can gain access to the resource that you own, it does a redirect to the so called authorization server
.
Resource Owner Web Application(Client)
|
| GET /authorize?
| client_id=webapp&
v scope=resource&
redirect_uri=https://webapp/cb&
Authorization response_type=code&
Server state=123
And you see here that this redirect here we're using a certain query string format here so you can see there's this thing called the client ID. That is basically the name of the client that's trying to access the results and clients are needed to be registered at your authorized server so there's a step zero, if you want. Where the client needs to register with the authorization server and from that point on it can make these interactions, so it is hey, I am the web app. I need, I want to gain access to a resource called resource, that's the scope parameter here, and when you are done call me back on this redirect line that I am providing here And what this authorizations, whatever is sent back on, on that callback URI is a so-called authorization code, as we will see in a second. And there's also the state parameter, which is optional, but very recommended. And that it's basically that the client comes up with a random number. Stores that locally so that when the call back comes back under redirect uri the authorization server should send the state back so it can correlate the, the response so you can make sure that this this call back is really a result of the request you're making, so that's basically for a cross side request for protection.
So, what happens now, when this GET request arrives in your authorization server, is a number of things. The first of all, if the user is not already authenticated with the authorization server, the user has to authenticate because otherwise the authorization server cannot know who the resource owner is, and cannot make any security decisions.
So I'm using the Google, the Google implementation here as an example, because I think the Google implementation is one of the best OAuth implementations out there right now. So the, the user has to authenticate and the next thing is the so-called consent screen.
So, what you see here is now, you see which, which client here, which application is making that request. And that in, in that case is, is Feedly which is a blog reader.
And this blog reader wants to have access a, basic information about the, the user on Google like his name and URL and photo. And also access to his Google Reader data so it can show the blogs, the blogs and the, the posts. So the content screen really says like okay Dominique listen, here's an application called Feedly and it needs access to that data. Do you want to allow that access? And then you press allow, or no thanks. Okay?
And when you press allow, then basically the authorization server will take care of giving this access token to Feedly so it can access my, my blogs. That's another example of a content screen. That's how Twitter, for example, does it. They combine the log on and the consent into a single screen. And you can also see that the application says, this will be able to read your tweets and see who you follow and update your profile, but it will not be able to see your Twitter password. So that's the whole idea, you give some client access to a resource you own without disclosing your password. That's a, that's an exam from Evernote, for example which is, is quite a nice content screen I think, or at least I like this feature that you can by default limit access to a certain amount of time. So for example in that scenario here you can say like if this application only access for one year, or shorter or longer. So, even when I forget about it, access, for in that, in that case BubbleBrowser, to my Evernote notes, will expire automatically as opposed to vice versa where you need to you know, manually rather revoke the access than renew it.
So the consent screen is really, really important if you are building an authorization server yourself, you should put a lot of thought into your consent screen. It should be really obvious what this application is asking for. Which types of permissions and also who the application is thought as you can read here on, on this URL. That, that there's a guy who, who talks about the importance of consent screens and he used that Photoshopped example here, and I also showed that consent screen to a number of people.
And it turns out that, some of them just take a raw because they kind of think they know what they are clicking to but, when you're closely reading the text here you might not really agree with what the application is doing. So that's a non-technical problem but if you are building more authorization servers you should make these consent screens really easy to understand for the users because actually when you click Allow, that's what you get. The authorization server will make it happen to give that application the, the requested permissions. Okay.
Resource Owner ---------------------------> Web Application(Client)
GET /cb?code=xyz&state=123
Authorization Server
So let's assume the user has clicked allow, so then the authorization server will redirect back to the callback uri, presenting the application with a so called authorization code. That is not the actual access token, we first send an authorization code, so on the client so because that authorization code gets transmitted via the user agent. We don't, transmit the access token itself so it, it, doesn't leak over, over the user agent, but only a so called code. And, and you can see that the state parameter is, is echoed back so that the client application can do it's anti cross-fire request measurement.
Resource Owner Web Application
|
| POST /token
V Authorization: Basic(client_id: secret)
Authorization grant_type=authorization_code&
Server authorization_code=xyz&
redirect_uri=https://webapp/cb
For the last step here is that the client application, now has a direct communication between the Authorization Server and the Client Application. The Client Application has to authenticate with the Authorization Server, that, that credentials, get exchanged as part of the initial registration process I talked about earlier. And then the, the client application sends the authorization code to the authorization server that we've just seen. And then when the authorization server's happy with the, with the code, and makes sure that this is actually the application that requested authorization in the first place, and all these, you know, extra security checks that you have to implement. Then, the authorization server sends back a token response.
{
"access_token" : "abc",
"expires_in" : "3600",
"token_type" : "Bearer",
"refresh_token" : "xyz"
}
And as you can see in this token response there are two types of tokens. One is the so called "access token", that's a short-lived token that expires in, in this case, one hour, that's expires in it's the number of seconds this thing is good for. And a longer lived token
, the so called refresh token
. And the refresh token is optional and I'll tell about it in a second but with the access token now, the application, the client, can now access the resource server, so we see that here in this step.
{
"access_token" : "abc", Resource Owner Web Application(Client) <---------- Authorization Server
"expires_in" : "3600",
"token_type" : "Bearer",
"refresh_token" : "xyz"
}
But as you see as you can see the access token is short lived, one hour here. So what does the application do when the access token has expired? Well, it could start that round trip again to the authorization server and again the, the user gives confidence. One, that would be one option. Or there's this content of a so called refresh token.And the refresh token the idea is that this is a long lift token that the application can use to request new access tokens.
So what would an access token look like? Well typically it would first of all, it obviously, contains things like the expiration date and the signature. And the issuer name and so on, but it also typically contains things about the resource owner identifier for, like, who is the human on which we have that access is taken care of.
Typically you would have a client identifier inside of that so you know which client actually does the request. You have something called scopes. So meaning for example you can say like in the scope parameter on the way in, you can say like I need read access to my Twitter feed or write access or I want to modify the profile, things like that.
So again the, the user has consented to these permissions, so you put them in the token so the resource server actually knows the, the things that the user has allowed. And you, you can put in anything else that makes sense to your application.
Resource Owner Web Application(Client) --------> Authorization Server
POST /token
Authorization: Basic (client_id:secret)
grant_type=refresh_token&
refresh_token=xyz
And as I said, if that access token has expired and you enabled refresh tokens for that client, then the client can basically post the refresh token to the authorization server and can get back a new access token.
So that's, that's a way how you can extend access over the short-lived access token lifetime. And also grant the application offline access to the, to the resource, like, for example, you know, the user shuts down the browser. But the web application in the background does some sort of batch processing on top of the resource data. So it can you know, maintain access to the result server. It can use the Refresh Token feature to get new tokens.
Once the application has a Refresh Token, it basically can access the resource forever and that's also something we typically don't want.
Resource Owner Web Application(Client) --------> Authorization Server
POST /token
Authorization: Basic (client_id: secret)
grant_type=refresh_token&
refresh_token=xyz
So, what many authorization servers implement is kind of a self-service way, how the, the resource owner can revoke access again for the client application. So let's say, for example, in that concrete example here, I gave, the Adobe PhotoShop Light Room application access to my Flickr album. And if I, at some point decided that I don't want it anymore, that PhotoShop can post to my Flickr album, I can click the remove permission button here which technically means that the Refresh Token gets deleted from Flickr's database. So, after now the short lived access token has expired, there's no way to get a new token for LightRoom in that case. And, that, that is very common practice, so that is for example how it looks at Dropbox. So, also Dropbox has this concept of you know, connecting applications that have access to the Dropbox resource on your behalf yeah. Or that is how it looks with Microsoft where you can give certain applications access to to your Microsoft account data like yeah, applications, for example.
Okay, so that's, the code flow, again, this is basically the way how server rendered web applications would use OAuth to orchestrate, the token request and token usage.
It's designed for server applications. And that application is typically the client, which is the web application has a user name and a password that he, that it can use to authenticate on the authorization server that it can use to refresh the access token once the, the short-lived token has expired.
From a security point of view this is really good because the access token never is leaked to the client or the user agent. And everything is happening on a potentially tightly secured sever environment. So this is the so called code flow and very commonly used in web applications.
Let me show you an example of the OAuth code flow. So this here is a client a web client, and you can see here's a link to start an authorization handshake. And that is really It, to the, the round trip to the authorize endpoint.
So you can see when you look at the bottom, the URL there's basically the client_id,
https://local/issue/oauth2/authorize?client_id=codeflowclient&scope=urn:webapisecurity&redirect_uri=https://localhost:4403/callback&response_type=code
just the scope of the resource we're trying to access just to redirect you And the response type equals code.
So when I click that I get from the authorization server. Since I haven't logged on yet, I need to authenticate, and after I successfully authenticated there's the Consent Screen. So you can see whether such like that this thing called the code flow client is requesting resource on your behalf. The resource name is that API security sample. And this here the client will be able to access the resource even when you're offline basically gives the hint that here are refresh tokens used, so I can say allow access.
Now, the authorization server sends me back the authorization code into the client, and now I can exchange the authorization code with the actual access token. Okay.
So you can see now that we have the access token, we, see the expiration and here, here's the refresh token. So now, as the last step, I can call the service, and basically the service echoes back the contents of the token, which has things like the audience, the issuer. Not before expiration, name, authentication method and so on.
So the, the contents of the token is really up to you, in this case. There's might be more in it, then it might be actually, you know necessary. We can also click the renew token here, and then we making the round trip to the authorization servers, sending the refresh token and getting back a new access token, and in that case also a new refresh token. That's again, up to the implementation of the authorization server, if you make the refresh token one time, use only, or can reuse it.
If you look at the page source you can also see here's the, the, actual access token, so let's copy that. There's a useful website and I will put the link into the reference appendix. But, this allows to decode so when you want to, when you play around with them, it's sometimes quite useful that you can look inside the tokens and they do the decoding for you. And you can see that basically here we have the same claims that our resource service echoed back.
So, another thing I want to show you is, let's go to the authorization server here, go to the administration, and then go to users and here, you, or, at least, the admin can see are there any tokens, that can be revoked. So I can go to the code slow sample and search and indeed, there are some tokens. One is from today, one is an older one. So, so what I just did is I basically deleted all the refresh tokens for that, for that user.So now we effectively revoked access for the client to the to the back end so the client can still call the service.But as soon as the access token has expired, once it tries to renew the token, this will fail. Okay, so that's the, the life cycle, if you want, yeah? And the client asks for authorization, the user grants authorization. We get back an access token and the refresh token. The returned access token can be used as long as the access token is valid and then it can be refreshed within the refresh token. And in the meanwhile, at any point, a client can revoke access by basically invalidating the refresh token. And from that point on, the client application can't access the data anymore, and has to go again through the consent cycle to get a new access token.
The next flow I want to talk about is the so called Implicit Flow and this is a very, very common scenario meaning that you're having applications which run completely locally, on the client device. That might be a native application, like something you've written you know in objective c on an iOS device. But also more and more common are the like a packaged applications, like JavaScript applications, which run inside, inside of a user agent on a client device, which are, you know, very much like local clients, but they and not written natively. Now again we have the same challenge here, this local application wants to access a resource on behalf of the resource owner. And again you might not be happy as the resource owner, to give that native or local application your master key or key your password, yeah. Think of think, think of a Twitter application for example, there are so many Twitter applications out there, Twitter clients which don't come from Twitter, so you, you don't really know what they're doing with your password. Like, are they storing that securely on, on my device? Or is it somewhere stored in clear text what happens if I lose my device. So in other words the Implicit Flow again, enables this scenario, where you don't have to give your master key to the application, but rather to the authorization server.And declined application, can retrieve an access token and can use the access token to access your resource.
Resource Owner
^ |
| | GET /authorize?
| | client_id=nativeapp&
GET /cb# | | scope=resource&
access_token=abc# | | redirect_uri=http://localhost/cb&
expires_in=3600& | | response_type=token&
state=123 | | state=123
| V
Authorization Server
So again, the way this works is very similar to, to the code flow
. The client application needs to be able to show web page, namely the authorization server, login, and Implicit Flow. And that is typically done by either popping up browser or by embedding a web flow into the application. I'm pretty sure you have seen that before on your mobile device that when either browser shows up and renders some log in or the login shows up in line with the application. And again basically you pass in a gate request into this webview browser that goes to the authorize end point. Again you specify which application we're talking about at this time, it's called the native app, you specify the scope meaning which resources you want to have access to. You specify a redirect URI, where the resource to authorization server goes back to after that is done. And this time, which is a little bit different, you don't specify a code here, but a token. Meaning we don't get back an access code here that we have to exchange using our credentials, but we get back the token directly. And the state parameter has the same purpose again as in the previous flow.
Now once the user has clicked the allow access button this time the access token comes back directly inside the callback. So you see here that, that as, that to get to the callback, and you embed the access token as parameters on that URL.
So we have the access token, we have the expires in, and we have the state and remember, and note the little difference here. We don't have a callback question mark, we have a callback hash, and that, that is called hash fragment encoded. Meaning these, that URL or, or this parameters on the URL will never be sent to some server. So the idea is that now, let, let's say, on the client you would, you would click a link, for example, these the access token wouldn't show up in, in the referrer, for example on the on, on, on, on the linked target, for example. So, to, today, I'm everything that's after the hash fragment is meant for local consumption and doesn't get sent to a server by a browser or a web view. So that is some, some protection that the token doesn't leak to a third party, for example. And then after that again, the client application uses that access token to, to request access to a resource, basically by putting it on your authorization header of the outgoing HTTP request.
Resource Server
^
GET /resource |
Authorization: |
Bearer access_token |
|
Resource Owner(Client)
So, you've seen that this flow is a simplified version, really, of the, the code flow. So there is no authorization code here, the token is exposed to the browser and the local operating system. So there are some security concerns here as well and since the client does not authenticate with the authorization server, to get the token, we also typically don't provide refresh tokens here. Because otherwise there would be no way to control the access to the refresh token and so on.
The, the other question would be, why doesn't the client authenticate to the authorization? That's because well then, that, that would mean we had to install the client's credentials on the device, and again, that is a of questionable security if we have to install credentials on a client device that we can't control. There are situations where that might make sense, but that is more like in the area where you type the control to client hardware and operating system platform, but let's say for the public and for a pubic app scenario, there's not a lot of good scenarios where you want to store hard coded client credentials on the device.
Now, since arguably this is a very common application scenario. Like you're building a mobile application that needs access to a back end resource. And you don't want to give that application the master key. But refresh tokens are per spec not allowed here.
Many people have build you know, variations of that flow and I put non-standard here in quotation marks because the standard itself already provides a high number of variations. So, many people just, you know, push the boundaries or the limits in one or the other direction to make it work for their scenario.
But for example, Google doesn't allow Google doesn't use refresh tokens but they use hidden iFrames to do the consent handshake again. So, in other words there, they using cookies which is, is some sort a refresh token. So, there, there are many variations to that flow and this is actually the, the most heavily debated flow as well especially to its security properties. To spec does says exactly what I'm saying here. No client authentication, no refresh tokens, but there are situations where you want to have refresh tokens. And the way they are implemented is either using refresh tokens and breaking the spec or some other companies get around it by using other creative means like hidden web views to refresh. The access tokens and then have a, a finite lifetime on the cookie that is used in the webview. So you have to re-authentic, like every two weeks, that is, for example, what Google is doing, yeah? You're getting, some sort of experience. But, you have to re-authenticate at a certain amount, after a certain amount of time. Just to make sure that if your device got stolen, for example, that you don't have, you know, unlimited access tokens on that device.
This is an example of the OAuth implicit flow. I'm using a Windows 8 store application here, and the reason I'm doing that is because I want to show you that Microsoft actually built operating system APIs now into Windows 8 to do this OAuth authorization server interaction. So, I've written a little method here, which is called DoImplicitFlowAsync, and I'm basically constructing my uri here. You see that client ID scope will redirect uri, I mean, nothing, nothing new here, yep.
But what's interesting here is this class, the WebAuthenticationBroker, that is actually a Win RT API. So I basically can call, authenticate Async, pass in some options. Pass in the uri that I just constructed, and then Windows 8 will open a webview which is in a protected container. So my client application doesn't have access to, for example keystrokes in that container and so on. It also doesn't have access to the cookies, the, the, the cookie container's actually a separate one from the normal internet explorer cookie container at least by default. So, it, it's basically an operating system created webview that has some security features as well. And then, when we get back a success, that is called meaning, or we redirect your hit, then we get passed back the query string, and then we can extract the access token from that, and use that to talk to our resource server. Now the nice thing is now we can now store this access token using other Windows 8 API's locally on the machine or even roaming to other trusted computers.
So let's say you are logging into this service on your windows 8 you know, tablet and, you're moving to your laptop and you don't have to re-authenticate and can you know, continue where you, where you left off. So that's quite nice so let me let me show you that.
So the first time you run the application you see we don't have an access token here. So we can't even sign in, now this is triggering this round trip to the authorization server that is basically now the webview that I just described. So we sign in here, now we get to the consent screen, so, again Windows 8 Client, that, that is the same consent screen that we've just seen, With the code flow and we say allow access and you see now that what the application gives back is JSON token. And now I can call the service and I'm fine. So when I now close the application and at some later point come back and the token is still valid, you know, it's still here. I can call service and I don't have to re-authenticate and if I would have to re-authenticate again, I could say, use some of the of the techniques that I described earlier like popping up the authentication again. And maybe we maintain a cookie to the authorization service, so we don't have to type in the password again. There are other variations how we do that, but that would be one option. So, as I said, this WebAuthenticationBroker, is another operating system level API. So there are other applications which use of that as well, much as my own little sample here.
So when you download the official Twitter application, you see they're doing exactly the same thing. Yeah, they are opening this web view here and rendering the consent as /log on screen in that case. So you I could sign in here now, and then, given my credentials are correct I can say authorize app, and from that point on, the application can use or can show me my twitter stream, and what, what activity to, to that line is doing. But the point is I didn't give that twitter application my path directly. It went via the implicit flow to the twitter back end and it will return, ultimately return a back end access token.
The resource owner password credential flow is typically what is considered for trusted applications, or even trusted devices. So the idea is, again, you have like some sort of client that might be a, that application client, that might be a client application like a like a local device installed client application, that needs access to the resource.
But this time the client application itself collects the credentials and not the authorization server. Okay.So, the scenario here is that you actually trust that client application so much that you want to type in your password into the client application and not in the authorization server's log-in URL, okay?
That basically means that you are trusting the client application. You are maybe trusting the manufacturer of the client application, which might be your employer, for example, so that's fine. Because you are accessing a company resource, and you are also, I guess, trusting the developers of these, of the client application, that they you know, take certain responsibility to not store that password in, in clear text on the device, stuff like that. So in other words, you give your client application, your master key directly and you assume that it is in good hands.
The way this works is that we skip the authorization and point altogether and directly talk to the token end point, you see where we're going to talk end point here, the client application authenticates with the authorization server again. This assumes that the device, this thing is running on it's considered trusted enough that it, you can store client credentials here.
Resource Server Authorization Server
^
POST /token |
Authorization: Basic (client_id: secret) |
grant_type=password& |
scope=resource& Resource Owner(Client)
user_name=owner&
password=password&
You directly pass in the password of the user, and the, the name of the user, into this post request, you pass in the scope that you want to get access to. And what you get back, again, is the standard award of token response, the access token, the refresh token, and so on.
Resource Server Authorization Server
|
{ |
"access_token" : "abc", |
"expires_in" : "360", |
"token_type" : "Bearer", |
"refresh_token" : "xyz" |
} V
Resource Owner(Client)
Now, actually, you know, given a well written application that only collects the user's password at the first time of the login. Then it goes to the authorization and exchanges it directly with an access token and then forgets the password forever. That is much better than, than actually storing the password on the client device.
So, this is a useful flow for scenarios yeah, so the application collects your password, it sends it to the authorization server, the authorization server sends you back an access token and then, you store the access token and the refresh token and forget about the client's password altogether.
That is much better than actually storing that password, and then, after that of course the declined application can ask the resource and can refresh the token once the access token has expired.
So this is, also a useful flow for many situations, you should just be aware that now the resource owner credentials are exposed to the client. And typically we shouldn't educate users to type in their password in too many applications, right? So, the rule of thumb is that they should be educated to just type in their password into their company's you know, logon UI. Yeah, like, like to think that the authorization server uses to authenticate the user, but that's not always possible. And obviously if users become too accustomed to typing in their password in to many UI's then that can become a phishing problem, so yeah we should limit usage of that flow to the right amount of applications, whatever that means yeah.
And as I said earlier I still think it's better to to use that flow even if the user type's independently inclined, then to you know, so we can exchange the password with an access token refresh token then you know storing the raw password. Given that the developer is actually using that feature and that is part of the trust decision, right so we are trusting that application being stored locally on our device. You know, it's, it's maybe coming from our company, it's coming from a trusted third party. So we also trust the developer is using, the features in the right way, so he doesn't store that password but uses the access token refresh token feature.
There's an example of the resource owner password flow. There's a .NET C# application. Or you know test client and you can see we are basically having this this very typical two legs here. One is requesting the token from the authorization server, and one is recalling the service using that token.
So let's have a look at the request token, method. So, I'm using something from something from Thinktecture Identity model and the link to that is in the, is in the resources appendix called the OAuth2Client. Basically what you pass in is the URL right to the OAuth2Token and point the Client name and the Client Secret and then we can request an access token using, theresource owner name, and resource owner password and the scope we want to access.
What this thing does essentially is really nothing rocket science, it just creates a form URL encoded post body, sends that to the token endpoint, and passes The response back into a class called response and then from thought access token we can access the access token. There is also a thought refresh token and a thought expires in, and so on.
Now, how to we call the service? We have the token as a string here. We are creating an HTTP client. Again, this little extension method comes from think texture identity model and just sets the authorization header with a scheme of bearer and that puts that token on the authorization header, and then we are just calling an end point. And again what this end pint is doing is it echoes back the claims that came, that the token contains.
So when I run this, you can see that first we are requesting the token that is the round trip to the authorization server.Then we are calling the service and again we have here claims expiration issuer, audience, name of the name of the user, and other things the authorization server put into this access token.
Just to show you, there's the official dropbox client for Windows. And it uses exactly the same approach here so. Again the, the whole idea of the results owner password flow is that the client application itself brings up a login UI, you type in name and password, and then under the covers, they're doing the resource owner flow.
To the authorization server, exchange that password and name with an excess token and refresh token, and then can forget about the name and the password. And that, that's okay because, you know, that is the official DropBox client, so, I guess it's okay for them to say hey, you know, we know your password anyway so you can also type it into our client interface.
The last flow is the so called client credentials flow. That is really for machine to machine communication when there is no human involved at all. So you can see that basically here, the client goes to the token endpoint of the authorization server, authenticates itself here, but not resource owner is involved here. So basically that is the equivalent of, you know, classic service to service communication or client to service communication.
And obviously the, the resulting token that will get issued here, also has no user meaning the client can never act on behalf of a user here, can, but can only act on behalf of itself. So that's again, you know, classic two silicon based lifeforms need to communicate, no carbon involved.
So to sort of summarize that, you've seen that the OAuth flows are really variations of how you can orchestratethe communication with the authorization server and the resource server for various application scenarios.
Typically there are two parts of an OAuth2 communication. One is where authorization gets requested. So when a human is involved, that's where the human has to sign in, go through the content screens, say, it's okay, I allow application X to access my resources. And to read operation and write operation and you know, change my email addresses, you know, that's totally application defined. But the point is that a human gives permission you know in an interaction for that client. And after this permission has been recorded by the authorization sever, then the client can do a direct communication with the token endpoint on the authorization server to request the corresponding access tokens.
Now, you've seen that there were some variations one is the code flow where there's in- intermediary code. Authorization code used to provide better accountability. One is the implicit flow, where they conflated your authorization and token round trip into a single one, because there are no client credentials involved. And there is also the resource owner flow where the password actually gets typed into the client application as opposed to the authorization server. And there's the client credential flow with is for service to service communication.
And you basically pick and choose what, you know, is the, the best flow for your application scenario. Obviously the one that is heavily debated right now is the Implicit Flow because they are many variations how to do that. And again the OAuth specification which cause itself a framework really gives you some you know, space for interpretation here really.
There's also a separate specification which I didn't explicitly talk about called RFC 6750. And that is basically the thing that describes, once the client has got the token from the authorization server, how it can be transmitted to the actual resource server. That is, yeah, not very complicated. I skipped that part here, but typically what you do is you put that access token on the authorization header of an HTTP request and then send it to the resource server.