Skip to content

Commit

Permalink
Issue and consume JWT tokens
Browse files Browse the repository at this point in the history
See cyberark/slosilo#10 for details.
  • Loading branch information
dividedmind committed Oct 18, 2017
1 parent 7c2e69f commit 1394490
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 45 deletions.
4 changes: 2 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ gem 'base32-crockford'
gem 'activesupport'
gem 'bcrypt-ruby', '~> 3.0.0'
gem 'random_password_generator', '= 1.0.0'
gem 'slosilo', '>=2.0.0'
gem 'slosilo', '~> 2.1'
gem 'listen'
gem 'gli', require: false

Expand All @@ -29,7 +29,7 @@ gem 'gli', require: false
gem 'ruby_dep', '= 1.3.1'

gem 'conjur-api', github: 'cyberark/api-ruby'
gem 'conjur-rack', github: 'conjurinc/conjur-rack'
gem 'conjur-rack', '~> 3.1'
gem 'conjur-rack-heartbeat'
gem 'conjur-policy-parser', github: 'conjurinc/conjur-policy-parser', branch: 'possum'
gem 'rack-rewrite'
Expand Down
21 changes: 8 additions & 13 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,6 @@ GIT
activesupport (~> 4.2)
safe_yaml

GIT
remote: https://github.com/conjurinc/conjur-rack.git
revision: c82a886e4e6e90df4c7de347b007cc8fe0b5c1b6
specs:
conjur-rack (3.0.0.pre)
conjur-api (< 6)
rack (~> 1)
slosilo

GIT
remote: https://github.com/cyberark/api-ruby.git
revision: 957b2063c37c6c94b789dca1004d57ce25f6becf
Expand Down Expand Up @@ -107,6 +98,10 @@ GEM
conjur-cli
docker-api (~> 1.33)
gli
conjur-rack (3.1.0)
conjur-api (< 6)
rack (~> 1)
slosilo (~> 2.1)
conjur-rack-heartbeat (2.0.0)
rack
contracts (0.16.0)
Expand Down Expand Up @@ -217,7 +212,7 @@ GEM
pry (~> 0.10)
public_suffix (2.0.5)
puma (3.8.2)
rack (1.6.5)
rack (1.6.8)
rack-jekyll (0.5.0)
jekyll (>= 1.3)
listen (>= 1.3)
Expand Down Expand Up @@ -314,7 +309,7 @@ GEM
simplecov-html (~> 0.10.0)
simplecov-html (0.10.0)
slop (3.6.0)
slosilo (2.0.1)
slosilo (2.1.1)
spring (2.0.1)
activesupport (>= 4.2)
spring-commands-cucumber (1.0.1)
Expand Down Expand Up @@ -362,7 +357,7 @@ DEPENDENCIES
conjur-cli (~> 6)
conjur-debify
conjur-policy-parser!
conjur-rack!
conjur-rack (~> 3.1)
conjur-rack-heartbeat
cucumber
database_cleaner
Expand Down Expand Up @@ -398,7 +393,7 @@ DEPENDENCIES
sequel-postgres-schemata
sequel-rails
simplecov
slosilo (>= 2.0.0)
slosilo (~> 2.1)
spring
spring-commands-cucumber
spring-commands-rspec
Expand Down
7 changes: 1 addition & 6 deletions apidocs/src/authenticate.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,5 @@ The request body is the API key. For example: `14m9cf91wfsesv1kkhevg12cdywm`.
+ Response 200

```
{
"data": "alice",
"timestamp": "2017-04-24 20:31:50 UTC",
"signature": "BpR0FEbQL8TpvpIjJ1awYr8uklvPecmXt-EpIIPcHpdAKBjoyrBQDZv8he1z7vKtF54H3webS0imvL0-UrHOE5yp_KB0fQdeF_z-oPYmaTywTcbwgsHNGzTkomcEUO49zeCmPdJN_zy_umiLqFJMBWfyFGMGj8lcJxcKTDMaXqJq5jK4e2-u1P0pG_AVnat9xtabL2_S7eySE1_2eK0SC7FHQ-7gY2b0YN7L5pjtHrfBMatg3ofCAgAbFmngTKCrtH389g2mmYXfAMillK1ZrndJ-vTIeDg5K8AGAQ7pz8xM0Cb0rqESWpYMc8ZuaipE5UMbmOym57m0uMuMftIJ_akBQZjb4zB-3IBQE25Sb4nrbFCgH_OyaqOt90Cw4397",
"key": "15ab2712d65e6983cf7107a5350aaac0"
}
{"protected":"eyJhbGciOiJjb25qdXIub3JnL3Nsb3NpbG8vdjIiLCJraWQiOiI0NGIwMjBmNjY0MDBmNzFhZDQ3Y2I0N2IzYTFiNmU5MSJ9","payload":"eyJzdWIiOiJhbGljZSIsImlhdCI6MTUwNTgzMDY1MX0=","signature":"iRLTwNomb_b6TS4e539IIC-isPsc0kIn-F_ajlvnGdrN6brEEHnVha2vm0oDwOjpnmpFrMYLzn8aPo4_7DP3edssfQbpMG6OZI2Ea9DRfkhQGtSQ2fQvhDos_f16EX_jWQkYlsY6T_RurAxf_7VC4hEhjZA8nLkXOohA1DheyoJiT2-7vdpLmf42G7r1gPWHd_JuFkee28Ax2vCi35l4yQXkAHFaLkb3cAD2iwYuavv3qcFnYsT5WhLQqndPoNzgNa4dMvWRkVNUoVmvL30oE6lAlWPO4rFbPpmLwJRJFudDF8IVV9cVRKnV3z79_3RfEsHJ6YTHVX4Cv--cXmkT17QSFp87DK94DAOX3jKvJNo49DdqkzXqAPUIj3CD3IWI"}
```
2 changes: 1 addition & 1 deletion app/controllers/concerns/token_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ def signing_key
end

def sign_token role
signing_key.signed_token Role.username_from_roleid(role.id)
signing_key.issue_jwt sub: Role.username_from_roleid(role.id)
end

end
19 changes: 12 additions & 7 deletions docs/reference/cryptography.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,21 @@ with the release of Slosilo 2.0 in November of 2014.

## Authentication tokens

Most requests to Conjur require an authentication token. An authentication token is a JSON object which contains the following fields:
Most requests to Conjur require an authentication token. An authentication token is a Conjur-specific [JWT](https://tools.ietf.org/html/rfc7519) with the following claims:

* **data** The client's login name.
* **timestamp** The date and time at which the token was issued.
* **signature** HMAC of the token using SHA-256.
* **key** Signature of the token-signing key (used to accelerate token-signing key lookup).
* **sub** The client's login name.
* **iat** Numeric timestamp at which the token was issued
* **exp** (optional) Numeric timestamp at which the token will expire.
* **cidr** (optional) List of IP netmasks which the request bearing this token must match.

Conjur access tokens are valid for 8 minutes. The lifespan is not configurable.
Protected JWS header should also include:

The access token implementation was included in the [slosilo](https://github.com/cyberark/slosilo) cryptographic audit.
* **alg** Signature algorithm; only `conjur.org/slosilo/v2` is accepted -- see [slosilo](https://github.com/conjurinc/slosilo) for reference implementation.
* **kid** Fingerprint of the token-signing key.

Conjur access tokens are valid for 8 minutes since `iat` if an `exp` claim does not dictate otherwise.

The signature algorithm and access token implementation was included in the cryptographic audit of Conjur v4; since v4 used proprietary JSON token encapsulation which predated JWT, with v5 Conjur migrated to using industry-standard JWT. The signature algorithm and most of the token handling code have been unchanged.

## Secret values

Expand Down
21 changes: 12 additions & 9 deletions docs/tutorials/integrations/authenticators.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,18 @@ To issue an access token, you need two things:
1. A signing key, which is a 2048-bit RSA private key.
2. The identity of the role for whom you want to issue the token.

Here's a snippet showing how easy it is to issue an access token for a user called "alice":
Here's a snippet showing how easy it is to issue an access token for a user called "alice" (note: output formatted and abridged for readability):

{% highlight ruby %}
irb(main):001:0> require 'slosilo'
=> true
irb(main):002:0> key = Slosilo::Key.new
=> #<Slosilo::Key:0x00000001999098 @key=#<OpenSSL::PKey::RSA:0x00000001999048>>
irb(main):003:0> puts JSON.pretty_generate(key.signed_token('alice'))
irb(main):003:0> puts key.issue_jwt(sub: 'alice').to_json
{
"data": "alice",
"timestamp": "2017-06-14 15:14:04 UTC",
"signature": "AA2x...rFnF",
"key": "8485e4f593dd2668a062cdaecf28d5bd"
"protected": "eyJhbGciOiJjb25qdXIub3JnL3Nsb3NpbG8vdjIiLCJraWQiOiIzZTY4N2E3N2Q0ZjkzOTkxYzZmMzBkMzkzYTNmZGM1MyJ9",
"payload": "eyJzdWIiOiJhbGljZSIsImlhdCI6MTUwNTgzMjg1NX0=",
"signature": "jzwY1MmbYQUElR8qA8mFOhWq[...]TXc_jJeus-6l2OyHzDx"
}
=> nil
{% endhighlight %}
Expand Down Expand Up @@ -92,7 +91,7 @@ Sequel::Model.db = Sequel.connect ENV['DATABASE_URL']
Slosilo::adapter = Slosilo::Adapters::SequelAdapter.new

# Issue a token
puts Slosilo["authn:#{account}"].signed_token 'alice'
puts Slosilo["authn:#{account}"].issue_jwt(sub: 'alice').to_json
{% endhighlight %}

### Extracting the Signing Key
Expand Down Expand Up @@ -156,7 +155,7 @@ post '/:account/:login/authenticate' do

halt 401 unless login == "public"
halt 404 unless key = Slosilo["authn:#{account}"]
key.signed_token(login).to_json
key.issue_jwt(sub: login).to_json
end
{% endhighlight %}

Expand All @@ -177,7 +176,11 @@ Then send a `POST` request to authenticate as the account user "public":

{% highlight shell %}
$ curl -X POST localhost:3000/myorg/public/authenticate
{"data":"public","timestamp":"2017-06-14 18:18:26 UTC","signature":"DR_9l...c22cd"}
{
"protected": "eyJhbGciOiJjb25qdXIub3JnL3Nsb3NpbG8vdjIiLCJraWQiOiIzZTY4N2E3N2Q0ZjkzOTkxYzZmMzBkMzkzYTNmZGM1MyJ9",
"payload": "eyJzdWIiOiJhbGljZSIsImlhdCI6MTUwNTgzMjg1NX0=",
"signature": "jzwY1MmbYQUElR8qA8[...]c_jJeus-6l2OyHzDx"
}
{% endhighlight %}

Now send a `POST` request to authenticate as the (invalid) account user "alice":
Expand Down
9 changes: 7 additions & 2 deletions docs/tutorials/integrations/ruby.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,19 @@ irb(main)> Conjur.configuration.account = "myorg" # <- REPLACE ME!

{% include toc.md key='authentication' %}

Once the server connection is configured, the next step is to authenticate to obtain an access token. When you create a Conjur Host, the server issues an API key which you can use to authenticate as that host. Here's how you use it in Ruby:
Once the server connection is configured, the next step is to authenticate to obtain an access token. When you create a Conjur Host, the server issues an API key which you can use to authenticate as that host. Here's how you use it in Ruby (note: token formatted and abridged in the interests of readability):

{% highlight ruby %}
irb(main)> host_id = "host/myapp-01"
irb(main)> api_key = "1vgw4jzvyzmay95mrx2s5ad1d28gt3gh2gesb1411kqcah3nrv01r"
irb(main)> conjur = Conjur::API.new_from_key host_id, api_key
irb(main)> puts conjur.token
{"data"=>"admin", "timestamp"=>"2017-06-01 13:26:59 UTC", "signature"=>"NBc5...a7LLJl", "key"=>"ccd789173e1fc4770ac66cd1acf498b4"}
{
"protected": "eyJhbGciOiJjb25qdXIub3JnL3Nsb3NpbG8vdjIiLCJra
WQiOiIzZTY4N2E3N2Q0ZjkzOTkxYzZmMzBkMzkzYTNmZGM1MyJ9",
"payload": "eyJzdWIiOiJhbGljZSIsImlhdCI6MTUwNTgzMjg1NX0=",
"signature": "jzwY1MmbYQUEl[...]6l2OyHzDx"
}
{% endhighlight %}

<div class="note">
Expand Down
10 changes: 5 additions & 5 deletions spec/controllers/authenticate_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
RSpec::Matchers.define :have_valid_token_for do |login|
match do |response|
expect(response).to be_ok
token = JSON.parse response.body
expect(token['data']).to eq(login)
expect(token).to have_key('signature')
expect(token).to have_key('timestamp')
token = Slosilo::JWT.parse_json response.body
expect(token.claims['sub']).to eq(login)
expect(token.signature).to be
expect(token.claims).to have_key('iat')
end
end

Expand Down Expand Up @@ -71,4 +71,4 @@ def invoke
end
end
end
end
end

0 comments on commit 1394490

Please sign in to comment.