omniauth-azure_active_directory_b2c 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/omniauth-azure_active_directory_b2c.rb +1 -0
- data/lib/omniauth/strategies/azure_active_directory_b2c.rb +163 -0
- data/lib/omniauth/strategies/azure_active_directory_b2c/authentication_request.rb +48 -0
- data/lib/omniauth/strategies/azure_active_directory_b2c/authentication_response.rb +122 -0
- data/lib/omniauth/strategies/azure_active_directory_b2c/client.rb +14 -0
- data/lib/omniauth/strategies/azure_active_directory_b2c/jwt_validator.rb +93 -0
- data/lib/omniauth/strategies/azure_active_directory_b2c/policy.rb +26 -0
- data/lib/omniauth/strategies/azure_active_directory_b2c/policy_options.rb +97 -0
- data/lib/omniauth/strategies/azure_active_directory_b2c/version.rb +9 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0c0f7094e772b9c4aeeac8eba5ee0e5f8664c863
|
4
|
+
data.tar.gz: d48a92be522a55bb07ebbaca2d279a7fb84e825c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8dd3cdf6e26aec04a21bd50d7030ba583cc1d76a31b5e48dc7f726d1aa792bfacc2ef0dff1535b79a3e29aa52b7d7f8f4cafb28c09f543b308601042807bd567
|
7
|
+
data.tar.gz: 996b5025889be29a5dd93f4964f0aed570be8fe26ca2aef3506b4dcd34b53c82aa66e8c18906117242297f479f69c193bf80a453546ed2c4b87fff6d85c5f6ea
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'omniauth/strategies/azure_active_directory_b2c.rb'
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'omniauth'
|
2
|
+
require 'proc_evaluate'
|
3
|
+
|
4
|
+
require_relative 'azure_active_directory_b2c/authentication_request.rb'
|
5
|
+
require_relative 'azure_active_directory_b2c/authentication_response.rb'
|
6
|
+
require_relative 'azure_active_directory_b2c/client.rb'
|
7
|
+
require_relative 'azure_active_directory_b2c/jwt_validator.rb'
|
8
|
+
require_relative 'azure_active_directory_b2c/policy_options.rb'
|
9
|
+
require_relative 'azure_active_directory_b2c/policy.rb'
|
10
|
+
require_relative 'azure_active_directory_b2c/version.rb'
|
11
|
+
|
12
|
+
module OmniAuth
|
13
|
+
module Strategies
|
14
|
+
class AzureActiveDirectoryB2C
|
15
|
+
|
16
|
+
include OmniAuth::Strategy
|
17
|
+
|
18
|
+
using ProcEvaluate # adds the `evaluate` refinement method to Object and Proc instances
|
19
|
+
|
20
|
+
#########################################
|
21
|
+
# Error definitions
|
22
|
+
#########################################
|
23
|
+
|
24
|
+
GenericError = Class.new(StandardError)
|
25
|
+
|
26
|
+
# Errors raised du to missing options or settings
|
27
|
+
MissingOptionError = Class.new(GenericError)
|
28
|
+
|
29
|
+
# Errors raised during the callback stage
|
30
|
+
CallbackError = Class.new(GenericError) do
|
31
|
+
attr_reader :failure_message_key
|
32
|
+
def self.failure_message_key(key)
|
33
|
+
define_method(:failure_message_key) { key }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
InvalidCredentialsError = Class.new(CallbackError) { failure_message_key :invalid_credentials }
|
37
|
+
UnauthorizedError = Class.new(CallbackError) { failure_message_key :unauthorized }
|
38
|
+
MissingCodeError = Class.new(CallbackError) { failure_message_key :missing_code }
|
39
|
+
IdTokenValidationError = Class.new(CallbackError) { failure_message_key :id_token_validation_failed }
|
40
|
+
|
41
|
+
#########################################
|
42
|
+
# Strategy options
|
43
|
+
#########################################
|
44
|
+
|
45
|
+
option :name, 'azure_active_directory_b2c'
|
46
|
+
option :redirect_uri # the url to return to in the callback phase
|
47
|
+
option :policy_options
|
48
|
+
|
49
|
+
#########################################
|
50
|
+
# Strategy - setup
|
51
|
+
#########################################
|
52
|
+
|
53
|
+
def policy_options
|
54
|
+
@policy_options ||= options.policy_options || (raise MissingOptionError, '`policy_options` not defined')
|
55
|
+
end
|
56
|
+
|
57
|
+
def policy
|
58
|
+
@policy = Policy.new(**policy_options.symbolize_keys)
|
59
|
+
end
|
60
|
+
|
61
|
+
def redirect_uri
|
62
|
+
@redirect_uri ||= options.redirect_uri.evaluate(self) || (raise MissingOptionError, '`redirect_uri` not defined')
|
63
|
+
end
|
64
|
+
|
65
|
+
def setup_phase
|
66
|
+
end
|
67
|
+
|
68
|
+
#########################################
|
69
|
+
# Strategy - request
|
70
|
+
#########################################
|
71
|
+
|
72
|
+
def authentication_request
|
73
|
+
@authentication_request ||= AuthenticationRequest.new(policy, redirect_uri: redirect_uri)
|
74
|
+
end
|
75
|
+
|
76
|
+
def authorization_uri
|
77
|
+
authentication_request.authorization_uri
|
78
|
+
end
|
79
|
+
|
80
|
+
def set_session_variables!
|
81
|
+
# set the session details to check against in the callback_phase
|
82
|
+
session['omniauth.nonce'] = authentication_request.nonce
|
83
|
+
session['omniauth.state'] = authentication_request.state
|
84
|
+
end
|
85
|
+
|
86
|
+
def request_phase
|
87
|
+
# this phase needs to redirect to B2C with the correct params and record the state and nonce in the session to check against in the callback_phase
|
88
|
+
set_session_variables!
|
89
|
+
redirect authentication_request.authorization_uri
|
90
|
+
end
|
91
|
+
|
92
|
+
#########################################
|
93
|
+
# Strategy - callback
|
94
|
+
#########################################
|
95
|
+
|
96
|
+
def authentication_response
|
97
|
+
@authentication_response ||= AuthenticationResponse.new(policy, request.params['code'])
|
98
|
+
end
|
99
|
+
|
100
|
+
def callback_phase
|
101
|
+
validate_callback_response!
|
102
|
+
validate_id_token!
|
103
|
+
super # required to complete the callback phase
|
104
|
+
|
105
|
+
rescue UnauthorizedError => e
|
106
|
+
return Rack::Response.new(['401 Unauthorized'], 401).finish
|
107
|
+
rescue CallbackError => e
|
108
|
+
fail!(e.failure_message_key, e)
|
109
|
+
rescue ::Timeout::Error, ::Errno::ETIMEDOUT => e
|
110
|
+
fail!(:timeout, e)
|
111
|
+
rescue ::SocketError => e
|
112
|
+
fail!(:failed_to_connect, e)
|
113
|
+
end
|
114
|
+
|
115
|
+
def validate_callback_response!
|
116
|
+
state, code, error, error_reason, error_description = request.params.values_at('state', 'code', 'error', 'error_reason', 'error_description')
|
117
|
+
|
118
|
+
if error || error_reason || error_description
|
119
|
+
raise InvalidCredentialsError, [error, error_reason, error_description].compact.join('. ')
|
120
|
+
elsif state.to_s.empty? || state != session.delete('omniauth.state')
|
121
|
+
raise UnauthorizedError
|
122
|
+
elsif !code
|
123
|
+
raise MissingCodeError, 'Code was not returned from OpenID Connect Provider'
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def validate_id_token!
|
128
|
+
results = authentication_response.validate_id_token
|
129
|
+
if results.has_errors?
|
130
|
+
raise IdTokenValidationError, results.full_messages.join('. ')
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
#########################################
|
135
|
+
# Auth Hash Schema
|
136
|
+
#########################################
|
137
|
+
|
138
|
+
def user_info
|
139
|
+
authentication_response.user_info
|
140
|
+
end
|
141
|
+
|
142
|
+
def subject_id
|
143
|
+
authentication_response.subject_id
|
144
|
+
end
|
145
|
+
|
146
|
+
def extra_info
|
147
|
+
authentication_response.extra_info
|
148
|
+
end
|
149
|
+
|
150
|
+
def credentials
|
151
|
+
authentication_response.credentials
|
152
|
+
end
|
153
|
+
|
154
|
+
# return the details required by OmniAuth
|
155
|
+
info { user_info }
|
156
|
+
uid { subject_id }
|
157
|
+
extra { extra_info }
|
158
|
+
credentials { credentials }
|
159
|
+
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
OmniAuth.config.add_camelization('azure_active_directory_b2c', 'AzureActiveDirectoryB2C')
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module OmniAuth
|
2
|
+
module Strategies
|
3
|
+
class AzureActiveDirectoryB2C
|
4
|
+
class AuthenticationRequest
|
5
|
+
|
6
|
+
class ResponseType
|
7
|
+
# TODO: provide constants for each option
|
8
|
+
CODE = 'code'
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :policy, :client
|
12
|
+
|
13
|
+
def initialize(policy, redirect_uri:, **override_options)
|
14
|
+
@policy = policy
|
15
|
+
@client = policy.initialize_client({ redirect_uri: redirect_uri, **override_options })
|
16
|
+
end
|
17
|
+
|
18
|
+
def authorization_uri(**override_options)
|
19
|
+
options = default_authorization_uri_options.merge(override_options)
|
20
|
+
options = options.select {|k, v| v }
|
21
|
+
client.authorization_uri(options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def state
|
25
|
+
@state ||= SecureRandom.hex(16)
|
26
|
+
end
|
27
|
+
|
28
|
+
def nonce
|
29
|
+
@nonce ||= SecureRandom.hex(16)
|
30
|
+
end
|
31
|
+
|
32
|
+
def response_type
|
33
|
+
ResponseType::CODE
|
34
|
+
end
|
35
|
+
|
36
|
+
def default_authorization_uri_options
|
37
|
+
{
|
38
|
+
response_type: response_type,
|
39
|
+
scope: policy.scope,
|
40
|
+
state: state,
|
41
|
+
nonce: nonce,
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
end # AuthenticationRequest
|
46
|
+
end # AzureActiveDirectoryB2C
|
47
|
+
end # Strategies
|
48
|
+
end # OmniAuth
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module OmniAuth
|
2
|
+
module Strategies
|
3
|
+
class AzureActiveDirectoryB2C
|
4
|
+
class AuthenticationResponse
|
5
|
+
|
6
|
+
class AuthenticationMethod
|
7
|
+
BASIC = 'basic'
|
8
|
+
BODY = 'body'
|
9
|
+
POST = 'post'
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :policy, :client, :code
|
13
|
+
|
14
|
+
def initialize(policy, code, **override_options)
|
15
|
+
@policy = policy
|
16
|
+
@client = policy.initialize_client({ redirect_uri: nil, **override_options })
|
17
|
+
@client.authorization_code = code
|
18
|
+
@code = code
|
19
|
+
end
|
20
|
+
|
21
|
+
def access_token
|
22
|
+
@access_token ||= get_access_token!
|
23
|
+
end
|
24
|
+
|
25
|
+
def id_token
|
26
|
+
@id_token ||= get_id_token!
|
27
|
+
end
|
28
|
+
|
29
|
+
def refresh_token
|
30
|
+
access_token.refresh_token
|
31
|
+
end
|
32
|
+
|
33
|
+
def expires_in
|
34
|
+
access_token.expires_in
|
35
|
+
end
|
36
|
+
|
37
|
+
def subject_id
|
38
|
+
id_token.sub
|
39
|
+
end
|
40
|
+
|
41
|
+
def user_info
|
42
|
+
{
|
43
|
+
name: id_token.raw_attributes['name'],
|
44
|
+
email: ([*id_token.raw_attributes['emails']].first),
|
45
|
+
nickname: id_token.raw_attributes['preferred_username'],
|
46
|
+
first_name: id_token.raw_attributes['given_name'],
|
47
|
+
last_name: id_token.raw_attributes['family_name'],
|
48
|
+
gender: id_token.raw_attributes['gender'],
|
49
|
+
image: id_token.raw_attributes['picture'],
|
50
|
+
phone: id_token.raw_attributes['phone_number'],
|
51
|
+
urls: { website: id_token.raw_attributes['website'] }
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def extra_info
|
56
|
+
{ raw_info: id_token.raw_attributes }
|
57
|
+
end
|
58
|
+
|
59
|
+
def scope
|
60
|
+
policy.scope
|
61
|
+
end
|
62
|
+
|
63
|
+
def authentication_method
|
64
|
+
AuthenticationMethod::BODY
|
65
|
+
end
|
66
|
+
|
67
|
+
def credentials
|
68
|
+
{
|
69
|
+
id_token: id_token,
|
70
|
+
token: access_token.access_token,
|
71
|
+
refresh_token: refresh_token,
|
72
|
+
expires_in: expires_in,
|
73
|
+
scope: scope,
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def default_access_token_options
|
78
|
+
{
|
79
|
+
scope: scope,
|
80
|
+
client_auth_method: authentication_method,
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
def get_access_token!
|
85
|
+
client.access_token!(default_access_token_options)
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_id_token!
|
89
|
+
# TODO: if the id_token is not passed back, we could get the id token from the userinfo endpoint (or fail if no endpoint is defined?)
|
90
|
+
encrypted_id_token = access_token.id_token
|
91
|
+
decoded_id_token = decode_id_token!(encrypted_id_token)
|
92
|
+
end
|
93
|
+
|
94
|
+
def decode_id_token!(id_token)
|
95
|
+
::OpenIDConnect::ResponseObject::IdToken.decode(id_token, public_key)
|
96
|
+
end
|
97
|
+
|
98
|
+
def public_key
|
99
|
+
if policy.jwk_signing_algorithm == :RS256 && policy.jwk_signing_keys
|
100
|
+
jwk_key
|
101
|
+
else
|
102
|
+
raise 'id_token signing algorithm is currently not supported: %s' % policy.jwk_signing_algorithm
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def jwk_key
|
107
|
+
key = policy.jwk_signing_keys
|
108
|
+
if key.has_key?('keys')
|
109
|
+
JSON::JWK::Set.new(key['keys']) # a set of keys
|
110
|
+
else
|
111
|
+
JSON::JWK.new(key) # a single key
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def validate_id_token(seconds_since_epoc = Time.now.to_i)
|
116
|
+
JwtValidator.validate(id_token.raw_attributes, public_key, policy, seconds_since_epoc)
|
117
|
+
end
|
118
|
+
|
119
|
+
end # AuthenticationResponse
|
120
|
+
end # AzureActiveDirectoryB2C
|
121
|
+
end # Strategies
|
122
|
+
end # OmniAuth
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'openid_connect'
|
2
|
+
|
3
|
+
module OmniAuth
|
4
|
+
module Strategies
|
5
|
+
class AzureActiveDirectoryB2C
|
6
|
+
|
7
|
+
class Client < ::OpenIDConnect::Client
|
8
|
+
# developers can override this class as required
|
9
|
+
# Be sure to also override MicrosoftAzureB2C::Policy#initialize_client
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module OmniAuth
|
2
|
+
module Strategies
|
3
|
+
class AzureActiveDirectoryB2C
|
4
|
+
|
5
|
+
class ValidationResult
|
6
|
+
def error_messages
|
7
|
+
@error_messages ||= []
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_error(key, message)
|
11
|
+
error_messages << { error: key, message: message }
|
12
|
+
end
|
13
|
+
|
14
|
+
def has_errors?
|
15
|
+
error_messages.any?
|
16
|
+
end
|
17
|
+
|
18
|
+
def okay?
|
19
|
+
error_messages.empty?
|
20
|
+
end
|
21
|
+
|
22
|
+
def full_messages
|
23
|
+
error_messages.collect {|err| err[:message] }
|
24
|
+
end
|
25
|
+
end # ValidationResult
|
26
|
+
|
27
|
+
class JwtValidator
|
28
|
+
|
29
|
+
EPOC_TIME_LEEWAY_SECONDS = 10
|
30
|
+
|
31
|
+
attr_reader :jwt, :jwt_key, :policy, :seconds_since_epoc
|
32
|
+
|
33
|
+
def self.validate(jwt, jwt_key, policy, seconds_since_epoc = Time.now.to_i)
|
34
|
+
new(jwt, jwt_key, policy, seconds_since_epoc).validate
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(jwt, jwt_key, policy, seconds_since_epoc = Time.now.to_i)
|
38
|
+
@jwt = jwt
|
39
|
+
@jwt_key = jwt_key
|
40
|
+
@policy = policy
|
41
|
+
@seconds_since_epoc = seconds_since_epoc
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate
|
45
|
+
results = ValidationResult.new
|
46
|
+
results.add_error(:algorithm_mismatch, "Signing algorithm mismatch: Expected `#{policy.jwk_signing_algorithm} but got #{jwt.algorithm}`") unless signing_algoritm_matches?
|
47
|
+
results.add_error(:issue_mismatch, "Issue mismatch: Expected `#{policy.issuer}` but got `#{jwt[:iss]}`") unless issuer_matches?
|
48
|
+
results.add_error(:audience_mismatch, "Audience mismatch: Expected `#{policy.aud}` but got `#{jwt[:aud]}`") unless audience_matches?
|
49
|
+
results.add_error(:before_start_time, "Token has not yet commenced: Valid at #{jwt[:nbf]} but currently #{seconds_since_epoc}") unless on_or_after_not_before_time?
|
50
|
+
results.add_error(:after_expiry_time, "Token has expired: Expired at #{jwt[:exp]} but currently #{seconds_since_epoc}") unless before_expiration_time?
|
51
|
+
|
52
|
+
begin
|
53
|
+
verify_signature!
|
54
|
+
rescue JSON::JWS::VerificationFailed
|
55
|
+
results.add_error(:signiture_verification_failed, 'Signture verification failed') unless signature_verified?
|
56
|
+
rescue JSON::JWS::UnexpectedAlgorithm
|
57
|
+
results.add_error(:unexpected_signiture_algorithm, 'Unexpected signature algorithm') unless signature_verified?
|
58
|
+
rescue => e
|
59
|
+
results.add_error(:signiture_verification_failed, e.message || 'Signature verification failed') unless signature_verified?
|
60
|
+
end
|
61
|
+
|
62
|
+
results # return results
|
63
|
+
end
|
64
|
+
|
65
|
+
def signing_algoritm_matches?
|
66
|
+
# An attacker may change the signing algorith to provide a forged signature
|
67
|
+
jwt.algorithm.to_sym == policy.jwk_signing_algorithm
|
68
|
+
end
|
69
|
+
|
70
|
+
def issuer_matches?
|
71
|
+
jwt[:iss] && jwt[:iss] != '' && jwt[:iss] == policy.issuer
|
72
|
+
end
|
73
|
+
|
74
|
+
def audience_matches?
|
75
|
+
jwt[:aud] && jwt[:aud] != '' && jwt[:aud] == policy.application_identifier
|
76
|
+
end
|
77
|
+
|
78
|
+
def on_or_after_not_before_time?
|
79
|
+
(seconds_since_epoc + EPOC_TIME_LEEWAY_SECONDS) >= jwt[:nbf]
|
80
|
+
end
|
81
|
+
|
82
|
+
def before_expiration_time?
|
83
|
+
(seconds_since_epoc - EPOC_TIME_LEEWAY_SECONDS) < jwt[:exp]
|
84
|
+
end
|
85
|
+
|
86
|
+
def verify_signature!
|
87
|
+
jwt.verify!(jwt_key)
|
88
|
+
end
|
89
|
+
|
90
|
+
end # JwtVerifier
|
91
|
+
end # AzureActiveDirectoryB2C
|
92
|
+
end # Strategies
|
93
|
+
end # OmniAuth
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module OmniAuth
|
2
|
+
module Strategies
|
3
|
+
class AzureActiveDirectoryB2C
|
4
|
+
class Policy
|
5
|
+
include AzureActiveDirectoryB2C::PolicyOptions
|
6
|
+
|
7
|
+
attr_reader :application_identifier, :application_secret, :issuer, :tenant_name, :policy_name, :jwk_signing_algorithm, :jwk_signing_keys
|
8
|
+
|
9
|
+
def initialize(application_identifier:, application_secret:, issuer:, tenant_name:, policy_name:, jwk_signing_algorithm:, jwk_signing_keys:, scope: nil)
|
10
|
+
@application_identifier = application_identifier
|
11
|
+
@application_secret = application_secret
|
12
|
+
@issuer = issuer
|
13
|
+
@tenant_name = tenant_name
|
14
|
+
@policy_name = policy_name
|
15
|
+
@jwk_signing_algorithm = jwk_signing_algorithm
|
16
|
+
@jwk_signing_keys = jwk_signing_keys
|
17
|
+
@scope = *scope
|
18
|
+
end
|
19
|
+
|
20
|
+
def scope
|
21
|
+
@scope.any? ? @scope : super
|
22
|
+
end
|
23
|
+
end # Policy
|
24
|
+
end # AzureActiveDirectoryB2C
|
25
|
+
end # Strategies
|
26
|
+
end # OmniAuth
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module OmniAuth
|
2
|
+
module Strategies
|
3
|
+
class AzureActiveDirectoryB2C
|
4
|
+
module PolicyOptions
|
5
|
+
|
6
|
+
def respond_to_missing?(method_name, *args)
|
7
|
+
self.class.instance_methods.include?("policy_#{method_name}".to_sym) || super
|
8
|
+
end
|
9
|
+
|
10
|
+
def method_missing(method_name, *args, &block)
|
11
|
+
policy_method_name = 'policy_%s' % method_name
|
12
|
+
if respond_to?(policy_method_name)
|
13
|
+
send(policy_method_name, *args, &block)
|
14
|
+
else
|
15
|
+
super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def policy_application_identifier
|
20
|
+
raise MissingOptionError, '`application_identifier` not defined'
|
21
|
+
end
|
22
|
+
|
23
|
+
def policy_application_secret
|
24
|
+
raise MissingOptionError, '`application_secret` not defined'
|
25
|
+
end
|
26
|
+
|
27
|
+
def policy_issuer
|
28
|
+
raise MissingOptionError, '`issuer` not defined'
|
29
|
+
end
|
30
|
+
|
31
|
+
def policy_tenant_name
|
32
|
+
raise MissingOptionError, '`tenant_name` not defined'
|
33
|
+
end
|
34
|
+
|
35
|
+
def policy_policy_name
|
36
|
+
raise MissingOptionError, '`policy_name` not defined'
|
37
|
+
end
|
38
|
+
|
39
|
+
def policy_host_name
|
40
|
+
'https://login.microsoftonline.com/te/%s/%s' % [tenant_name, policy_name]
|
41
|
+
end
|
42
|
+
|
43
|
+
def policy_authorization_endpoint
|
44
|
+
'%s/oauth2/v2.0/authorize' % host_name
|
45
|
+
end
|
46
|
+
|
47
|
+
def policy_token_endpoint
|
48
|
+
'%s/oauth2/v2.0/token' % host_name
|
49
|
+
end
|
50
|
+
|
51
|
+
def policy_jwks_uri
|
52
|
+
'%s/discovery/v2.0/keys' % host_name
|
53
|
+
end
|
54
|
+
|
55
|
+
def policy_jwk_signing_algorithm
|
56
|
+
# this can be "discovered" from the `jwks_uri` property at .well-known/openid-configuration
|
57
|
+
'RS256'.to_sym
|
58
|
+
end
|
59
|
+
|
60
|
+
def policy_id_token_signing_algorithm
|
61
|
+
policy_jwk_signing_algorithm
|
62
|
+
end
|
63
|
+
|
64
|
+
def policy_scope
|
65
|
+
[
|
66
|
+
:openid, # This requests an ID token
|
67
|
+
# :offline_access, # This requests a refresh token using Auth Code flows. See: https://docs.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-reference-oauth-code).
|
68
|
+
# Request API permissions. See https://docs.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-access-tokens
|
69
|
+
]
|
70
|
+
end
|
71
|
+
|
72
|
+
def policy_jwk_signing_keys
|
73
|
+
# public keys are listed at the url specified in the the `jwks_uri` property at .well-known/openid-configuration
|
74
|
+
# eg. https://login.microsoftonline.com/mipwtest.onmicrosoft.com/discovery/v2.0/keys?p=b2c_1_signupin
|
75
|
+
raise MissingOptionError, '`jwk_signing_keys` not defined'
|
76
|
+
end
|
77
|
+
|
78
|
+
def policy_default_client_options
|
79
|
+
{
|
80
|
+
identifier: application_identifier,
|
81
|
+
secret: application_secret,
|
82
|
+
authorization_endpoint: authorization_endpoint,
|
83
|
+
token_endpoint: token_endpoint,
|
84
|
+
jwks_uri: jwks_uri,
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
def policy_initialize_client(redirect_uri:, **override_options)
|
89
|
+
options = default_client_options.merge(override_options)
|
90
|
+
options[:redirect_uri] = redirect_uri
|
91
|
+
Client.new(options)
|
92
|
+
end
|
93
|
+
|
94
|
+
end # PolicyOptions
|
95
|
+
end # AzureActiveDirectoryB2C
|
96
|
+
end # Strategies
|
97
|
+
end # OmniAuth
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: omniauth-azure_active_directory_b2c
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brent Jacobs
|
8
|
+
- NextFaze
|
9
|
+
- Meeco
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2017-10-27 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: omniauth
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - "~>"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - "~>"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '1.3'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: openid_connect
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - "~>"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '1.1'
|
36
|
+
type: :runtime
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - "~>"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '1.1'
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
name: proc_evaluate
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '1.0'
|
50
|
+
type: :runtime
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - "~>"
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '1.0'
|
57
|
+
description:
|
58
|
+
email: developers@meeco.me
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- lib/omniauth-azure_active_directory_b2c.rb
|
64
|
+
- lib/omniauth/strategies/azure_active_directory_b2c.rb
|
65
|
+
- lib/omniauth/strategies/azure_active_directory_b2c/authentication_request.rb
|
66
|
+
- lib/omniauth/strategies/azure_active_directory_b2c/authentication_response.rb
|
67
|
+
- lib/omniauth/strategies/azure_active_directory_b2c/client.rb
|
68
|
+
- lib/omniauth/strategies/azure_active_directory_b2c/jwt_validator.rb
|
69
|
+
- lib/omniauth/strategies/azure_active_directory_b2c/policy.rb
|
70
|
+
- lib/omniauth/strategies/azure_active_directory_b2c/policy_options.rb
|
71
|
+
- lib/omniauth/strategies/azure_active_directory_b2c/version.rb
|
72
|
+
homepage: https://github.com/Meeco/omniauth-azure_active_directory_b2c
|
73
|
+
licenses:
|
74
|
+
- MIT
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 2.6.12
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: Azure AD B2C Strategy for OmniAuth.
|
96
|
+
test_files: []
|