simple_oauth2 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|