simple_oauth2 0.0.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 +7 -0
- data/.codeclimate.yml +25 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +26 -0
- data/.hound.yml +4 -0
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +12 -0
- data/.travis.yml +31 -0
- data/Gemfile +18 -0
- data/LICENSE +21 -0
- data/README.md +11 -0
- data/Rakefile +11 -0
- data/gemfiles/nobrainer.rb +15 -0
- data/lib/simple_oauth2/configuration/class_accessors.rb +36 -0
- data/lib/simple_oauth2/configuration/constants.rb +36 -0
- data/lib/simple_oauth2/configuration.rb +169 -0
- data/lib/simple_oauth2/generators/authorization.rb +64 -0
- data/lib/simple_oauth2/generators/base.rb +31 -0
- data/lib/simple_oauth2/generators/token.rb +71 -0
- data/lib/simple_oauth2/helpers.rb +40 -0
- data/lib/simple_oauth2/mixins/nobrainer/access_grant.rb +62 -0
- data/lib/simple_oauth2/mixins/nobrainer/access_token.rb +98 -0
- data/lib/simple_oauth2/mixins/nobrainer/client.rb +43 -0
- data/lib/simple_oauth2/resource/bearer.rb +20 -0
- data/lib/simple_oauth2/responses.rb +62 -0
- data/lib/simple_oauth2/scopes.rb +59 -0
- data/lib/simple_oauth2/strategies/authorization_code.rb +22 -0
- data/lib/simple_oauth2/strategies/base.rb +61 -0
- data/lib/simple_oauth2/strategies/client_credentials.rb +21 -0
- data/lib/simple_oauth2/strategies/code.rb +25 -0
- data/lib/simple_oauth2/strategies/password.rb +21 -0
- data/lib/simple_oauth2/strategies/refresh_token.rb +53 -0
- data/lib/simple_oauth2/strategies/token.rb +24 -0
- data/lib/simple_oauth2/uniq_token.rb +20 -0
- data/lib/simple_oauth2/version.rb +26 -0
- data/lib/simple_oauth2.rb +62 -0
- data/logo.png +0 -0
- data/simple_oauth2.gemspec +22 -0
- data/spec/configuration/config_spec.rb +181 -0
- data/spec/configuration/version_spec.rb +11 -0
- data/spec/dummy/endpoints/authorization.rb +15 -0
- data/spec/dummy/endpoints/custom_authorization.rb +21 -0
- data/spec/dummy/endpoints/custom_token.rb +21 -0
- data/spec/dummy/endpoints/status.rb +51 -0
- data/spec/dummy/endpoints/token.rb +22 -0
- data/spec/dummy/orm/nobrainer/app/config/db.rb +8 -0
- data/spec/dummy/orm/nobrainer/app/models/access_grant.rb +3 -0
- data/spec/dummy/orm/nobrainer/app/models/access_token.rb +3 -0
- data/spec/dummy/orm/nobrainer/app/models/client.rb +3 -0
- data/spec/dummy/orm/nobrainer/app/models/user.rb +11 -0
- data/spec/dummy/orm/nobrainer/app/twitter.rb +51 -0
- data/spec/dummy/orm/nobrainer/config.ru +37 -0
- data/spec/dummy/simple_oauth2_config.rb +7 -0
- data/spec/requests/flows/authorization_code_spec.rb +177 -0
- data/spec/requests/flows/client_credentials_spec.rb +163 -0
- data/spec/requests/flows/code_spec.rb +98 -0
- data/spec/requests/flows/password_spec.rb +183 -0
- data/spec/requests/flows/refresh_token_spec.rb +282 -0
- data/spec/requests/flows/token_spec.rb +113 -0
- data/spec/requests/protected_resources_spec.rb +65 -0
- data/spec/requests/revoke_token_spec.rb +90 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/support/helper.rb +11 -0
- metadata +125 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
module Simple
|
2
|
+
module OAuth2
|
3
|
+
module NoBrainer
|
4
|
+
# Includes all the required API, associations, validations and callbacks
|
5
|
+
module AccessGrant
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do # rubocop:disable Metrics/BlockLength
|
9
|
+
include ::NoBrainer::Document
|
10
|
+
include ::NoBrainer::Document::Timestamps
|
11
|
+
|
12
|
+
belongs_to :client, class_name: Simple::OAuth2.config.client_class_name,
|
13
|
+
foreign_key: :client_id, primary_key: :id
|
14
|
+
belongs_to :resource_owner, class_name: Simple::OAuth2.config.resource_owner_class_name,
|
15
|
+
foreign_key: :resource_owner_id, primary_key: :id
|
16
|
+
|
17
|
+
before_save { self.updated_at = Time.now }
|
18
|
+
before_validation :setup_expiration, if: :new_record?
|
19
|
+
|
20
|
+
field :resource_owner_id, type: String, index: true, required: true
|
21
|
+
field :client_id, type: String, index: true, required: true
|
22
|
+
|
23
|
+
field :token,
|
24
|
+
type: String,
|
25
|
+
required: true,
|
26
|
+
uniq: true,
|
27
|
+
index: true,
|
28
|
+
default: -> { Simple::OAuth2.config.token_generator.generate }
|
29
|
+
|
30
|
+
field :redirect_uri, type: String, required: true
|
31
|
+
field :scopes, type: String
|
32
|
+
|
33
|
+
field :revoked_at, type: Time
|
34
|
+
field :expires_at, type: Time, required: true
|
35
|
+
field :created_at, type: Time, required: true, default: -> { Time.now }
|
36
|
+
field :updated_at, type: Time, required: true, default: -> { Time.now }
|
37
|
+
|
38
|
+
class << self
|
39
|
+
def create_for(client, resource_owner, redirect_uri, scopes = nil)
|
40
|
+
create(
|
41
|
+
client_id: client.id,
|
42
|
+
resource_owner_id: resource_owner.id,
|
43
|
+
redirect_uri: redirect_uri,
|
44
|
+
scopes: scopes
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def authenticate(token)
|
49
|
+
where(token: token.to_s).first
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def setup_expiration
|
56
|
+
self.expires_at = Time.now.utc + Simple::OAuth2.config.authorization_code_lifetime if expires_at.nil?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Simple
|
2
|
+
module OAuth2
|
3
|
+
module NoBrainer
|
4
|
+
# Includes all the required API, associations, validations and callbacks
|
5
|
+
module AccessToken
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do # rubocop:disable Metrics/BlockLength
|
9
|
+
include ::NoBrainer::Document
|
10
|
+
include ::NoBrainer::Document::Timestamps
|
11
|
+
|
12
|
+
before_save { self.updated_at = Time.now }
|
13
|
+
before_validation :setup_expiration, if: :new_record?
|
14
|
+
|
15
|
+
belongs_to :client, class_name: Simple::OAuth2.config.client_class_name,
|
16
|
+
foreign_key: :client_id, primary_key: :id
|
17
|
+
belongs_to :resource_owner, class_name: Simple::OAuth2.config.resource_owner_class_name,
|
18
|
+
foreign_key: :resource_owner_id, primary_key: :id
|
19
|
+
|
20
|
+
field :resource_owner_id, type: String, index: true, required: true
|
21
|
+
field :client_id, type: String, index: true, required: true
|
22
|
+
field :token,
|
23
|
+
type: String,
|
24
|
+
index: true,
|
25
|
+
required: true,
|
26
|
+
uniq: true,
|
27
|
+
default: -> { Simple::OAuth2.config.token_generator.generate }
|
28
|
+
field :refresh_token,
|
29
|
+
type: String,
|
30
|
+
index: true,
|
31
|
+
uniq: true,
|
32
|
+
default: -> do
|
33
|
+
if Simple::OAuth2.config.issue_refresh_token
|
34
|
+
Simple::OAuth2.config.token_generator.generate
|
35
|
+
else
|
36
|
+
''
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
field :scopes, type: String
|
41
|
+
|
42
|
+
field :revoked_at, type: Time
|
43
|
+
field :expires_at, type: Time, required: true
|
44
|
+
field :created_at, type: Time, required: true, default: -> { Time.now }
|
45
|
+
field :updated_at, type: Time, required: true, default: -> { Time.now }
|
46
|
+
|
47
|
+
class << self
|
48
|
+
def create_for(client, resource_owner, scopes = nil)
|
49
|
+
create(
|
50
|
+
client_id: client.id,
|
51
|
+
resource_owner_id: resource_owner.id,
|
52
|
+
scopes: scopes
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
def authenticate(token, token_type_hint = nil)
|
57
|
+
return if token.blank?
|
58
|
+
|
59
|
+
if token_type_hint == 'refresh_token'
|
60
|
+
where(refresh_token: token).first
|
61
|
+
else
|
62
|
+
where(token: token).first
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def expired?
|
68
|
+
expires_at && Time.now.utc > expires_at
|
69
|
+
end
|
70
|
+
|
71
|
+
def revoked?
|
72
|
+
revoked_at && revoked_at <= Time.now.utc
|
73
|
+
end
|
74
|
+
|
75
|
+
def revoke!(revoked_at = Time.now.utc)
|
76
|
+
update!(revoked_at: revoked_at)
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_bearer_token
|
80
|
+
{
|
81
|
+
access_token: token,
|
82
|
+
expires_in: expires_at && Simple::OAuth2.config.access_token_lifetime.to_i,
|
83
|
+
refresh_token: refresh_token,
|
84
|
+
scope: scopes
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def setup_expiration
|
91
|
+
expires_in = Simple::OAuth2.config.access_token_lifetime.to_i
|
92
|
+
self.expires_at = Time.now.utc + expires_in if expires_at.nil? && !expires_in.nil?
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Simple
|
2
|
+
module OAuth2
|
3
|
+
module NoBrainer
|
4
|
+
# Includes all the required API, associations, validations and callbacks
|
5
|
+
module Client
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
include ::NoBrainer::Document
|
10
|
+
include ::NoBrainer::Document::Timestamps
|
11
|
+
|
12
|
+
before_save { self.updated_at = Time.now }
|
13
|
+
|
14
|
+
has_many :access_tokens, class_name: Simple::OAuth2.config.access_token_class_name, foreign_key: :client_id
|
15
|
+
has_many :access_grants, class_name: Simple::OAuth2.config.access_grant_class_name, foreign_key: :client_id
|
16
|
+
|
17
|
+
field :name, type: String, required: true
|
18
|
+
field :redirect_uri, type: String, required: true
|
19
|
+
|
20
|
+
field :key,
|
21
|
+
type: String,
|
22
|
+
required: true,
|
23
|
+
index: true,
|
24
|
+
uniq: true,
|
25
|
+
default: -> { Simple::OAuth2.config.token_generator.generate }
|
26
|
+
field :secret,
|
27
|
+
type: String,
|
28
|
+
required: true,
|
29
|
+
index: true,
|
30
|
+
uniq: true,
|
31
|
+
default: -> { Simple::OAuth2.config.token_generator.generate }
|
32
|
+
|
33
|
+
field :created_at, type: Time, required: true, default: -> { Time.now }
|
34
|
+
field :updated_at, type: Time, required: true, default: -> { Time.now }
|
35
|
+
|
36
|
+
def self.authenticate(key)
|
37
|
+
where(key: key.to_s).first
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Simple
|
2
|
+
module OAuth2
|
3
|
+
module Resource
|
4
|
+
# OAuth2 middleware Protected Resource Endpoint
|
5
|
+
class Bearer
|
6
|
+
def initialize(app)
|
7
|
+
@app = app
|
8
|
+
end
|
9
|
+
|
10
|
+
# See https://github.com/nov/rack-oauth2/wiki/Server-Resource-Endpoint
|
11
|
+
def call(env)
|
12
|
+
app = Rack::OAuth2::Server::Resource::Bearer.new(@app, Simple::OAuth2.config.realm) do |req|
|
13
|
+
Simple::OAuth2.config.token_authenticator.call(req)
|
14
|
+
end
|
15
|
+
app.call(env)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Simple
|
2
|
+
module OAuth2
|
3
|
+
# Processes Rack Responses and contains helper methods
|
4
|
+
#
|
5
|
+
# @return [Object] Rack response
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# rack_response = [
|
9
|
+
# 200,
|
10
|
+
# { 'Content-Type' => 'application/json' },
|
11
|
+
# Rack::BodyProxy.new(Rack::Response.new('200'.to_json))
|
12
|
+
# ]
|
13
|
+
# response = Simple::OAuth2::Responses.new(rack_response)
|
14
|
+
#
|
15
|
+
# response.status #=> 200
|
16
|
+
# response.headers #=> {}
|
17
|
+
# response.body #=> '200'
|
18
|
+
# response #=> <Simple::OAuth2::Responses:0x007fc9f32080b8 @response=[
|
19
|
+
# 200,
|
20
|
+
# {},
|
21
|
+
# <Rack::BodyProxy:0x007fc9f3208108
|
22
|
+
# @block=nil,
|
23
|
+
# @body= <Rack::Response:0x007fc9f3208388
|
24
|
+
# @block=nil,
|
25
|
+
# @body=["\"200\""],
|
26
|
+
# @header={"Content-Length"=>"5"},
|
27
|
+
# @length=5,
|
28
|
+
# @status=200
|
29
|
+
# >,
|
30
|
+
# @closed=false
|
31
|
+
# >
|
32
|
+
# ]
|
33
|
+
#
|
34
|
+
class Responses
|
35
|
+
# Simple::OAuth2 response class
|
36
|
+
#
|
37
|
+
# @param response [Array] raw Rack::Response object
|
38
|
+
#
|
39
|
+
def initialize(response)
|
40
|
+
@response = response
|
41
|
+
end
|
42
|
+
|
43
|
+
# Response status
|
44
|
+
def status
|
45
|
+
@response[0]
|
46
|
+
end
|
47
|
+
|
48
|
+
# Response headers
|
49
|
+
def headers
|
50
|
+
@response[1]
|
51
|
+
end
|
52
|
+
|
53
|
+
# Response JSON-parsed body
|
54
|
+
def body
|
55
|
+
response_body = @response[2].body.first
|
56
|
+
return {} if response_body.nil? || response_body.empty?
|
57
|
+
|
58
|
+
JSON.parse(response_body)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Simple
|
2
|
+
module OAuth2
|
3
|
+
# Scopes helper for scopes validation
|
4
|
+
class Scopes
|
5
|
+
# Checks if requested scopes are valid
|
6
|
+
#
|
7
|
+
# @param access_scopes [Array] scopes of AccessToken class
|
8
|
+
# @param scopes [Array<String, Symbol>] array, symbol, string of any object that responds to `to_a`
|
9
|
+
#
|
10
|
+
def self.valid?(access_scopes, scopes)
|
11
|
+
new(access_scopes, scopes).valid?
|
12
|
+
end
|
13
|
+
|
14
|
+
# Helper class initializer
|
15
|
+
#
|
16
|
+
# @param access_scopes [Array] scopes of AccessToken class
|
17
|
+
# @param scopes [Array<String, Symbol>] array, symbol, string of any object that responds to `to_a`
|
18
|
+
#
|
19
|
+
def initialize(access_scopes, scopes = [])
|
20
|
+
@scopes = to_array(scopes)
|
21
|
+
@access_scopes = to_array(access_scopes)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Checks if requested scopes (passed and processed on initialization) are presented in the AccessToken
|
25
|
+
#
|
26
|
+
# @return [Boolean] true if requested scopes are empty or present in access_scopes
|
27
|
+
#
|
28
|
+
def valid?
|
29
|
+
@scopes.empty? || present_in_access_token?
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# Checks if scopes present in access_scopes
|
35
|
+
#
|
36
|
+
# @return [Boolean] true if requested scopes present in access_scopes
|
37
|
+
#
|
38
|
+
def present_in_access_token?
|
39
|
+
Set.new(@access_scopes) >= Set.new(@scopes)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Converts scopes set to the array
|
43
|
+
#
|
44
|
+
# @param scopes [Array<String, Symbol>, #to_a]
|
45
|
+
# string, symbol, array or object that responds to `to_a`
|
46
|
+
# @return [Array<String>] array of scopes
|
47
|
+
#
|
48
|
+
def to_array(scopes)
|
49
|
+
collection = if scopes.is_a?(Array) || scopes.respond_to?(:to_a)
|
50
|
+
scopes.to_a
|
51
|
+
elsif scopes.is_a?(String) || scopes.is_a?(Symbol)
|
52
|
+
scopes.split(',')
|
53
|
+
end
|
54
|
+
|
55
|
+
collection.map(&:to_s)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Simple
|
2
|
+
module OAuth2
|
3
|
+
module Strategies
|
4
|
+
# Authorization Code strategy class
|
5
|
+
# Processes request and respond with Access Token
|
6
|
+
class AuthorizationCode < Base
|
7
|
+
class << self
|
8
|
+
# Processes Authorization Code request
|
9
|
+
def process(request)
|
10
|
+
client = token_verify_client!(request)
|
11
|
+
|
12
|
+
code = authenticate_access_grant(request) || request.invalid_grant!
|
13
|
+
code.redirect_uri == request.redirect_uri || request.invalid_grant!
|
14
|
+
|
15
|
+
token = config.access_token_class.create_for(client, code.resource_owner, code.scopes)
|
16
|
+
expose_to_bearer_token(token)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Simple
|
2
|
+
module OAuth2
|
3
|
+
# Simple::OAuth2 strategies namespace
|
4
|
+
module Strategies
|
5
|
+
# Base Strategies class.
|
6
|
+
# Contains common functionality for all the descendants
|
7
|
+
class Base
|
8
|
+
class << self
|
9
|
+
# Authenticates Client from the request
|
10
|
+
def authenticate_client(request)
|
11
|
+
config.client_class.authenticate(request.client_id)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Authenticates Resource Owner from the request
|
15
|
+
def authenticate_resource_owner(client, request)
|
16
|
+
config.resource_owner_class.oauth_authenticate(
|
17
|
+
client,
|
18
|
+
request.params['username'],
|
19
|
+
request.params['password']
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Authenticates Access Grant from the request
|
24
|
+
def authenticate_access_grant(request)
|
25
|
+
config.access_grant_class.authenticate(request.code)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Exposes token object to Bearer token.
|
29
|
+
#
|
30
|
+
# @param token [AccessToken] any object that responds to `to_bearer_token`
|
31
|
+
# @return [Rack::OAuth2::AccessToken::Bearer] bearer token instance
|
32
|
+
#
|
33
|
+
def expose_to_bearer_token(token)
|
34
|
+
Rack::OAuth2::AccessToken::Bearer.new(token.to_bearer_token)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Token endpoint, check client for exact matching verifier
|
38
|
+
def token_verify_client!(request)
|
39
|
+
client = authenticate_client(request) || request.invalid_client!
|
40
|
+
client.secret == request.client_secret || request.invalid_client!
|
41
|
+
client
|
42
|
+
end
|
43
|
+
|
44
|
+
# Authorization endpoint, check client and redirect_uri for exact matching verifier
|
45
|
+
def authorization_verify_client!(request, response)
|
46
|
+
client = authenticate_client(request) || request.bad_request!
|
47
|
+
response.redirect_uri = request.verify_redirect_uri!(client.redirect_uri)
|
48
|
+
client
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# Short getter for Simple::OAuth2 configuration.
|
54
|
+
def config
|
55
|
+
Simple::OAuth2.config
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Simple
|
2
|
+
module OAuth2
|
3
|
+
module Strategies
|
4
|
+
# ClientCredentials strategy class.
|
5
|
+
# Processes request and respond with Access Token
|
6
|
+
class ClientCredentials < Base
|
7
|
+
class << self
|
8
|
+
# Processes ClientCredentials request
|
9
|
+
def process(request)
|
10
|
+
client = authenticate_client(request) || request.invalid_client!
|
11
|
+
|
12
|
+
resource_owner = authenticate_resource_owner(client, request) || request.invalid_grant!
|
13
|
+
|
14
|
+
token = config.access_token_class.create_for(client, resource_owner, request.scope.join(','))
|
15
|
+
expose_to_bearer_token(token)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Simple
|
2
|
+
module OAuth2
|
3
|
+
module Strategies
|
4
|
+
# Code strategy class.
|
5
|
+
# Processes request and respond with Code
|
6
|
+
class Code < Base
|
7
|
+
class << self
|
8
|
+
# Processes Code request
|
9
|
+
def process(request, response)
|
10
|
+
client = authorization_verify_client!(request, response)
|
11
|
+
|
12
|
+
authorization_code = config.access_grant_class.create_for(
|
13
|
+
client,
|
14
|
+
config.resource_owner_authenticator.call(request),
|
15
|
+
response.redirect_uri,
|
16
|
+
request.scope.join(',')
|
17
|
+
)
|
18
|
+
|
19
|
+
response.code = authorization_code.token
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Simple
|
2
|
+
module OAuth2
|
3
|
+
module Strategies
|
4
|
+
# Resource Owner Password Credentials strategy class
|
5
|
+
# Processes request and respond with Access Token
|
6
|
+
class Password < Base
|
7
|
+
class << self
|
8
|
+
# Processes Password request
|
9
|
+
def process(request)
|
10
|
+
client = token_verify_client!(request)
|
11
|
+
|
12
|
+
resource_owner = authenticate_resource_owner(client, request) || request.invalid_grant!
|
13
|
+
|
14
|
+
token = config.access_token_class.create_for(client, resource_owner, request.scope.join(','))
|
15
|
+
expose_to_bearer_token(token)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Simple
|
2
|
+
module OAuth2
|
3
|
+
module Strategies
|
4
|
+
# Refresh Token strategy class
|
5
|
+
# Processes request and respond with Access Token
|
6
|
+
class RefreshToken < Base
|
7
|
+
class << self
|
8
|
+
# Processes Refresh Token request
|
9
|
+
def process(request)
|
10
|
+
client = token_verify_client!(request)
|
11
|
+
refresh_token = verify_refresh_token!(request, client.id)
|
12
|
+
|
13
|
+
token = config.access_token_class.create_for(
|
14
|
+
client, refresh_token.resource_owner, request.scope.join(',')
|
15
|
+
)
|
16
|
+
run_callback_on_refresh_token(refresh_token) if config.on_refresh_runnable?
|
17
|
+
|
18
|
+
expose_to_bearer_token(token)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# Check refresh token and client id for exact matching verifier
|
24
|
+
def verify_refresh_token!(request, client_id)
|
25
|
+
refresh_token = config.access_token_class.authenticate(request.refresh_token, 'refresh_token')
|
26
|
+
refresh_token || request.invalid_grant!
|
27
|
+
refresh_token.client_id == client_id || request.unauthorized_client!
|
28
|
+
|
29
|
+
refresh_token
|
30
|
+
end
|
31
|
+
|
32
|
+
# Invokes custom callback on Access Token refresh.
|
33
|
+
# If callback is a proc, then call it with token.
|
34
|
+
# If access token responds to callback value (symbol for example), then call it from the token.
|
35
|
+
#
|
36
|
+
# @param access_token [Object] Access Token instance
|
37
|
+
#
|
38
|
+
def run_callback_on_refresh_token(access_token)
|
39
|
+
callback = config.on_refresh
|
40
|
+
|
41
|
+
if callback.respond_to?(:call)
|
42
|
+
callback.call(access_token)
|
43
|
+
elsif access_token.respond_to?(callback)
|
44
|
+
access_token.send(callback)
|
45
|
+
else
|
46
|
+
raise(ArgumentError, ":on_refresh is not a block and Access Token class doesn't respond to #{callback}!")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Simple
|
2
|
+
module OAuth2
|
3
|
+
module Strategies
|
4
|
+
# Token strategy class
|
5
|
+
# Processes request and respond with Access Token
|
6
|
+
class Token < Base
|
7
|
+
class << self
|
8
|
+
# Processes Token request
|
9
|
+
def process(request, response)
|
10
|
+
client = authorization_verify_client!(request, response)
|
11
|
+
|
12
|
+
access_token = config.access_token_class.create_for(
|
13
|
+
client,
|
14
|
+
config.resource_owner_authenticator.call(request),
|
15
|
+
request.scope.join(',')
|
16
|
+
)
|
17
|
+
|
18
|
+
response.access_token = expose_to_bearer_token(access_token)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Simple
|
2
|
+
module OAuth2
|
3
|
+
# OAuth2 helper for generation of unique token values.
|
4
|
+
# Can process custom payload and options
|
5
|
+
module UniqToken
|
6
|
+
# Generates unique token value
|
7
|
+
#
|
8
|
+
# @param _payload [Hash]
|
9
|
+
# payload
|
10
|
+
# @param options [Hash]
|
11
|
+
# options for generator
|
12
|
+
#
|
13
|
+
# @return [String]
|
14
|
+
# unique token value
|
15
|
+
def self.generate(_payload = {}, options = {})
|
16
|
+
SecureRandom.hex(options.delete(:size) || 32)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Simple
|
2
|
+
# Semantic versioning
|
3
|
+
module OAuth2
|
4
|
+
# Simple::OAuth2 version
|
5
|
+
# @return [Gem::Version] version of the gem
|
6
|
+
#
|
7
|
+
def self.gem_version
|
8
|
+
Gem::Version.new VERSION::STRING
|
9
|
+
end
|
10
|
+
|
11
|
+
# Simple::OAuth2 semantic versioning module
|
12
|
+
# Contains detailed info about gem version
|
13
|
+
module VERSION
|
14
|
+
# Level changes for implementation level detail changes, such as small bug fixes
|
15
|
+
PATCH = 0
|
16
|
+
# Level changes for any backwards compatible API changes, such as new functionality/features
|
17
|
+
MINOR = 0
|
18
|
+
# Level changes for backwards incompatible API changes,
|
19
|
+
# such as changes that will break existing users code if they update
|
20
|
+
MAJOR = 0
|
21
|
+
|
22
|
+
# Full gem version string
|
23
|
+
STRING = [MAJOR, MINOR, PATCH].join('.')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|