This library contains cowboy handlers that are compatible to the OAuth2 specs and interface with snarl as a backend.

It includes additional support for 2FA with YubiKeys as a custom extension, that however is not needed and can be skipped.

An example of including the endpoints can be found below. Please note that the token endpoint has one parameter that is the base URL for the 2FA auth (can be left out if 2FA is not used).

{<<"/api/:version/oauth/token">>,
 cowboy_oauth_token, []},
{<<"/api/:version/oauth/auth">>,
 cowboy_oauth_auth, [<<"/api/0.2.0/oauth/2fa">>]},
{<<"/api/:version/oauth/2fa">>,
 cowboy_oauth_2fa, []}

In addition to the handlers it provides three helper functions:

%%
AuthData = cowboy_oauth:resolve_bearer(<<"grizly bearer token">>),
%%...
%%
true = cowboy_oauth:allowed(AuthData, [<<"cloud">>, <<"vms">>, <<"create">>]),
%%...

The OAuth2 forms used can be configured via the environment parameters oauth_form and oauth_2fa_form defaults for the .dtl files can be found in the templates directory.

Interacting

The following examples are based on the CURL commands from oauth2_webmachine

The examples use the following conventions:

Authorization Code Grant

CURL

curl -v -X POST \
  http://192.168.1.41/api/0.2.0/oauth/auth -d \
  "response_type=code&client_id=test&redirect_uri=http://localhost&scope=*&state=foo&username=admin&password=admin"

The server responds with a HTTP 302 status, and the authorization code is in the Location field of the header

location: http://localhost?code=6nZNUuYeBM7dfD0k45VF8ZnVKTZJRe2C&state=foo

Use that code to request an access token

curl -v -X POST http://192.168.1.41/api/0.2.0/oauth/token -d \
"grant_type=authorization_code&client_id=test&client_secret=test&redirect_uri=http://localhost&code=6nZNUuYeBM7dfD0k45VF8ZnVKTZJRe2C"

The response will look like this:

{
    "access_token": "Ebaz7zB51OPXnmOlVDnKRhv9Ig9kKW2V",
    "token_type": "bearer",
    "expires_in": 86400,
    "refresh_token": "Qpxr7bOaDA4NUloYc9XYWmS16QAio3Dr",
    "scope": [
        "*"
    ]
}

HTTPie

http --form \
  POST http://192.168.1.41/api/0.2.0/oauth/auth \
  response_type=code client_id=test \
  redirect_uri=http://localhost \
  scope=* \
  state=foo \
  username=admin \
  password=admin

The server responds with a HTTP 302 status, and the authorization code is in the Location field of the header

location: http://localhost?code=ZKSytwIzw5VCE3dcTD4A7wgE3stg4dX4&state=foo

Use that code to request an access token

http --form \
  POST http://192.168.1.41/api/0.2.0/oauth/token \
  grant_type=authorization_code \
  client_id=test \
  client_secret=test \
  redirect_uri=http://localhost \
  code=ZKSytwIzw5VCE3dcTD4A7wgE3stg4dX4

The response will look like this:

{
    "access_token": "AehTS7wrcTp4JY1CZchsXbGyZdmBUvk2",
    "expires_in": 86400,
    "refresh_token": "gcSwNN369G2Ks8cet2CQTzYdlebpQtkD",
    "scope": [
        "*"
    ],
    "token_type": "bearer"
}

Implicit Grant

CURL

curl -v -X POST http://192.168.1.41/api/0.2.0/oauth/auth -d \
  "response_type=token&client_id=test&redirect_uri=http://localhost&scope=*&state=foo&username=admin&password=admin"

The server responds with a HTTP 302 status, and the access token is in the Location field of the header

location: http://localhost#access_token=W9QNN10ZdFNSt7kDcbINtBYWb7brNXqE&token_type=bearer&expires_in=86400&state=foo&scope=%2A

HTTPie

http --form \
  POST http://192.168.1.41/api/0.2.0/oauth/auth \
  response_type=token \
  client_id=test \
  redirect_uri=http://localhost \
  scope=* \
  state=foo \
  username=admin \
  password=admin

The server responds with a HTTP 302 status, and the access token is in the Location field of the header

location: http://localhost#access_token=oCuag0G5VzMZ4AydyYfe9wcjqe9JnqKw&token_type=bearer&expires_in=86400&state=foo&scope=%2A

Resource Owner Password Credentials Grant

CURL

Send an access token request with

curl -v -X POST http://192.168.1.41/api/0.2.0/oauth/token -d \
"grant_type=password&username=admin&password=admin&scope=*"

The response will look like this:

{
    "access_token": "JaYoUbKFdU6hCJBqzr4iayM63fvPk0Wk",
    "expires_in": 86400,
    "scope": "*",
    "token_type": "bearer"
}

HTTPie

http --form POST http://192.168.1.41/api/0.2.0/oauth/token \
  grant_type=password \
  username=admin \
  password=admin \
  scope=*

The response will look like this:

{
    "access_token": "JaYoUbKFdU6hCJBqzr4iayM63fvPk0Wk",
    "expires_in": 86400,
    "scope": "*",
    "token_type": "bearer"
}

Client Credentials Grant

Send an access token request with

CURL

curl -v -X POST http://192.168.1.41/api/0.2.0/oauth/token -d \
"grant_type=client_credentials&client_id=test&client_secret=test&scope=*"

The response will look like this:

{
    "access_token": "8bNpl12bdnup9oUCfpR7UXOI0EJ2dGty",
    "expires_in": 86400,
    "scope": "*",
    "token_type": "bearer"
}

HTTPie

http --form POST http://192.168.1.41/api/0.2.0/oauth/token \
  grant_type=client_credentials \
  client_id=test \
  client_secret=test \
  scope=*

The response will look like this:

{
    "access_token": "8bNpl12bdnup9oUCfpR7UXOI0EJ2dGty",
    "expires_in": 86400,
    "scope": "*",
    "token_type": "bearer"
}

Refreshing an Access Token

If the Authorization Code Grant flow is performed succesfully, the response to the final request should include a refresh token as follows

"refresh_token":"gcSwNN369G2Ks8cet2CQTzYdlebpQtkD"

Obtain a new access token from the refresh token with

CURL

curl -v -X POST http://192.168.1.41/api/0.2.0/oauth/token -d \
"grant_type=refresh_token&client_id=test&client_secret=test&refresh_token=gcSwNN369G2Ks8cet2CQTzYdlebpQtkD&scope=*"

The response will look like this:

{
    "access_token": "fNsfwTV5lgvF1cWdTiGIphwUUsbI4mSU",
    "expires_in": 86400,
    "scope": "*",
    "token_type": "bearer"
}

HTTPie

http --form POST http://192.168.1.41/api/0.2.0/oauth/token \
  grant_type=refresh_token \
  client_id=test \
  client_secret=test \
  refresh_token=gcSwNN369G2Ks8cet2CQTzYdlebpQtkD \
  scope=*

The response will look like this:

{
    "access_token": "fNsfwTV5lgvF1cWdTiGIphwUUsbI4mSU",
    "expires_in": 86400,
    "scope": "*",
    "token_type": "bearer"
}