doorkeeper-jwt_assertion 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 27fd53398a97014fdf2e15738c0a3e8b614fe9c9
4
- data.tar.gz: 122d76d1dfee424e13f85983aaf6e3bbfb69b142
3
+ metadata.gz: b1025f43737a9664dcc50c45898f896b533d3c63
4
+ data.tar.gz: f3b91579bb961c9d970d09016397bbb5e951faa1
5
5
  SHA512:
6
- metadata.gz: 1f58e5c6f1ca803e953c95567729a1aba332173560c27a9c339426f2346603430c60e78f17d3088a8a650e7a79a5db1f893319f66dce708a5a93417a713d0623
7
- data.tar.gz: 57f040de3c1bd245eb7ea76db943003320eade8a2dc8126e3c379def4b499c74c6e5b53417919fa857313d59931c7040ae89ea819a41eb4cf6e74bf3da2237ea
6
+ metadata.gz: 0a275595653379460375328fe8c5d43802d15ac096c48c2e07ad848f23f4063f50989cb0e386c235508a2182c8e8a0c139754ea4a25eeb8ae93481368852f893
7
+ data.tar.gz: 6ce0dbc0869904a8df606fbcc95d68a81c91548cca8b3c62df20c9f18edfdd61c6e8f382e701a1d221ff84918b1afc2ad948bf24c4e145e93ab44e8cc66ae318
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # Doorkeeper JWT Assertion
2
2
 
3
- ## Description
4
-
5
- Extending (Doorkeeper)[https://github.com/doorkeeper-gem/doorkeeper] to support JWT Assertion grant type using a secret or a private key file.
3
+ Extending [Doorkeeper](https://github.com/doorkeeper-gem/doorkeeper) to support JWT Assertion grant type using a secret or a private key file.
6
4
 
7
5
  **This library is in alpha. Future incompatible changes may be necessary.**
8
6
 
@@ -24,13 +22,18 @@ Doorkeeper.configure do
24
22
  jwt_private_key Rails.root.join('config', 'keys', 'private.key')
25
23
 
26
24
  jwt_secret 'notasecret'
25
+
26
+ # Optional
27
+ jwt_use_issuer_as_client_id true
27
28
  end
28
29
  ```
29
30
 
30
31
  This will automatically push `assertion` into the Doorkeeper's grant_types configuration attribute.
31
32
 
32
- You can also use the `resource_owner_authenticator` in the configuration to identify the owner based on the JWT claim values.
33
- If the client request a token with an invalid assertion, an error will be raised. So you can rely on the `jwt` getter if an assertion grant was requested.
33
+ When `jwt_use_issuer_as_client_id` is set to false then the `client_id` MUST be available from the parameters. By default it will extract the 'iss' and use it as the client_id to retrieve the oauth application.
34
+
35
+ Use the `resource_owner_authenticator` in the configuration to identify the owner based on the JWT claim values. This values can be accessible from `jwt`.
36
+ If the client request a token with an invalid assertion, or an expired JWT claim, an :invalid_grant error response will be generated before retrieving the resource_owner.
34
37
 
35
38
  ``` ruby
36
39
  Doorkeeper.configure do
@@ -38,8 +41,7 @@ Doorkeeper.configure do
38
41
  resource_owner_authenticator do
39
42
 
40
43
  if jwt
41
- head :unauthorized unless user = User.where(:email => jwt['prn']).first
42
- return user
44
+ jwt['prn'].present? and User.find_by_email(jwt['prn'])
43
45
  end
44
46
 
45
47
  end
@@ -60,14 +62,20 @@ p12 = OpenSSL::PKCS12.new( Rails.root.join('config', 'keys', 'private.p12').open
60
62
  params = { :private_key => p12.key,
61
63
  :aud => 'audience',
62
64
  :prn => 'person', # or :sub => 'subject', not suported on OAuth2 1.0.0 yet.
63
- :iss => 'issuer',
65
+ :iss => 'client_id',
64
66
  :scope => 'scope',
65
67
  :exp => Time.now.utc.to_i + 5.minutes }
66
68
 
67
69
  token = client.assertion.get_token(params)
68
70
  ```
69
71
 
72
+ > "[...] refresh tokens are not issued
73
+ > in response to assertion grant requests and access tokens will be
74
+ > issued with a reasonably short lifetime."
75
+ > - [draft-ietf-oauth-assertions-18](https://tools.ietf.org/html/draft-ietf-oauth-assertions-18#section-4.1)
76
+
70
77
  ## TO DO
71
78
 
72
79
  * Better error handling
80
+ * JWT Client Authentication Flow
73
81
  * Testing
@@ -7,7 +7,7 @@ require 'jwt'
7
7
  module Doorkeeper
8
8
  module JWTAssertion
9
9
 
10
- attr_reader :jwt
10
+ attr_reader :jwt, :jwt_header
11
11
 
12
12
  end
13
13
  end
@@ -22,6 +22,10 @@ module Doorkeeper
22
22
  context.instance_variable_set('@jwt', jwt)
23
23
  end
24
24
 
25
+ def jwt_header=(jwt_header)
26
+ @jwt_header = jwt_header
27
+ context.instance_variable_set('@jwt_header', jwt_header)
28
+ end
25
29
 
26
30
  end
27
31
  end
@@ -30,6 +34,7 @@ module Doorkeeper
30
34
  class Config
31
35
 
32
36
  option :jwt_key
37
+ option :jwt_use_issuer_as_client_id, :default => true
33
38
 
34
39
  class Builder
35
40
 
@@ -48,10 +53,10 @@ module Doorkeeper
48
53
 
49
54
  Config.class_eval do
50
55
  alias_method :remember_calculate_token_grant_types, :calculate_token_grant_types
51
-
52
56
  define_method :calculate_token_grant_types do
53
- remember_calculate_token_grant_types << 'assertion'
57
+ remember_calculate_token_grant_types << 'assertion' << 'urn:ietf:params:oauth:grant-type:jwt-bearer'
54
58
  end
59
+
55
60
  end
56
61
 
57
62
  jwt_key key
@@ -1,5 +1,5 @@
1
1
  module Doorkeeper
2
2
  module JwtAssertion
3
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
4
4
  end
5
5
  end
@@ -0,0 +1,149 @@
1
+ require 'doorkeeper/oauth/describable_error_response'
2
+
3
+ module Doorkeeper
4
+ module OAuth
5
+ class AssertionAccessTokenRequest
6
+
7
+ include Validations
8
+ include OAuth::RequestConcern
9
+ include OAuth::Helpers
10
+
11
+ attr_accessor :server, :original_scopes
12
+ attr_reader :resource_owner, :client, :configuration, :access_token, :response, :error_description
13
+
14
+ validate :assertion, error: :invalid_grant
15
+ validate :client, error: :invalid_client
16
+ validate :scopes, error: :invalid_scope
17
+ validate :resource_owner, error: :invalid_grant
18
+ validate :access_token, error: :invalid_grant
19
+
20
+
21
+ ##
22
+ # @see https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12#section-2.1
23
+ #
24
+ def initialize(server, configuration)
25
+ @server = server
26
+ @configuration = configuration
27
+ @response = nil
28
+ @original_scopes = server.parameters[:scope]
29
+ end
30
+
31
+ def authorize
32
+ validate
33
+ if valid?
34
+ @response = TokenResponse.new(access_token)
35
+ else
36
+ @response = DescribableErrorResponse.from_request(self)
37
+ @response.description = error_description
38
+ @response
39
+ end
40
+ end
41
+
42
+
43
+
44
+ private
45
+
46
+ ##
47
+ # > The value of the "grant_type" is "urn:ietf:params:oauth:grant-
48
+ # > type:jwt-bearer".
49
+ # > The value of the "assertion" parameter MUST contain a single JWT.
50
+ # > - draft-ietf-oauth-jwt-bearer-12
51
+ #
52
+ # @see https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12#section-2.1
53
+ #
54
+ # > assertion_type
55
+ # > REQUIRED. The format of the assertion as defined by the
56
+ # > authorization server. The value MUST be an absolute URI.
57
+ # >
58
+ # > assertion
59
+ # > REQUIRED. The assertion.
60
+ # >
61
+ # > - draft-ietf-oauth-v2-10
62
+ # @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.1.3
63
+ #
64
+ # Newer versions of ietf-oauth-v2 don't need assertion_type. So it's still optional.
65
+ def validate_assertion
66
+ assertion, assertion_type = server.parameters.values_at(:assertion, :assertion_type)
67
+
68
+ if assertion_type and assertion_type != 'urn:ietf:params:oauth:grant-type:jwt-bearer'
69
+ raise StandardError.new('Assertion type not valid. Expected urn:ietf:params:oauth:grant-type:jwt-bearer')
70
+ end
71
+
72
+ payload, header = JWT.decode(assertion, configuration.jwt_key)
73
+ server.jwt = payload
74
+ server.jwt_header = header
75
+
76
+ rescue => error
77
+ @error_description = error.message
78
+ false
79
+ end
80
+
81
+
82
+
83
+ ##
84
+ # If `jwt_use_issuer_as_client_id` is `true` then validate the client using the issuer as the client_id.
85
+ # Otherwise, use the client_id directly from the parameters.
86
+ #
87
+ # > Authentication of the client is optional, as described in
88
+ # > Section 3.2.1 of OAuth 2.0 [RFC6749] and consequently, the
89
+ # > "client_id" is only needed when a form of client authentication that
90
+ # > relies on the parameter is used.
91
+ # > - draft-ietf-oauth-assertions-18
92
+ #
93
+ # @see https://tools.ietf.org/html/draft-ietf-oauth-assertions-18#section-4.1
94
+ #
95
+ # > The JWT MUST contain an "iss" (issuer) claim that contains a
96
+ # > unique identifier for the entity that issued the JWT.
97
+ # > - draft-ietf-oauth-jwt-bearer-12
98
+ #
99
+ # @see https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12#section-3
100
+ #
101
+ def validate_client
102
+ @client ||= if configuration.jwt_use_issuer_as_client_id
103
+ OAuth::Client.find(server.jwt['iss']) if server.jwt['iss'].present?
104
+
105
+ elsif sever.parameters[:client_id].present?
106
+ OAuth::Client.find( sever.parameters[:client_id] )
107
+
108
+ end
109
+ end
110
+
111
+
112
+ ##
113
+ # > The "scope" parameter may be used, as defined in the Assertion
114
+ # > Framework for OAuth 2.0 Client Authentication and Authorization
115
+ # > Grants [I-D.ietf-oauth-assertions] specification, to indicate the
116
+ # > requested scope.
117
+ #
118
+ # @see https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12#section-2.1
119
+ #
120
+ def validate_scopes
121
+ return true unless @original_scopes.present?
122
+ ScopeChecker.valid? @original_scopes, configuration.scopes, client.try(:scopes)
123
+ end
124
+
125
+
126
+ def validate_resource_owner
127
+ resource_owner || (@resource_owner = server.current_resource_owner)
128
+ end
129
+
130
+ def validate_access_token
131
+ access_token or create_token
132
+ end
133
+
134
+
135
+ ##
136
+ # > [...] refresh tokens are not issued
137
+ # > in response to assertion grant requests and access tokens will be
138
+ # > issued with a reasonably short lifetime.
139
+ #
140
+ # @see https://tools.ietf.org/html/draft-ietf-oauth-assertions-18#section-4.1
141
+ #
142
+ def create_token
143
+ expires_in = Authorization::Token.access_token_expires_in(configuration, client)
144
+ @access_token = AccessToken.find_or_create_for(client, resource_owner.id, scopes, expires_in, false)
145
+ end
146
+
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,8 @@
1
+ module Doorkeeper
2
+ module OAuth
3
+
4
+ class DescribableErrorResponse < ErrorResponse
5
+ attr_accessor :description
6
+ end
7
+ end
8
+ end
@@ -1,38 +1,27 @@
1
+ require 'doorkeeper/oauth/assertion_access_token_request'
2
+
1
3
  module Doorkeeper
2
4
  module Request
3
5
 
4
6
  class Assertion
5
7
  def self.build(server)
6
- assertion = server.parameters[:assertion]
7
- begin
8
- jwt = JWT.decode(assertion, Doorkeeper.configuration.jwt_key)
9
- rescue JWT::ExpiredSignature => e
10
- raise Errors::ExpiredSignature
11
- end
12
- server.jwt = jwt.is_a?(Array) ? jwt.first : jwt
13
-
14
- new(server.credentials, server.current_resource_owner, server)
8
+ new(server)
15
9
  end
16
10
 
17
- attr_accessor :credentials, :resource_owner, :server
11
+ attr_reader :server
18
12
 
19
- def initialize(credentials, resource_owner, server)
20
- @credentials = credentials
21
- @resource_owner = resource_owner
13
+ def initialize(server)
22
14
  @server = server
23
15
  end
24
16
 
25
17
  def request
26
- @request ||= OAuth::PasswordAccessTokenRequest.new(
27
- Doorkeeper.configuration,
28
- credentials,
29
- resource_owner,
30
- server.parameters)
18
+ @request ||= OAuth::AssertionAccessTokenRequest.new(server, Doorkeeper.configuration)
31
19
  end
32
20
 
33
21
  def authorize
34
22
  request.authorize
35
23
  end
24
+
36
25
  end
37
26
 
38
27
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: doorkeeper-jwt_assertion
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Omac
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-27 00:00:00.000000000 Z
11
+ date: 2015-04-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: doorkeeper
@@ -83,6 +83,8 @@ files:
83
83
  - lib/doorkeeper/jwt_assertion.rb
84
84
  - lib/doorkeeper/jwt_assertion/railtie.rb
85
85
  - lib/doorkeeper/jwt_assertion/version.rb
86
+ - lib/doorkeeper/oauth/assertion_access_token_request.rb
87
+ - lib/doorkeeper/oauth/describable_error_response.rb
86
88
  - lib/doorkeeper/request/assertion.rb
87
89
  homepage: https://github.com/kioru/doorkeeper-jwt_assertion
88
90
  licenses: