Hydra
Hydra is a twelve factor authentication, authorization and account management service, ready for you to use in your micro service architecture. Hydra is written in go and backed by PostgreSQL or any implementation of account/storage.go.
Hydra implements TLS, different OAuth2 IETF standards and supports HTTP/2. To make things as easy as possible, hydra
comes with tools to generate TLS and RS256 PEM files, leaving you with almost zero trouble to set up.
Please be aware that Hydra is not ready for production just yet and has not been tested on a production system.
If time schedule holds, we will use it in production in Q1 2016 for an awesome business app that has yet to be revealed.
This should however not discourage you from trying out or using Hydra. Most of the HTTP endpoints have reached a stable status and should not change in a major way until the first release (0.1).
Current status:
- Development: 90% of principal components
- HTTP API: 70% (in review)
- Real world use: 10%
Table of Contents
What is Hydra?
Authentication, authorization and user account management are always lengthy to plan and implement. If you're building a micro service app
in need of these three, you are in the right place.
Motivation
Hydra was written because we at Ory needed a scalable 12factor OAuth2 consumer / provider with enterprise grade
authorization and interoperability without a ton of dependencies or crazy features. While we where at it, we added
Policy, Account and Client management and other cool features. Because we hate maintaining 5 different databases (or paying someone to maintain them)
and dealing with crazy and unpredictable dependency trees, Hydra only requires Go and PostgreSQL (or any SQL speaking database).
Hydra is the open source alternative to proprietary authorization solutions in the age of micro services.
Use it, enjoy it and contribute!
Features
Hydra's core features in a nutshell:
- Account Management: Sign up, settings, password recovery
- Access Control / Policy Decision Point / Policy Storage Point backed by Ladon.
- Rich set of OAuth2 features:
- Hydra does not speak HTML. We believe that the design decision to keep templates out of Hydra is a core feature. Hydra is backend, not frontend.
- Easy command line tools like
hydra-host jwt
for generating jwt signing key pairs or hydra-host client create
.
- Hydra works both over HTTP (use only in development) and HTTP/2 with TLS (use in production).
- Hydra is unit and integration tested. We use dockertest
What do you mean by Hydra is backend?
Hydra does not offer a sign in, sign up or authorize HTML page. Instead, if such action is required, Hydra redirects the user
to a predefined URL, for example http://sign-up-app.yourservice.com/sign-up
or http://sign-in-app.yourservice.com/sign-in
.
Additionally, a user can authenticate through another OAuth2 Provider, for example Dropbox or Google.
Take a look at the example sign up/in endpoint implementations hydra-signin
and hydra-signup.
HTTP/2 RESTful API
The API is described at apiary. The API Documentation is still work in progress.
Run hydra-host
With vagrant
You'll need Vagrant, VirtualBox and Git
installed on your system.
git clone https://github.com/ory-am/hydra.git
cd hydra
vagrant up
# Get a cup of coffee
You should now have a running Hydra instance! Vagrant exposes ports 9000 (HTTPS - Hydra) and 9001 (Postgres) on your localhost.
Open https://localhost:9000/ to confirm that Hydra is running. You will probably have to add an exception for the
HTTP certificate because it is self-signed, but after that you should see a 404 error indicating that Hydra is running!
hydra-host offers different capabilities for managing your Hydra instance.
Check the this section if you want to find out more.
You can also always access hydra-host through vagrant:
# Assuming, that your current working directory is /where/you/cloned/hydra
vagrant ssh
hydra-host help
Set up PostgreSQL locally
On Windows and Max OS X, download and install Docker Toolbox. After starting the Docker Quickstart Terminal,
do the following:
> docker-machine ssh default # if you're not already ssh'ed into it
> docker run --name hydra-postgres -e POSTGRES_PASSWORD=secret -p 5432:5432 -d postgres
> exit
> docker-machine ip default
# This should give you something like: 192.168.99.100
> # On Windows
> set DATABASE_URL=postgres://postgres:secret@{ip from above}:5432/postgres?sslmode=disable
> # On Mac OSX
> export DATABASE_URL=postgres://postgres:secret@{ip from above}:5432/postgres?sslmode=disable
On Linux download and install Docker:
> docker run --name hydra-postgres -e POSTGRES_PASSWORD=secret -p 5432:5432 -d postgres
> export DATABASE_URL=postgres://postgres:secret@localhost:5432/postgres?sslmode=disable
Warning: This uses the postgres database, which is reserved.
For brevity the guide to creating a new database in Postgres has been skipped.
Run as executable
> go get -d -v github.com/ory-am/hydra/...
> go install github.com/ory-am/hydra/cli/hydra-host
> hydra-host start
Note: For this to work, $GOPATH/bin must be in your path
Run from sourcecode
> go get -d -v github.com/ory-am/hydra/...
> # cd to project root, usually in $GOPATH/src/github.com/ory-am/hydra
> cd cli
> cd hydra-host
> go run main.go start
Available Environment Variables
The CLI currently requires two environment variables:
Variable |
Description |
Format |
Default |
PORT |
Which port to listen on |
number |
443 |
HOST |
Which host to listen on |
ip or hostname |
empty (all) |
HOST_URL |
Hydra's host URL |
url |
"https://localhost:4443" |
DATABASE_URL |
PostgreSQL Database URL |
postgres://user:password@host:port/database |
empty |
BCRYPT_WORKFACTOR |
BCrypt Strength |
number |
10 |
SIGNUP_URL |
Sign up URL |
url |
empty |
SIGNIN_URL |
Sign in URL |
url |
empty |
DROPBOX_CLIENT |
Dropbox Client ID |
string |
empty |
DROPBOX_SECRET |
Dropbox Client Secret |
string |
empty |
JWT_PUBLIC_KEY_PATH |
JWT Signing Public Key |
./cert/rs256-public.pem (local path) |
"../../example/cert/rs256-public.pem" |
JWT_PRIVATE_KEY_PATH |
JWT Signing Private Key |
./cert/rs256-private.pem (local path) |
"../../example/cert/rs256-private.pem" |
TLS_CERT_PATH |
TLS Certificate Path |
./cert/cert.pem |
"../../example/cert/tls-cert.pem" |
TLS_KEY_PATH |
TLS Key Path |
./cert/key.pem |
"../../example/cert/tls-key.pem" |
DANGEROUSLY_FORCE_HTTP |
Disable HTTPS |
force |
disabled |
CLI Usage
NAME:
hydra-host - Dragons guard your resources
USAGE:
hydra-host [global options] command [command options] [arguments...]
VERSION:
0.0.0
COMMANDS:
client Client actions
user User actions
start Start the host service
jwt JWT actions
tls JWT actions
policy Policy actions
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--help, -h show help
--generate-bash-completion
--version, -v print the version
Start server
NAME:
hydra-host start - start hydra-host
USAGE:
hydra-host start [arguments...]
Create client
NAME:
hydra-host client create - Create a new client.
USAGE:
hydra-host client create [command options] [arguments...]
OPTIONS:
-i, --id Set client's id
-s, --secret The client's secret
-r, --redirect-url A list of allowed redirect URLs: https://foobar.com/callback|https://bazbar.com/cb|http://localhost:3000/authcb
--as-superuser Grant superuser privileges to the client
Create user
NAME:
hydra-host user create - create a new user
USAGE:
hydra-host user create [command options] <username>
OPTIONS:
--password the user's password
--as-superuser grant superuser privileges to the user
Create JWT RSA Key Pair
To generate files rs256-private.pem and rs256-public.pem in the current directory, run:
NAME:
hydra-host jwt generate-keypair - Create a JWT PEM keypair.
You can use these files by providing the environment variables JWT_PRIVATE_KEY_PATH and JWT_PUBLIC_KEY_PATH
USAGE:
hydra-host jwt generate-keypair [command options] [arguments...]
OPTIONS:
-s, --private-file-path "rs256-private.pem" Where to save the private key PEM file
-p, --public-file-path "rs256-public.pem" Where to save the private key PEM file
Create a TLS certificate
NAME:
hydra-host tls generate-dummy-certificate - Create a dummy TLS certificate and private key.
You can use these files (in development!) by providing the environment variables TLS_CERT_PATH and TLS_KEY_PATH
USAGE:
hydra-host tls generate-dummy-certificate [command options] [arguments...]
OPTIONS:
-c, --certificate-file-path "tls-cert.pem" Where to save the private key PEM file
-k, --key-file-path "tls-key.pem" Where to save the private key PEM file
-u, --host Comma-separated hostnames and IPs to generate a certificate for
--sd, --start-date Creation date formatted as Jan 1 15:04:05 2011
-d, --duration "8760h0m0s" Duration that certificate is valid for
--ca whether this cert should be its own Certificate Authority
--rb, --rsa-bits "2048" Size of RSA key to generate. Ignored if --ecdsa-curve is set
--ec, --ecdsa-curve ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521
Import policies
You can import policies from json files.
NAME:
hydra-host policy import - Import a json file which defines an array of policies
USAGE:
hydra-host policy import <policies1.json> <policies2.json> <policies3.json>
Here's an exemplary policies.json:
[
{
"description": "Allow editing and deleting of personal articles and all sub resources.",
"subject": ["{edit|delete}"],
"effect": "allow",
"resources": [
"urn:flitt.net:articles:{.*}"
],
"permissions": [
"edit"
],
"conditions": [
{
"op": "SubjectIsOwner"
}
]
},
{
"description": "Allow creation of personal articles and all sub resources.",
"subject": ["create"],
"effect": "allow",
"resources": [
"urn:flitt.net:articles"
],
"permissions": [
"edit"
],
"conditions": [
{
"op": "SubjectIsOwner"
}
]
}
]
Good to know
This section covers information necessary for understanding how hydra works.
Policies
Policies are something very powerful. I have to admit that I am a huge fan of how AWS handles policies and adopted their architecture for Hydra. Please find a more in depth documentation
at the Ladon GitHub Repository.
{
// This should be a unique ID. This ID is required for database retrieval.
id: "68819e5a-738b-41ec-b03c-b58a1b19d043",
// A human readable description. Not required
description: "something humanly readable",
// Which identity does this policy affect?
// As you can see here, you can use regular expressions inside < >.
subjects: ["max", "peter", "<zac|ken>"],
// Should the policy allow or deny access?
effect: "allow",
// Which resources this policy affects.
// Again, you can put regular expressions in inside < >.
resources: ["urn:something:resource_a", "urn:something:resource_b", "urn:something:foo:<.+>"],
// Which permissions this policy affects. Supports RegExp
// Again, you can put regular expressions in inside < >.
permissions: ["<create|delete>", "get"],
// Under which conditions this policy is active.
conditions: [
// Currently, only an exemplary SubjectIsOwner condition is available.
{
"op": "SubjectIsOwner"
}
]
}
This is what a policy looks like. As you can see, we have various attributes:
- A Subject could be an account or an client app
- A Resource could be an online article or a file in a cloud drive
- A Permission can also be referred to as "Action" ("create" something, "delete" something, ...)
- A Condition can be an intelligent assertion (e.g. is the Subject requesting access also the Resource Owner?). Right now, only the SubjectIsOwner Condition is defined. In the future, many more (e.g. IPAddressMatches or UserAgentMatches) will be added.
- The Effect, which can only be allow or deny (deny always overrides).
Hydra needs the following information to decide if a access request is allowed:
- Resource: Which resource is affected
- Permission: Which permission is requested
- Token: What access token is trying to perform this action
- Context: The context, for example the user ID.
- Header
Authorization: Bearer <token>
with a valid access token, so this endpoint can't be scanned by malicious anonymous users.
Everything is RESTful. No HTML. No Templates.
Hydra never responds with HTML. There is no way to set up HTML templates for signing in, up or granting access.
Sign up workflow
Hydra offers capabilities to sign users up. First, a registered client has to acquire an access token through the OAuth2 Workflow.
Second, the client sets up a user account through the /accounts
endpoint.
You can set up a environment variable called SIGNUP_URL
for Hydra to redirect users to,
when the user successfully authenticated via the OAuth2 Provider Workflow but has not an account in hydra yet.
If you leave this variable empty, a 401 Unauthorized Error will be shown instead.
Sign in workflow
Hydra offers different methods to sign users in.
Authenticate with Google, Dropbox, ...
You can authenticate a user through any other OAuth2 provider, such as Google, Dropbox or Facebook. To do so, simply add
a provider query parameter to the authentication url endpoint:
/oauth2/auth?provider=google&client_id=123&response_type=code&redirect_uri=/callback&state=randomstate
The provider workflow is not standardized by any authority, has not yet been subject to a security audit and is therefore subject to change.
Unfortunately most providers do not support SSO provider endpoints so we might have to rely on the OAuth2 provider workflow for a while.
We will soon document how you can add more providers (currently only Dropbox is supported).
Authenticate with a hydra account
There are multiple ways to authenticate a hydra account:
- Password grant type: To do so, use the OAuth2 PASSWORD grant type.
At this moment, the password grant is allowed to all clients. This will be changed in the future.
- Callback: You can set up an environment variable called
SIGNIN_URL
for Hydra to redirect users to,
when a client requests authorization through the /oauth2/auth
endpoint but is not yet authenticated.
Visually confirm authorization
When a client is not allowed to bypass the authorization screen ("Do you want to grant app XYZ access to your private information?"),
he will be redirected to the value of the environment variable AUTHORIZE_URL
.
This feature is not implemented yet.
Principles
- Authorization and authentication require verbose logging.
- Logging should never include credentials, neither passwords, secrets nor tokens.
Attributions