oidc 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +2 -0
- data/.rubocop.yml +28 -0
- data/CHANGELOG.md +4 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/LICENSE.txt +21 -0
- data/README.md +46 -0
- data/Rakefile +12 -0
- data/lib/oidc/access_token/mtls.rb +9 -0
- data/lib/oidc/access_token.rb +45 -0
- data/lib/oidc/client/registrar.rb +186 -0
- data/lib/oidc/client.rb +43 -0
- data/lib/oidc/connect_object.rb +52 -0
- data/lib/oidc/discovery/provider/config/resource.rb +39 -0
- data/lib/oidc/discovery/provider/config/response.rb +112 -0
- data/lib/oidc/discovery/provider/config.rb +20 -0
- data/lib/oidc/discovery/provider.rb +34 -0
- data/lib/oidc/discovery.rb +8 -0
- data/lib/oidc/exception.rb +39 -0
- data/lib/oidc/jwtnizable.rb +14 -0
- data/lib/oidc/request_object/claimable.rb +54 -0
- data/lib/oidc/request_object/id_token.rb +8 -0
- data/lib/oidc/request_object/user_info.rb +7 -0
- data/lib/oidc/request_object.rb +37 -0
- data/lib/oidc/response_object/id_token.rb +99 -0
- data/lib/oidc/response_object/user_info/address.rb +10 -0
- data/lib/oidc/response_object/user_info.rb +65 -0
- data/lib/oidc/response_object.rb +8 -0
- data/lib/oidc/version.rb +5 -0
- data/lib/oidc.rb +98 -0
- data/lib/rack/oauth2/server/authorize/error_with_connect_ext.rb +34 -0
- data/lib/rack/oauth2/server/authorize/extension/code_and_id_token.rb +40 -0
- data/lib/rack/oauth2/server/authorize/extension/code_and_id_token_and_token.rb +36 -0
- data/lib/rack/oauth2/server/authorize/extension/id_token.rb +40 -0
- data/lib/rack/oauth2/server/authorize/extension/id_token_and_token.rb +36 -0
- data/lib/rack/oauth2/server/authorize/request_with_connect_params.rb +26 -0
- data/lib/rack/oauth2/server/id_token_response.rb +24 -0
- data/oidc.gemspec +46 -0
- data/sig/omniauth_oidc.rbs +4 -0
- metadata +252 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
module Oidc
|
2
|
+
module Discovery
|
3
|
+
module Provider
|
4
|
+
class Config
|
5
|
+
class Response
|
6
|
+
include ActiveModel::Validations, AttrRequired, AttrOptional
|
7
|
+
|
8
|
+
cattr_accessor :metadata_attributes
|
9
|
+
attr_reader :raw
|
10
|
+
attr_accessor :expected_issuer
|
11
|
+
uri_attributes = {
|
12
|
+
required: [
|
13
|
+
:issuer,
|
14
|
+
:authorization_endpoint,
|
15
|
+
:jwks_uri
|
16
|
+
],
|
17
|
+
optional: [
|
18
|
+
:token_endpoint,
|
19
|
+
:userinfo_endpoint,
|
20
|
+
:registration_endpoint,
|
21
|
+
:end_session_endpoint,
|
22
|
+
:service_documentation,
|
23
|
+
:check_session_iframe,
|
24
|
+
:op_policy_uri,
|
25
|
+
:op_tos_uri
|
26
|
+
]
|
27
|
+
}
|
28
|
+
attr_required(*(uri_attributes[:required] + [
|
29
|
+
:response_types_supported,
|
30
|
+
:subject_types_supported,
|
31
|
+
:id_token_signing_alg_values_supported
|
32
|
+
]))
|
33
|
+
attr_optional(*(uri_attributes[:optional] + [
|
34
|
+
:scopes_supported,
|
35
|
+
:response_modes_supported,
|
36
|
+
:grant_types_supported,
|
37
|
+
:acr_values_supported,
|
38
|
+
:id_token_encryption_alg_values_supported,
|
39
|
+
:id_token_encryption_enc_values_supported,
|
40
|
+
:userinfo_signing_alg_values_supported,
|
41
|
+
:userinfo_encryption_alg_values_supported,
|
42
|
+
:userinfo_encryption_enc_values_supported,
|
43
|
+
:request_object_signing_alg_values_supported,
|
44
|
+
:request_object_encryption_alg_values_supported,
|
45
|
+
:request_object_encryption_enc_values_supported,
|
46
|
+
:token_endpoint_auth_methods_supported,
|
47
|
+
:token_endpoint_auth_signing_alg_values_supported,
|
48
|
+
:display_values_supported,
|
49
|
+
:claim_types_supported,
|
50
|
+
:claims_supported,
|
51
|
+
:claims_locales_supported,
|
52
|
+
:ui_locales_supported,
|
53
|
+
:claims_parameter_supported,
|
54
|
+
:request_parameter_supported,
|
55
|
+
:request_uri_parameter_supported,
|
56
|
+
:require_request_uri_registration
|
57
|
+
]))
|
58
|
+
|
59
|
+
validates(*required_attributes, presence: true)
|
60
|
+
validates(*uri_attributes.values.flatten, url: true, allow_nil: true)
|
61
|
+
validates :issuer, with: :validate_issuer_matching
|
62
|
+
|
63
|
+
def initialize(hash)
|
64
|
+
(required_attributes + optional_attributes).each do |key|
|
65
|
+
self.send "#{key}=", hash[key]
|
66
|
+
end
|
67
|
+
@raw = hash
|
68
|
+
end
|
69
|
+
|
70
|
+
def as_json(options = {})
|
71
|
+
validate!
|
72
|
+
(required_attributes + optional_attributes).inject({}) do |hash, _attr_|
|
73
|
+
value = self.send _attr_
|
74
|
+
hash.merge! _attr_ => value unless value.nil?
|
75
|
+
hash
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def validate!
|
80
|
+
valid? or raise ValidationFailed.new(self)
|
81
|
+
end
|
82
|
+
|
83
|
+
def jwks
|
84
|
+
@jwks ||= Oidc.http_client.get(jwks_uri).body.with_indifferent_access
|
85
|
+
JSON::JWK::Set.new @jwks[:keys]
|
86
|
+
end
|
87
|
+
|
88
|
+
def jwk(kid)
|
89
|
+
@jwks ||= {}
|
90
|
+
@jwks[kid] ||= JSON::JWK::Set::Fetcher.fetch(jwks_uri, kid: kid)
|
91
|
+
end
|
92
|
+
|
93
|
+
def public_keys
|
94
|
+
@public_keys ||= jwks.collect(&:to_key)
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def validate_issuer_matching
|
100
|
+
if expected_issuer.present? && issuer != expected_issuer
|
101
|
+
if Oidc.validate_discovery_issuer
|
102
|
+
errors.add :issuer, 'mismatch'
|
103
|
+
else
|
104
|
+
Oidc.logger.warn 'ignoring issuer mismach.'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Oidc
|
2
|
+
module Discovery
|
3
|
+
module Provider
|
4
|
+
class Config
|
5
|
+
def self.discover!(identifier, cache_options = {})
|
6
|
+
uri = URI.parse(identifier)
|
7
|
+
Resource.new(uri).discover!(cache_options).tap do |response|
|
8
|
+
response.expected_issuer = identifier
|
9
|
+
response.validate!
|
10
|
+
end
|
11
|
+
rescue SWD::Exception, ValidationFailed => e
|
12
|
+
raise DiscoveryFailed.new(e.message)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'oidc/discovery/provider/config/resource'
|
20
|
+
require 'oidc/discovery/provider/config/response'
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Oidc
|
2
|
+
module Discovery
|
3
|
+
module Provider
|
4
|
+
module Issuer
|
5
|
+
REL_VALUE = 'http://openid.net/specs/connect/1.0/issuer'
|
6
|
+
|
7
|
+
def issuer
|
8
|
+
self.link_for(REL_VALUE)[:href]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.discover!(identifier)
|
13
|
+
resource = case identifier
|
14
|
+
when /^acct:/, /https?:\/\//
|
15
|
+
identifier
|
16
|
+
when /@/
|
17
|
+
"acct:#{identifier}"
|
18
|
+
else
|
19
|
+
"https://#{identifier}"
|
20
|
+
end
|
21
|
+
response = WebFinger.discover!(
|
22
|
+
resource,
|
23
|
+
rel: Issuer::REL_VALUE
|
24
|
+
)
|
25
|
+
response.extend Issuer
|
26
|
+
response
|
27
|
+
rescue WebFinger::Exception => e
|
28
|
+
raise DiscoveryFailed.new(e.message)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'oidc/discovery/provider/config'
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Oidc
|
2
|
+
class Exception < StandardError; end
|
3
|
+
|
4
|
+
class ValidationFailed < Exception
|
5
|
+
attr_reader :object
|
6
|
+
|
7
|
+
def initialize(object)
|
8
|
+
super object.errors.full_messages.to_sentence
|
9
|
+
@object = object
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class HttpError < Exception
|
14
|
+
attr_accessor :status, :response
|
15
|
+
def initialize(status, message = nil, response = nil)
|
16
|
+
super message
|
17
|
+
@status = status
|
18
|
+
@response = response
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class BadRequest < HttpError
|
23
|
+
def initialize(message = nil, response = nil)
|
24
|
+
super 400, message, response
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Unauthorized < HttpError
|
29
|
+
def initialize(message = nil, response = nil)
|
30
|
+
super 401, message, response
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Forbidden < HttpError
|
35
|
+
def initialize(message = nil, response = nil)
|
36
|
+
super 403, message, response
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Oidc
|
2
|
+
module JWTnizable
|
3
|
+
def to_jwt(key, algorithm = :RS256, &block)
|
4
|
+
as_jwt(key, algorithm, &block).to_s
|
5
|
+
end
|
6
|
+
|
7
|
+
def as_jwt(key, algorithm = :RS256, &block)
|
8
|
+
token = JSON::JWT.new as_json
|
9
|
+
yield token if block_given?
|
10
|
+
token = token.sign key, algorithm if algorithm != :none
|
11
|
+
token
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Oidc
|
2
|
+
class RequestObject
|
3
|
+
module Claimable
|
4
|
+
def self.included(klass)
|
5
|
+
klass.send :attr_optional, :claims
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(attributes = {})
|
9
|
+
super
|
10
|
+
if claims.present?
|
11
|
+
_claims_ = {}
|
12
|
+
claims.each do |key, value|
|
13
|
+
_claims_[key] = case value
|
14
|
+
when :optional, :voluntary
|
15
|
+
{
|
16
|
+
essential: false
|
17
|
+
}
|
18
|
+
when :required, :essential
|
19
|
+
{
|
20
|
+
essential: true
|
21
|
+
}
|
22
|
+
else
|
23
|
+
value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
self.claims = _claims_.with_indifferent_access
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def as_json(options = {})
|
31
|
+
keys = claims.try(:keys)
|
32
|
+
hash = super
|
33
|
+
Array(keys).each do |key|
|
34
|
+
hash[:claims][key] ||= nil
|
35
|
+
end
|
36
|
+
hash
|
37
|
+
end
|
38
|
+
|
39
|
+
def required?(claim)
|
40
|
+
accessible?(claim) && claims[claim].is_a?(Hash) && claims[claim][:essential]
|
41
|
+
end
|
42
|
+
alias_method :essential?, :required?
|
43
|
+
|
44
|
+
def optional?(claim)
|
45
|
+
accessible?(claim) && !required?(claim)
|
46
|
+
end
|
47
|
+
alias_method :voluntary?, :optional?
|
48
|
+
|
49
|
+
def accessible?(claim)
|
50
|
+
claims.try(:include?, claim)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Oidc
|
2
|
+
class RequestObject < ConnectObject
|
3
|
+
include JWTnizable
|
4
|
+
|
5
|
+
attr_optional :client_id, :response_type, :redirect_uri, :scope, :state, :nonce, :display, :prompt, :userinfo, :id_token
|
6
|
+
validate :require_at_least_one_attributes
|
7
|
+
|
8
|
+
undef :id_token=
|
9
|
+
def id_token=(attributes = {})
|
10
|
+
@id_token = IdToken.new(attributes) if attributes.present?
|
11
|
+
end
|
12
|
+
|
13
|
+
undef :userinfo=
|
14
|
+
def userinfo=(attributes = {})
|
15
|
+
@userinfo = UserInfo.new(attributes) if attributes.present?
|
16
|
+
end
|
17
|
+
|
18
|
+
def as_json(options = {})
|
19
|
+
super.with_indifferent_access
|
20
|
+
end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def decode(jwt_string, key = nil)
|
24
|
+
new JSON::JWT.decode(jwt_string, key)
|
25
|
+
end
|
26
|
+
|
27
|
+
def fetch(request_uri, key = nil)
|
28
|
+
jwt_string = Oidc.http_client.get(request_uri).body
|
29
|
+
decode jwt_string, key
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'oidc/request_object/claimable'
|
36
|
+
require 'oidc/request_object/id_token'
|
37
|
+
require 'oidc/request_object/user_info'
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Oidc
|
2
|
+
class ResponseObject
|
3
|
+
class IdToken < ConnectObject
|
4
|
+
class InvalidToken < Exception; end
|
5
|
+
class ExpiredToken < InvalidToken; end
|
6
|
+
class InvalidIssuer < InvalidToken; end
|
7
|
+
class InvalidNonce < InvalidToken; end
|
8
|
+
class InvalidAudience < InvalidToken; end
|
9
|
+
|
10
|
+
attr_required :iss, :sub, :aud, :exp, :iat
|
11
|
+
attr_optional :acr, :amr, :azp, :jti, :sid, :auth_time, :nonce, :sub_jwk, :at_hash, :c_hash, :s_hash
|
12
|
+
attr_accessor :access_token, :code, :state
|
13
|
+
alias_method :subject, :sub
|
14
|
+
alias_method :subject=, :sub=
|
15
|
+
|
16
|
+
def initialize(attributes = {})
|
17
|
+
super
|
18
|
+
(all_attributes - [:aud, :exp, :iat, :auth_time, :sub_jwk]).each do |key|
|
19
|
+
self.send "#{key}=", self.send(key).try(:to_s)
|
20
|
+
end
|
21
|
+
self.auth_time = auth_time.to_i unless auth_time.nil?
|
22
|
+
end
|
23
|
+
|
24
|
+
def verify!(expected = {})
|
25
|
+
raise ExpiredToken.new('Invalid ID token: Expired token') unless exp.to_i > Time.now.to_i
|
26
|
+
raise InvalidIssuer.new('Invalid ID token: Issuer does not match') unless iss == expected[:issuer]
|
27
|
+
raise InvalidNonce.new('Invalid ID Token: Nonce does not match') unless nonce == expected[:nonce]
|
28
|
+
|
29
|
+
# aud(ience) can be a string or an array of strings
|
30
|
+
unless Array(aud).include?(expected[:audience] || expected[:client_id])
|
31
|
+
raise InvalidAudience.new('Invalid ID token: Audience does not match')
|
32
|
+
end
|
33
|
+
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
include JWTnizable
|
38
|
+
def to_jwt(key, algorithm = :RS256, &block)
|
39
|
+
hash_length = algorithm.to_s[2, 3].to_i
|
40
|
+
if access_token
|
41
|
+
token = case access_token
|
42
|
+
when Rack::OAuth2::AccessToken
|
43
|
+
access_token.access_token
|
44
|
+
else
|
45
|
+
access_token
|
46
|
+
end
|
47
|
+
self.at_hash = left_half_hash_of token, hash_length
|
48
|
+
end
|
49
|
+
if code
|
50
|
+
self.c_hash = left_half_hash_of code, hash_length
|
51
|
+
end
|
52
|
+
if state
|
53
|
+
self.s_hash = left_half_hash_of state, hash_length
|
54
|
+
end
|
55
|
+
super
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def left_half_hash_of(string, hash_length)
|
61
|
+
digest = OpenSSL::Digest.new("SHA#{hash_length}").digest string
|
62
|
+
Base64.urlsafe_encode64 digest[0, hash_length / (2 * 8)], padding: false
|
63
|
+
end
|
64
|
+
|
65
|
+
class << self
|
66
|
+
def decode(jwt_string, key_or_config)
|
67
|
+
case key_or_config
|
68
|
+
when :self_issued
|
69
|
+
decode_self_issued jwt_string
|
70
|
+
when Oidc::Discovery::Provider::Config::Response
|
71
|
+
jwt = JSON::JWT.decode jwt_string, :skip_verification
|
72
|
+
jwt.verify! key_or_config.jwk(jwt.kid)
|
73
|
+
new jwt
|
74
|
+
else
|
75
|
+
new JSON::JWT.decode jwt_string, key_or_config
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def decode_self_issued(jwt_string)
|
80
|
+
jwt = JSON::JWT.decode jwt_string, :skip_verification
|
81
|
+
jwk = JSON::JWK.new jwt[:sub_jwk]
|
82
|
+
raise InvalidToken.new('Missing sub_jwk') if jwk.blank?
|
83
|
+
raise InvalidToken.new('Invalid subject') unless jwt[:sub] == jwk.thumbprint
|
84
|
+
jwt.verify! jwk
|
85
|
+
new jwt
|
86
|
+
end
|
87
|
+
|
88
|
+
def self_issued(attributes = {})
|
89
|
+
attributes[:sub_jwk] ||= JSON::JWK.new attributes.delete(:public_key)
|
90
|
+
_attributes_ = {
|
91
|
+
iss: 'https://self-issued.me',
|
92
|
+
sub: JSON::JWK.new(attributes[:sub_jwk]).thumbprint
|
93
|
+
}.merge(attributes)
|
94
|
+
new _attributes_
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Oidc
|
2
|
+
class ResponseObject
|
3
|
+
class UserInfo < ConnectObject
|
4
|
+
attr_optional(
|
5
|
+
:sub,
|
6
|
+
:name,
|
7
|
+
:given_name,
|
8
|
+
:family_name,
|
9
|
+
:middle_name,
|
10
|
+
:nickname,
|
11
|
+
:preferred_username,
|
12
|
+
:profile,
|
13
|
+
:picture,
|
14
|
+
:website,
|
15
|
+
:email,
|
16
|
+
:email_verified,
|
17
|
+
:gender,
|
18
|
+
:birthdate,
|
19
|
+
:zoneinfo,
|
20
|
+
:locale,
|
21
|
+
:phone_number,
|
22
|
+
:phone_number_verified,
|
23
|
+
:address,
|
24
|
+
:updated_at
|
25
|
+
)
|
26
|
+
alias_method :subject, :sub
|
27
|
+
alias_method :subject=, :sub=
|
28
|
+
|
29
|
+
validates :email_verified, :phone_number_verified, allow_nil: true, inclusion: {in: [true, false]}
|
30
|
+
validates :zoneinfo, allow_nil: true, inclusion: {in: TZInfo::TimezoneProxy.all.collect(&:name)}
|
31
|
+
validates :profile, :picture, :website, allow_nil: true, url: true
|
32
|
+
validates :email, allow_nil: true, email: true
|
33
|
+
validates :updated_at, allow_nil: true, numericality: {only_integer: true}
|
34
|
+
validate :validate_address
|
35
|
+
validate :require_at_least_one_attributes
|
36
|
+
# TODO: validate locale
|
37
|
+
|
38
|
+
def initialize(attributes = {})
|
39
|
+
super
|
40
|
+
(all_attributes - [:email_verified, :phone_number_verified, :address, :updated_at]).each do |key|
|
41
|
+
self.send "#{key}=", self.send(key).try(:to_s)
|
42
|
+
end
|
43
|
+
self.updated_at = updated_at.try(:to_i)
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate_address
|
47
|
+
errors.add :address, address.errors.full_messages.join(', ') if address.present? && !address.valid?
|
48
|
+
end
|
49
|
+
|
50
|
+
undef :address=
|
51
|
+
def address=(hash_or_address)
|
52
|
+
@address = case hash_or_address
|
53
|
+
when Hash
|
54
|
+
Address.new hash_or_address
|
55
|
+
when Address
|
56
|
+
hash_or_address
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
Dir[File.dirname(__FILE__) + '/user_info/*.rb'].each do |file|
|
64
|
+
require file
|
65
|
+
end
|
data/lib/oidc/version.rb
ADDED
data/lib/oidc.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'logger'
|
3
|
+
require 'faraday'
|
4
|
+
require 'faraday/follow_redirects'
|
5
|
+
require 'swd'
|
6
|
+
require 'webfinger'
|
7
|
+
require 'active_model'
|
8
|
+
require 'tzinfo'
|
9
|
+
require 'validate_url'
|
10
|
+
require 'email_validator/strict'
|
11
|
+
require 'mail'
|
12
|
+
require 'attr_required'
|
13
|
+
require 'attr_optional'
|
14
|
+
require 'json/jwt'
|
15
|
+
require 'rack/oauth2'
|
16
|
+
require 'rack/oauth2/server/authorize/error_with_connect_ext'
|
17
|
+
require 'rack/oauth2/server/authorize/request_with_connect_params'
|
18
|
+
require 'rack/oauth2/server/id_token_response'
|
19
|
+
|
20
|
+
module Oidc
|
21
|
+
def self.logger
|
22
|
+
@@logger
|
23
|
+
end
|
24
|
+
def self.logger=(logger)
|
25
|
+
@@logger = logger
|
26
|
+
end
|
27
|
+
self.logger = Logger.new(STDOUT)
|
28
|
+
self.logger.progname = 'Oidc'
|
29
|
+
|
30
|
+
@sub_protocols = [
|
31
|
+
SWD,
|
32
|
+
WebFinger,
|
33
|
+
Rack::OAuth2
|
34
|
+
]
|
35
|
+
def self.debugging?
|
36
|
+
@@debugging
|
37
|
+
end
|
38
|
+
def self.debugging=(boolean)
|
39
|
+
@sub_protocols.each do |klass|
|
40
|
+
klass.debugging = boolean
|
41
|
+
end
|
42
|
+
@@debugging = boolean
|
43
|
+
end
|
44
|
+
def self.debug!
|
45
|
+
@sub_protocols.each do |klass|
|
46
|
+
klass.debug!
|
47
|
+
end
|
48
|
+
self.debugging = true
|
49
|
+
end
|
50
|
+
def self.debug(&block)
|
51
|
+
sub_protocol_originals = @sub_protocols.inject({}) do |sub_protocol_originals, klass|
|
52
|
+
sub_protocol_originals.merge!(klass => klass.debugging?)
|
53
|
+
end
|
54
|
+
original = self.debugging?
|
55
|
+
debug!
|
56
|
+
yield
|
57
|
+
ensure
|
58
|
+
@sub_protocols.each do |klass|
|
59
|
+
klass.debugging = sub_protocol_originals[klass]
|
60
|
+
end
|
61
|
+
self.debugging = original
|
62
|
+
end
|
63
|
+
self.debugging = false
|
64
|
+
|
65
|
+
def self.http_client
|
66
|
+
Faraday.new(headers: {user_agent: "Oidc (#{VERSION})"}) do |faraday|
|
67
|
+
faraday.request :url_encoded
|
68
|
+
faraday.request :json
|
69
|
+
faraday.response :json
|
70
|
+
faraday.adapter Faraday.default_adapter
|
71
|
+
http_config&.call(faraday)
|
72
|
+
faraday.response :logger, Oidc.logger, {bodies: true} if debugging?
|
73
|
+
end
|
74
|
+
end
|
75
|
+
def self.http_config(&block)
|
76
|
+
@sub_protocols.each do |klass|
|
77
|
+
klass.http_config(&block) unless klass.http_config
|
78
|
+
end
|
79
|
+
@@http_config ||= block
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.validate_discovery_issuer=(boolean)
|
83
|
+
@@validate_discovery_issuer = boolean
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.validate_discovery_issuer
|
87
|
+
@@validate_discovery_issuer
|
88
|
+
end
|
89
|
+
|
90
|
+
self.validate_discovery_issuer = true
|
91
|
+
end
|
92
|
+
|
93
|
+
require 'oidc/exception'
|
94
|
+
require 'oidc/client'
|
95
|
+
require 'oidc/access_token'
|
96
|
+
require 'oidc/jwtnizable'
|
97
|
+
require 'oidc/connect_object'
|
98
|
+
require 'oidc/discovery'
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Rack
|
2
|
+
module OAuth2
|
3
|
+
module Server
|
4
|
+
class Authorize
|
5
|
+
module ErrorWithConnectExt
|
6
|
+
DEFAULT_DESCRIPTION = {
|
7
|
+
invalid_redirect_uri: 'The redirect_uri in the request does not match any of pre-registered redirect_uris.',
|
8
|
+
interaction_required: 'End-User interaction required.',
|
9
|
+
login_required: 'End-User authentication required.',
|
10
|
+
session_selection_required: 'The End-User is required to select a session at the Authorization Server.',
|
11
|
+
consent_required: 'End-User consent required.',
|
12
|
+
invalid_request_uri: 'The request_uri in the request returns an error or invalid data.',
|
13
|
+
invalid_openid_request_object: 'The request parameter contains an invalid OpenID Request Object.'
|
14
|
+
}
|
15
|
+
|
16
|
+
def self.included(klass)
|
17
|
+
DEFAULT_DESCRIPTION.each do |error, default_description|
|
18
|
+
# NOTE:
|
19
|
+
# Connect Message spec doesn't say anything about HTTP status code for each error code.
|
20
|
+
# It probably means "use 400".
|
21
|
+
error_method = :bad_request!
|
22
|
+
klass.class_eval <<-ERROR
|
23
|
+
def #{error}!(description = "#{default_description}", options = {})
|
24
|
+
#{error_method} :#{error}, description, options
|
25
|
+
end
|
26
|
+
ERROR
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
Request.send :include, ErrorWithConnectExt
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|