OAuth 2.0 and OpenID

The basic idea of OAuth 2.0 and OpenID is when a user wants to do something with an application (“Application X”). Access to the application requires access to an account of a company or business (“Service Z”) that would require authorization on the user’s behalf. To allow authorization in a good and secure way (without violating the users’ trust to ask for the their credentials directly), you can integrate something like Hydra into Service Z so that Application X can make authorized requests on the users’ behalf.

Enter Hydra

Understanding why something like Hydra is necessary in the first place is not entirely straightforward if you don’t already understand the use case for running an OAuth 2.0 server. Simply put, Hydra is an OAuth 2.0 authorization server that is both simple and straightforward. The best example (or clearest reason why you would want this) for a use case for this type of authenticating service would be if you created an API and want to have a way for third parties to access your API (for instance, on a user’s behalf).

The following figure does a good job of helping to understand the basic idea and gives a high-level overview of of the authorization code flow.

Figure 1: The Hydra process

thehydraprocess

Installing Hydra

There are three basic ways to run and install Hydra. Personally, I recommend that you use and run Hydra from a Docker image or container, which is how we will run the server in this article. For the sake of posterity, other ways to run Hydra would be to download a binary from the official releases or build from the source. Because we are also going to run Hydra on our host machine to connect to the server, you should install it locally as well. However, it’s not necessary to install it locally if you feel comfortable running all of the commands from a Docker container that is connected to our Hydra server. Installing from binary is relatively easy and just requires downloading the respective release from the project releases page and installing somewhere in your PATH. Or you can directly call the binary if you make it executable.

On a macOS, making PATH executable is as simple and straightforward as:

chmod +x hydra-darwin-amd64 ./hydra-darwin-amd64 -h

To build from the source, you need to have golang 1.8 or higher set up and properly configured (configure the PATH and make sure go get works).

Setting up Hydra with Docker

Because setting up the Hydra server should be app-independent, most of the steps are from following the official guide. For the purposes of this article, we will be using Docker because there are already images for this and it also allows us to run the server easily and allows us to understand how the process works without worrying about the details.

Before we start the Hydra server, we want to start a database up. A good choice for your database is either Postgres or MySQL. To start your database, you need to migrate and create the schemas with a built-in migrate command before you can run the server. To demonstrate how to use Hydra, we are going to just use an in-memory database so that we don’t worry about this for now. Note that this is just for understanding purposes and not for any production use cases. Also note that we are using various flags throughout this (such as --dangerous-force-http), which is for testing and understanding purposes as well.

To get Hydra up and running you need to run the following command:

docker run -d \
 --name hydra-server \
 -p 9000:4444 \
 -e SYSTEM_SECRET=$SYSTEM_SECRET \
 -e DATABASE_URL=$DATABASE_URL \
 -e ISSUER=https://localhost:9000/ \
 -e CONSENT_URL=http://localhost:3000/consent \
 -e FORCE_ROOT_CLIENT_CREDENTIALS=admin:admin-password \
 oryd/hydra:latest --dangerous-force-http --dangerous-auto-logon --disable-telemetry

Note on using SSL

While we are not using any security measures for this article setup (which is ironic because this is a security application), we won’t use security measures for the purposes of understanding the most basic process for the consent flow process to work. Many of the flags and options that we use should not be used outside of testing or learning purposes. For example, not using SSL for the Hydra server (along with many other things) is not recommended. For a full list on what you can do to run this in production environment, please read the official documentation. To use SSL (which is a good idea), you need a key and certificate when you are starting the Hydra server. The easiest way to do this is to use Openssl on your host machine, following a guide such as this one from heroku.

From this step, you need to access the Hydra server so you can later set up the example client. Because we are not doing this from the Docker container that we are already running, we have multiple ways to do setting up the example client. One way is starting up another Docker container that has Hydra, attaching to the container shell instead of running the Hydra server and connecting to the server. This way is great, but to show how else we can do it, we are going to run Hydra on the host machine and attach it to the Docker container.

Using Hydra locally

Download a binary for Hydra from the release page and add it to your $PATH. From here you will be able to connect to the Dockerized Hydra container that is already running on the host machine:

hydra connect --url https://localhost:9000 --id admin --secret admin-password

To make sure it is working, you can create and validate a token:

hydra token validate --skip-tls-verify $(hydra token client --skip-tls-verify)

Next,set up the Hydra client and policy:

hydra clients create --skip-tls-verify \
  --id consent-app \
  --secret consent-secret \
  --name "Consent App Client" \
  --grant-types client_credentials \
  --response-types token \
  --allowed-scopes hydra.consent

hydra policies create --skip-tls-verify \
  --actions get,accept,reject \
  --description "Allow consent-app to manage OAuth2 consent requests." \
  --allow \
  --id consent-app-policy \
  --resources "rn:hydra:oauth2:consent:requests:<.*>" \
  --subjects consent-app

Note: At this point all of these requests can be theoretically run anywhere, such as cURL or by using Python or any OAuth2 library to automate and streamline the process.

Integrating Hydra into your application

The main point of using Hydra is to use it with your application. Luckily, there are two good examples that are provided from the official repo, so we will use one of them (the Node.js one). While the Go app is straightforward (see the repo here) we are going to use the Node.js example. The basics of this Express app are that by using the simple-oauth2 library, it requests an access token from the /oauth2/auth route on the Hydra server. From this, you can use the Hydra SDK to create an instance of the client, which you then use to request consent on the users’ behalf. To spin this up and test it out is fairly simple because there is already a published Docker image that we can use. To make sure everything is working correctly, and understand how it can be integrated from a third-party perspective if you want to allow access to an API you created that requires the end users’ consent, you can run the image:

docker run -d --name hydra-consent --link hydra-server:hydra -p 9020:3000 \
 -e HYDRA_CLIENT_ID=consent-app \
 -e HYDRA_CLIENT_SECRET=consent-secret \
 -e HYDRA_URL=https://hydra:4444 \
 -e NODE_TLS_REJECT_UNAUTHORIZED=0 \
 oryd/hydra-consent-app-express:latest

Getting an access token

Now that the consent app is running (the Node.js app container) you need to create an OAuth consumer app client. These are both things that you would theoretically use for the HTTP API (or any library that does the complete OAuth2 consent flow). For now, we can run these commands through the Hydra binary that is connected to the Hydra server. To create the client and then start the OAuth 2.0 code flow, run the following:

hydra clients create --skip-tls-verify \
 --id some-consumer \
 --secret consumer-secret \
 -g authorization_code,refresh_token,client_credentials \
 -r token,code,id_token \
 --allowed-scopes openid,offline,hydra.clients \
 --callbacks http://localhost:9010/callback

hydra token user --skip-tls-verify \
 --auth-url https://localhost:9000/oauth2/auth \
 --token-url https://hydra:4444/oauth2/token \
 --id some-consumer \
 --secret consumer-secret \
 --scopes openid,offline,hydra.clients \
 --redirect http://localhost:9010/callback

With this, you can have a URL returned such as https://localhost:9000/oauth2/auth?client_id=some-consumer&redirect_uri=http+various_other_data from your browser and login. The credentials to enter are provided on the web page and you can check all of the boxes, but these are what users would technically check when they are going through this process. This is the only part the user will most likely be seeing, because the rest of this process is initiated on the back end of either the authentication server or the client application. You can also cover the returned access token (and because we provided offline in scopes, a refresh token) along with when it will expire. These tokens can also be revoked, inspected, and more, as shown in the Hydra docs. It would probably be wise for the application that is integrating this process to inspect any previous tokens and refresh them if necessary. Luckily, this is straightforward with Hydra.

Using the access token

Now let’s use the authentication token. While the token itself might have access to the API, you still need to create a policy for the user as well as it isn’t enabled by default. Using the access token that we generated earlier through the Hydra binary, run the following:

hydra policies create --skip-tls-verify \
 --actions get \
 --allow \
 --id consent-app-policy \
 --resources "rn:hydra:clients" \
 --subjects user:12345:dandean

From here, using cURL or Python, you can for instance, view all of the clients we created.

curl --insecure -H "Authorization: bearer `ACCESS_TOKEN_FROM_ABOVE`"
                https://localhost:9000/clients/

This sums up the process behind how to set up an OAuth 2.0 server, how the consent flow works as part of another web application, and how the user interacts with it.

Other: Using with Python

Using something like Hydra with Python is feasible as well. Because both the official SDKs were created with swagger-codegen, you can do this for Python in as simple as the following:

brew install swagger-codegen
swagger-codegen generate -i ./docs/api.swagger.json -l python -o ./sdk/python/swagger

You can also use an alternative SDK as well, such as [python-hydra-sdk](https://github.com/OSSystems/python-hydra-sdk). For the sake of this article, we pushed the Python SDK here.

You need a way to get the initial access token (below) if you are doing this entirely through Python.

While the OAuth 2.0 client can be integrated directly into the SDK as mentioned (and the python-flask codegen seems to work as is, provided you change the URLs in the configuration file it creates), I’ll show how to get the initial auth token by using the requests_oauthlib library.

To set this up, install with pip install requests_oauthlib.

The first step is to create an access token that we can use to create policies and clients for the users who would be going through the Hydra service:

from oauthlib.oauth2 import BackendApplicationClient
from requests.auth import HTTPBasicAuth

auth = HTTPBasicAuth('admin', 'admin-password') # from FORCE_ROOT_CLIENT_CREDENTIALS above
client = BackendApplicationClient(client_id=client_id)
oauth = OAuth2Session(client=client)

token = oauth.fetch_token(token_url='https://localhost:9000/oauth2/token', auth=auth, verify=False)
print(token)

We now get a response such as:

{'access_token': '-tdmTUbaEXZLussxTD7NDUzakQ.VrpS4hOml1-
bw_5pkUlikqEs3jjvKOrPcE2hg4yNBBk', 'expires_in': 3599, 'scope': [''],
'token_type': 'bearer', 'expires_at': 1512371155.7248588}

With this access token you can then perform the actual tasks of creating clients, policies, and more.

Conclusion

You will now be able to use Hydra with any API that you create to provide secure third-party access to the API. This allows you to create safe and secure ways to verify users’ identity without having to personally worry about their security details. For instance, instead of having a user sign up from and storing user passwords in a database that can potentially leak and be a huge liability, you can implement an OAuth2 consent flow into the application or website and have much less to worry about in terms of storing private user data. In addition, your users will feel much more comfortable signing up.