Every time I want to get an access token for a REST service for an integration test or other non-interactive use I forget how to go about it using OAuth and then have to piece it back together. My use case is wanting to give some headless process access to a user level service like Google Calendar. This post is now my permanent memory of how to do it, I hope it helps someone else.
The Steps
- Get an account on the service
- Define the client for OAuth access – example using a Google API
- Get the code using an interactive request in a browser
- Get the refresh token using a POST request from CURL or Postman or your favourite tool.
- Store the refresh token somewhere safe
- Get an access token as needed using a simple REST call
- Use the access token
Steps 1-5 are pretty much one time only, then each time you can perform 6 and 7 to gain access to the service. Here’s the official Google documentation that much of the specifics are taken from. Reading through that first might be a good idea, or just refer to it to clarify points below. Where there are differences you should trust the Google documentation as there may have been changes.
Get an account on the Service
Regardless of the service you’ll need a user account for that service before you can request REST access via OAuth if you don’t already have one. The details on this vary from service to service but this part is simple, just go to the client for that service, for example the Google Photos website, and create a user account for yourself.
Define the Client for OAuth Access
OAuth uses the concept of a client to define the information required to enable access to a resource. Although services have different ways of defining the client they share quite a few similarities since OAuth requires a client id and usually a secret. For access to services protected by Google Identity the client is defined in API & Services which is accessible from the Google Console. Once the client is defined you can use it to get tokens.
Get the Code in a browser
The goal is to get an access token but getting new access tokens requires a refresh token. To get the refresh token requires that you log in manually to the service in a browser. Here’s the URL to use in the browser. This is a little indirect so to be clear, it doesn’t matter which Google service you use, the endpoint is the same, just the parameters differ. Whether you’re trying to get access to Calendar, Photos, or other, the endpoint is still https://accounts.google.com/o/oauth2/auth
.
https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/bigquery&response_type=code&access_type=offline&redirect_uri=https://example.com/&client_id=lotsofrandomcharactors.apps.googleusercontent.com
There are several parameters to the query that need to be provided based on information from your environment.
Parameter | Description |
scope | This will vary depending on what you are trying to access. Each of the Google APIs list their scope URIs in the API definition. https://www.googleapis.com/auth/bigquery is from the Big Query API definition. This is the main differentiator that allows access to different services. To allow access to multiple scopes use a space delimited list of scopes, URI encoded of course. |
response_type | code |
access_type | offline Usually this is the right value since you’ll want to be able to create new access tokens without a user being present for most use cases. |
redirect_uri | This comes from the client definition created in the previous step and must match exactly. You can use the home page of your website for example, it doesn’t matter much what it is as long as it matches your definition. |
client_id | This comes from the client definition created in the previous step and must match exactly. |
prompt | consent This optional parameter can be used to force the OAuth token endpoint to deliver a new refresh token if you mistakenly call it multiple times without recording the refresh token. |
Navigate to this URL in your browser and you will be prompted to provide login to the service and then to grant access to the scope that you specified. Assuming that you allow access the result will be a redirect to the page that you specified. The redirect URL will include a code that you need for the next step. Here’s an example based on the above request.
https://example.com/?code=4%1Z1zZZZZZZZzzZ1ZZzz1ZzZ1Z1zZzsZzZ-zZzU1zZZzz1ZZ11zzz1ZZzZzzz111OzzzZZZZZZZ1zZZzZzzzzZZZ-1&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fbigquery
The value that you want is after code=
. Note that it is URL encoded so you’ll have to decode it before you can use it.
Get the Refresh Token
Now that you have a code you can make a POST request to the OAuth endpoint to get an access token and a refresh token. The POST is a x-www-form-url-encoded
body containing the following parameters.
Field | Description |
grant_type | authorization_code |
code | This is the URL decoded value obtained from the browser request in the previous step. |
client_secret | This comes from the client definition created in the second step and must match exactly. |
client_id | This comes from the client definition created in the second step and must match exactly. |
redirect_uri | This comes from the client definition created in the second step and must match exactly. You can use the home page of your website for example, it doesn’t matter much what it is as long as it matches your definition. |
Post this data to the Google OAuth token endpoint.
https://accounts.google.com/o/oauth2/token
The response is JSON that contains both an access token and a refresh token. The refresh token is what you want because it allows you to request new access tokens and has a very long lifespan. The response looks like this.
{
"access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in": 3920,
"token_type": "Bearer",
"scope": "https://www.googleapis.com/auth/bigquery",
"refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}
Store the Refresh Token
The refresh token should now be stored somewhere safe where you won’t lose it and others can’t get access to it. A refresh token is a long term credential to access protected content so it should only be stored in a secure fashion. Some mobile apps for example put it in the app preferences but this is pretty easy to read and is not a secure place to store it.
One thing the Google docs don’t point out is that the refresh token is only returned on the first POST request to the auth endpoint for a particular user. After that you will receive new access tokens but no new refresh token. You can force it to hand out a new refresh token using the extra prompt=consent
parameter.
Get an Access Token
To access the endpoint from your code you need to provide an access token. The access token provided in response to the above call to the token endpoint expires pretty quickly, usually in an hour, so it is not sufficient to just store that and reuse it. Instead you need to make a POST call to an endpoint with the refresh token and get a new access token.
The POST is a x-www-form-url-encoded
body containing the following parameters.
Parameter | Description |
grant_type | refresh_token |
client_id | This comes from the client definition created in the second step and must match exactly. |
client_secret | This comes from the client definition created in the second step and must match exactly. |
refresh_token | The token retrieved in the previous step. |
Post this data to the Google OAuth token endpoint.
https://oauth2.googleapis.com/token
The response is JSON that contains an access token like this:
{
"access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in": 3920,
"scope": "https://www.googleapis.com/auth/bigquery",
"token_type": "Bearer"
}
Use the Access Token
Most Google REST APIs take the access token in a header to allow access to the functionality. That header would look like this:
Authorization: Bearer 1/fFAGRNJru1FTz70BzhT3Zg
Where Authorization
is the header name and the value is
Bearer 1/fFAGRNJru1FTz70BzhT3Zg
.
bro … I feel like I owe you money for this 🙂
Thank you so much !!!
for getting refresh token I did the same thing but i got this error
{
“error”: “invalid_grant”,
“error_description”: “Bad Request”
}
what can I do now ?
great documenation
While getting the code, give prompt=consent as the first query parameter followed by the rest, sign in again, get the code and update the code in your request.