oauth2c 0.1.0 → 0.2.0

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: 97d040c802c43d0eb1e4f0c6ea2884699e58fc16
4
- data.tar.gz: 64b1657921b0c240465bf84a352b7ab71714bf52
3
+ metadata.gz: 6697c2a55898049be8fa0ce834bc70692fa74a4e
4
+ data.tar.gz: 75faff5bcd7f7666f5d0944b0c13e3efef607825
5
5
  SHA512:
6
- metadata.gz: 47f34578cb60dc843c7d5a3badd18d6b11b74de81514cbbfa894e2929389e674241e6d43dcad1416a7b37d875ec28724ffa106f9e01edce704aca222c0d19ef2
7
- data.tar.gz: 838e1f16877989fa75b8644872ea5f7deb15bc25bce877206268b71d4f9e408557ffece729ed97012441ddc00bc84c640b5d92e8fc8e32df1b14d6d92222bd64
6
+ metadata.gz: 953c28244c6d6d30a258f9c8a07e84ab68a8293a8e28075b0986fd221e54e0f1a853da2bb4de8cf34f55eda0ef301c4c7bc75e936eedfa8a7d2e0dcc76f0ab68
7
+ data.tar.gz: 37dcfb0f767d88f62e24aecc032e5f85c4bb3e5b97adadda90fe4f61f239eb1b19d26c7774ee75f88f74a9e5f4d9102149a007075a75af11df914b7d97cfa713
data/README.md CHANGED
@@ -46,7 +46,7 @@ This gem ships with following grant types:
46
46
 
47
47
  #### Authorization Code Grant
48
48
 
49
- As described by https://tools.ietf.org/html/rfc6749#section-4.1, the client generates a URL and redirects the user-agent it:
49
+ As described by https://tools.ietf.org/html/rfc6749#section-4.1, the client generates a URL and redirects the user-agent:
50
50
 
51
51
  ```ruby
52
52
  grant = OAUTH2C_CLIENT.authorization_code(state: "STATE", scope: ["profile", "email"])
@@ -60,9 +60,9 @@ grant = OAUTH2C_CLIENT.authorization_code(state: "STATE", scope: ["profile", "em
60
60
  grant.token(url)
61
61
  ```
62
62
 
63
- #### Implicit Flow
63
+ #### Implicit Grant
64
64
 
65
- As described by https://tools.ietf.org/html/rfc6749#section-4.2, the client generates a URL and redirects the user-agent it:
65
+ As described by https://tools.ietf.org/html/rfc6749#section-4.2, the client generates a URL and redirects the user-agent:
66
66
 
67
67
  ```ruby
68
68
  grant = OAUTH2C_CLIENT.implicit(state: "STATE", scope: ["profile", "email"])
@@ -102,7 +102,7 @@ grant.token
102
102
  As described by https://tools.ietf.org/html/rfc7521 and https://tools.ietf.org/html/rfc7523, the client issues a token on behalf of user without requiring the user approval. Instead, the client provides a assertion with the information about the user.
103
103
 
104
104
  ```ruby
105
- profile = OAuth2c::Grants::Assertion::JWT.new(
105
+ profile = OAuth2c::Grants::Assertion::JWTProfile.new(
106
106
  "HS512",
107
107
  "assertion-key",
108
108
  iss: "https://myapp.example",
@@ -154,6 +154,8 @@ For example, if the cached access token for a key is present and has the scope `
154
154
 
155
155
  However, whenever there is a cached token without the necessary scopes, a new token will be issued with a scope matching the union of the cached token's scope and the scope of the token being requested. In the example above, a new token with the scopes `["basic", "profile", "other"]` would be requested.
156
156
 
157
+ In addition to `OAuth2c::Cache::Backends::InMemoryLRU`, the gem also ships a Redis backend `OAuth2c::Cache::Backends::Redis` and a null service backend `OAuth2c::Cache::Backends::Null` which is useful to prevent caching while still using the cache manager and keeping the API the same.
158
+
157
159
  ## Development
158
160
 
159
161
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -12,6 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ require "time"
16
+
15
17
  module OAuth2c
16
18
  class AccessToken
17
19
  attr_reader(
@@ -23,7 +25,7 @@ module OAuth2c
23
25
  :extra,
24
26
  )
25
27
 
26
- def initialize(access_token:, token_type:, expires_in:, refresh_token: nil, **extra)
28
+ def initialize(access_token:, token_type:, expires_in:, expires_at: nil, refresh_token: nil, **extra)
27
29
  @access_token = access_token
28
30
  @token_type = token_type
29
31
  @expires_in = Integer(expires_in)
@@ -33,7 +35,7 @@ module OAuth2c
33
35
  extra.delete(:expires_at)
34
36
  @extra = extra
35
37
 
36
- @expires_at = Time.now + @expires_in
38
+ @expires_at = normalize_time(expires_at) || Time.now + @expires_in
37
39
  end
38
40
 
39
41
  def attributes
@@ -47,12 +49,33 @@ module OAuth2c
47
49
  }
48
50
  end
49
51
 
52
+ def expired?(leeway = 0)
53
+ @expires_at - leeway < Time.now
54
+ end
55
+
50
56
  def ==(other)
57
+ return false unless other.is_a?(self.class)
58
+
51
59
  access_token == other.access_token &&
52
60
  token_type == other.token_type &&
53
61
  expires_in == other.expires_in &&
54
62
  refresh_token == other.refresh_token &&
55
63
  extra == other.extra
56
64
  end
65
+
66
+ private
67
+
68
+ def normalize_time(time)
69
+ case time
70
+ when Time, NilClass
71
+ time
72
+ when String
73
+ Time.parse(time)
74
+ when Integer
75
+ Time.at(time)
76
+ else
77
+ raise ArgumentError, "invalid time #{time.inspect}"
78
+ end
79
+ end
57
80
  end
58
81
  end
@@ -19,18 +19,39 @@ module OAuth2c
19
19
  class Manager
20
20
  extend Forwardable
21
21
 
22
+ def_delegators(:@client,
23
+ :authz_url,
24
+ :token_url,
25
+ :client_id,
26
+ :client_secret,
27
+ :redirect_uri,
28
+ :default_scope,
29
+ )
30
+
22
31
  def initialize(client, cache_backend)
23
32
  @client = client
24
33
  @cache = Cache::Store.new(cache_backend)
25
34
  end
26
35
 
27
- def_delegators :@cache, :cached?, :cached
36
+ def cached?(key, scope: @client.default_scope)
37
+ @cache.cached?(key, scope: scope)
38
+ end
39
+
40
+ def cached(key, scope: @client.default_scope)
41
+ @cache.cached(key, scope: scope)
42
+ end
28
43
 
29
44
  def method_missing(name, key, *args, **opts)
30
45
  grant = @client.public_send(name, *args, **opts)
31
46
  CacheProxy.new(@cache, key, grant)
32
47
  end
33
48
 
49
+ def respond_to_missing?(name, include_private = false)
50
+ @client.respond_to?(name) || super
51
+ end
52
+
53
+ private
54
+
34
55
  class CacheProxy < BasicObject
35
56
  def initialize(cache, key, grant)
36
57
  @cache = cache
@@ -17,8 +17,9 @@ module OAuth2c
17
17
  class Store
18
18
  Bucket = Struct.new(:access_token, :scope)
19
19
 
20
- def initialize(backend)
21
- @backend = backend
20
+ def initialize(backend, exp_leeway: 300)
21
+ @backend = backend
22
+ @exp_leeway = exp_leeway
22
23
  end
23
24
 
24
25
  def cached?(key, scope: [])
@@ -26,21 +27,23 @@ module OAuth2c
26
27
  end
27
28
 
28
29
  def cached(key, scope: [])
30
+ return nil if key.nil?
31
+
29
32
  cache = @backend.lookup(key)
30
- return false if cache.nil?
31
- return false unless scope.all? { |s| cache.scope.include?(s) }
33
+ return nil if cache.nil?
34
+ return nil unless scope.all? { |s| cache.scope.include?(s) }
32
35
 
33
- if cache.access_token.expires_at >= Time.now
36
+ if cache.access_token.expires_at - @exp_leeway >= Time.now
34
37
  cache.access_token
35
38
  end
36
39
  end
37
40
 
38
41
  def issue(key, scope:, &block)
39
- cached = @backend.lookup(key)
42
+ cached = @backend.lookup(key) unless key.nil?
40
43
  scope = cached[:scope] | scope if cached
41
44
 
42
45
  access_token = block.call(scope)
43
- @backend.store(key, Bucket.new(access_token, scope))
46
+ @backend.store(key, Bucket.new(access_token, scope)) unless key.nil?
44
47
  access_token
45
48
  end
46
49
  end
@@ -22,22 +22,32 @@ module OAuth2c
22
22
  :client_id,
23
23
  :client_secret,
24
24
  :redirect_uri,
25
+ :default_scope,
25
26
  )
26
27
 
27
- def initialize(authz_url: nil, token_url:, client_id:, client_secret: nil, redirect_uri: nil)
28
+ def initialize(authz_url: nil, token_url:, client_id:, client_secret: nil, redirect_uri: nil, default_scope: [])
28
29
  @authz_url = authz_url
29
30
  @token_url = token_url
30
31
  @client_id = client_id
31
32
  @client_secret = client_secret
32
33
  @redirect_uri = redirect_uri
33
- end
34
+ @default_scope = default_scope
34
35
 
35
- def method_missing(name, *_, **opts)
36
- Grants.const_get(name.to_s.camelize).new(build_agent, **opts)
36
+ define_grant_methods!
37
37
  end
38
38
 
39
39
  private
40
40
 
41
+ def define_grant_methods!
42
+ Grants.constants.each do |name|
43
+ const = Grants.const_get(name)
44
+
45
+ define_singleton_method("#{name.to_s.underscore}") do |*_, scope: @default_scope, **opts|
46
+ const.new(build_agent, scope: scope, **opts)
47
+ end
48
+ end
49
+ end
50
+
41
51
  def build_agent
42
52
  Agent.new(
43
53
  authz_url: @authz_url,
@@ -15,8 +15,8 @@
15
15
  module OAuth2c
16
16
  module Grants
17
17
  class RefreshToken < OAuth2c::TwoLegged::Base
18
- def initialize(agent, refresh_token:)
19
- super(agent)
18
+ def initialize(agent, refresh_token:, **opts)
19
+ super(agent, **opts)
20
20
  @refresh_token = refresh_token
21
21
  end
22
22
 
@@ -15,8 +15,8 @@
15
15
  module OAuth2c
16
16
  module Grants
17
17
  class ResourceOwnerCredentials < OAuth2c::TwoLegged::Base
18
- def initialize(agent, username:, password:)
19
- super(agent)
18
+ def initialize(agent, username:, password:, **opts)
19
+ super(agent, **opts)
20
20
  @username = username
21
21
  @password = password
22
22
  end
@@ -4,6 +4,10 @@ module OAuth2c
4
4
  def camelize
5
5
  gsub(/(?:\A|_)([a-z])/) { $1.upcase }
6
6
  end
7
+
8
+ def underscore
9
+ gsub(/(\A|[a-z])([A-Z])/) { $1.empty?? $2.downcase : "#{$1}_#{$2.downcase}" }
10
+ end
7
11
  end
8
12
 
9
13
  refine Hash do
@@ -13,7 +17,7 @@ module OAuth2c
13
17
  end
14
18
 
15
19
  def symbolize_keys
16
- transform_keys{ |key| key.to_sym rescue key }
20
+ transform_keys { |key| key.to_sym rescue key }
17
21
  end
18
22
 
19
23
  def stringify_keys
@@ -35,8 +35,15 @@ module OAuth2c
35
35
  @agent.authz_url(state: @state, scope: @scope, **authz_params)
36
36
  end
37
37
 
38
- def token(callback_url)
39
- query_params, fragment_params = parse_callback_url(callback_url)
38
+ def token(callback_url_or_params, fragment_params = nil)
39
+ case callback_url_or_params
40
+ when String
41
+ query_params, fragment_params = parse_callback_url(callback_url_or_params)
42
+ when Hash
43
+ query_params, fragment_params = callback_url_or_params, fragment_params
44
+ else
45
+ raise ArgumentError, "invalid arguments, expects URL or hash with params"
46
+ end
40
47
 
41
48
  if query_params[:error]
42
49
  raise Error.new(query_params[:error], query_params[:error_description])
@@ -13,5 +13,5 @@
13
13
  # limitations under the License.
14
14
 
15
15
  module OAuth2c
16
- VERSION = "0.1.0"
16
+ VERSION = "0.2.0"
17
17
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oauth2c
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Kochenburger
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-03-10 00:00:00.000000000 Z
11
+ date: 2017-04-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http
@@ -197,7 +197,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
197
197
  version: '0'
198
198
  requirements: []
199
199
  rubyforge_project:
200
- rubygems_version: 2.6.8
200
+ rubygems_version: 2.5.1
201
201
  signing_key:
202
202
  specification_version: 4
203
203
  summary: OAuth2c is a extensible OAuth2 client implementation