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 +4 -4
- data/README.md +6 -4
- data/lib/oauth2c/access_token.rb +25 -2
- data/lib/oauth2c/cache/manager.rb +22 -1
- data/lib/oauth2c/cache/store.rb +10 -7
- data/lib/oauth2c/client.rb +14 -4
- data/lib/oauth2c/grants/refresh_token.rb +2 -2
- data/lib/oauth2c/grants/resource_owner_credentials.rb +2 -2
- data/lib/oauth2c/refinements.rb +5 -1
- data/lib/oauth2c/three_legged.rb +9 -2
- data/lib/oauth2c/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6697c2a55898049be8fa0ce834bc70692fa74a4e
|
|
4
|
+
data.tar.gz: 75faff5bcd7f7666f5d0944b0c13e3efef607825
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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
|
|
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
|
|
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::
|
|
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.
|
data/lib/oauth2c/access_token.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
data/lib/oauth2c/cache/store.rb
CHANGED
|
@@ -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
|
|
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
|
|
31
|
-
return
|
|
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
|
data/lib/oauth2c/client.rb
CHANGED
|
@@ -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
|
-
|
|
34
|
+
@default_scope = default_scope
|
|
34
35
|
|
|
35
|
-
|
|
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
|
data/lib/oauth2c/refinements.rb
CHANGED
|
@@ -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
|
data/lib/oauth2c/three_legged.rb
CHANGED
|
@@ -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(
|
|
39
|
-
|
|
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])
|
data/lib/oauth2c/version.rb
CHANGED
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.
|
|
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-
|
|
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.
|
|
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
|