oauth2c 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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