Thursday, July 12, 2018

Testing OAuth2 (Refresh Tokens) Locally

Recently, I have been trying to develop a tool that involves the use of OAuth2 and here I try to explain the steps I have taken to make it work locally on my laptop. I am particularly interested in obtaining refresh tokens so I would follow the path related to it..

I employ Authlib for the OAuth2 server and follow its documentation to build the client. The OAuth2 server documentation is here and the client documentation is here1. Since we are interested in getting refresh tokens, we need to follow the Authorization_Code flow of OAuth2 (see here). Now, we get into the details. First of all, since we want to test the OAuth2 server locally and Authlib requires HTTPS connection to Token server2, we need to make sure that the client (make it browser, Python code or curl) to the Token server has the relevant certificates and trusts in place for the local machine. I will first create a self-signed certificate for my machine (localhost) and add it to relevant certificate store. I am testing everything in Python so I will go through that path for the store as the requests module of Python uses its own certificates instead of the operating system level certificates. So if you have the certificates already installed to the operating system for the localhost (127.0.0.1) that may not work. I followed this quick list of openssl commands to generate CSR (Certificate Signing Request) and a signed X509 certificate with my own key, as follows:

- Create a Certificate request:
openssl req -out CSR.csr -key ~/.ssh/id_rsa -new

- Create a certificate with the given key and CSR:
openssl x509 -signkey ~/.ssh/id_rsa -in CSR.csr -req -days 365 -out mycert.crt

Then adding the generated certificate to certifi's (Python module) certificate store as given here. Note that the code in this link adds the certificate without the comments (it literally opens the certificate store file of Python/certifi and adds it) but no worries it works..

Once the certificate issue is sorted, we can now focus on the OAuth2 server setup, client registration and test the OAuth2 access and refresh tokens. The setup is straightforward:
- Just grab the example OAuth2 server code from here
- Even though it has been recently added to the repo, make sure the app.py file contains the flag for refresh token (i.e. 'OAUTH2_REFRESH_TOKEN_GENERATOR': True).

Note here that we are running the https (and thus certificate issues above), we do not care about the variable for disabling https.

Client registration
The client registration follows the steps the example OAuth2 server code example above. Here a couple of important things are :
- Make sure to add "authorization_code" (Authorization code flow) to allowed grant types
- Make sure to add "code" to allowed response types.


Obtaining Refresh Tokens
As mentioned authlib requires the Authorization code flow of Oauth2 in order issue refresh tokens. Just a quick note here on refresh tokens: Refresh tokens are usually long-lived and are used to obtain short-lived access tokens. Some OAuth2 implementations/proposals issue refresh tokens every time an access token is issued. Some others issue only when requested and some others do not support at all. Now coming back to Authorization code flow where an OAuth2 client first requests an authorization code and then uses it to obtain an access and refresh token.  Here is a piece of code taken from authlib's client example page :

client_id = '...'
client_secret = '...'
scope = '...'
session = OAuth2Session(client_id, client_secret, scope=scope)
authorize_url = 'https://127.0.0.1:5000/oauth/authorize'
uri, state = session.authorization_url(authorize_url)

Since we are just testing locally we, the user who would normally be forwarded directed to the link in the "uri" variable, need to open the link and give consent for the the resource referred by the scope. Then the OAuth2 server forwards us to the link that has been given during the client registration with an authorization code and a state parameters. Apparently, authlib can extract the code value from a given url so just do the following as given in the above link

session = OAuth2Session(client_id, client_secret, scope=scope)
urlset = 'https://127.0.0.1:5000/?code=..&state=....'
access_token_url = 'https://127.0.0.1:5000/oauth/token'
token = session.fetch_access_token(access_token_url,authorization_response=urlset)

That's it!!


Ps:
1: Authlib supports also OAuth1, so be careful not to follow the wrong documentation.
2: The documentation suggests that setting an environment variable should allow it to work without HTTPS but I couldn't make it work, let me know if you do :)