better_auth-sso 0.2.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/better_auth/plugins/sso.rb +305 -73
- data/lib/better_auth/sso/client.rb +31 -0
- data/lib/better_auth/sso/constants.rb +20 -0
- data/lib/better_auth/sso/domain_verification.rb +17 -0
- data/lib/better_auth/sso/linking/org_assignment.rb +118 -0
- data/lib/better_auth/sso/linking/types.rb +52 -0
- data/lib/better_auth/sso/linking.rb +24 -0
- data/lib/better_auth/sso/oidc/discovery.rb +259 -0
- data/lib/better_auth/sso/oidc/errors.rb +27 -0
- data/lib/better_auth/sso/oidc/types.rb +29 -0
- data/lib/better_auth/sso/oidc.rb +20 -0
- data/lib/better_auth/sso/routes/domain_verification.rb +19 -0
- data/lib/better_auth/sso/routes/helpers.rb +19 -0
- data/lib/better_auth/sso/routes/providers.rb +19 -0
- data/lib/better_auth/sso/routes/saml_pipeline.rb +19 -0
- data/lib/better_auth/sso/routes/schemas.rb +77 -0
- data/lib/better_auth/sso/routes/sso.rb +43 -0
- data/lib/better_auth/sso/saml/algorithms.rb +96 -0
- data/lib/better_auth/sso/saml/assertions.rb +21 -0
- data/lib/better_auth/sso/saml/error_codes.rb +24 -0
- data/lib/better_auth/sso/saml/parser.rb +19 -0
- data/lib/better_auth/sso/saml/timestamp.rb +19 -0
- data/lib/better_auth/sso/saml.rb +24 -4
- data/lib/better_auth/sso/saml_state.rb +30 -0
- data/lib/better_auth/sso/types.rb +20 -0
- data/lib/better_auth/sso/utils.rb +55 -0
- data/lib/better_auth/sso/version.rb +2 -1
- data/lib/better_auth/sso.rb +24 -0
- metadata +45 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuth
|
|
4
|
+
module SSO
|
|
5
|
+
module Routes
|
|
6
|
+
module Schemas
|
|
7
|
+
OIDC_MAPPING_KEYS = %i[id email email_verified name image extra_fields].freeze
|
|
8
|
+
SAML_MAPPING_KEYS = %i[id email email_verified name first_name last_name extra_fields].freeze
|
|
9
|
+
OIDC_CONFIG_KEYS = %i[
|
|
10
|
+
client_id
|
|
11
|
+
client_secret
|
|
12
|
+
authorization_endpoint
|
|
13
|
+
token_endpoint
|
|
14
|
+
user_info_endpoint
|
|
15
|
+
token_endpoint_authentication
|
|
16
|
+
jwks_endpoint
|
|
17
|
+
discovery_endpoint
|
|
18
|
+
scopes
|
|
19
|
+
pkce
|
|
20
|
+
override_user_info
|
|
21
|
+
mapping
|
|
22
|
+
].freeze
|
|
23
|
+
SAML_CONFIG_KEYS = %i[
|
|
24
|
+
entry_point
|
|
25
|
+
cert
|
|
26
|
+
callback_url
|
|
27
|
+
audience
|
|
28
|
+
idp_metadata
|
|
29
|
+
sp_metadata
|
|
30
|
+
want_assertions_signed
|
|
31
|
+
authn_requests_signed
|
|
32
|
+
want_logout_request_signed
|
|
33
|
+
want_logout_response_signed
|
|
34
|
+
signature_algorithm
|
|
35
|
+
digest_algorithm
|
|
36
|
+
identifier_format
|
|
37
|
+
private_key
|
|
38
|
+
decryption_pvk
|
|
39
|
+
additional_params
|
|
40
|
+
mapping
|
|
41
|
+
].freeze
|
|
42
|
+
|
|
43
|
+
module_function
|
|
44
|
+
|
|
45
|
+
def plugin_schema(config = {})
|
|
46
|
+
normalized = BetterAuth::Plugins.normalize_hash(config || {})
|
|
47
|
+
fields = {
|
|
48
|
+
issuer: {type: "string", required: true},
|
|
49
|
+
oidcConfig: {type: "string", required: false},
|
|
50
|
+
samlConfig: {type: "string", required: false},
|
|
51
|
+
userId: {type: "string", required: true},
|
|
52
|
+
providerId: {type: "string", required: true, unique: true},
|
|
53
|
+
domain: {type: "string", required: true},
|
|
54
|
+
organizationId: {type: "string", required: false}
|
|
55
|
+
}
|
|
56
|
+
if normalized.dig(:domain_verification, :enabled)
|
|
57
|
+
fields[:domainVerified] = {type: "boolean", required: false, default_value: false}
|
|
58
|
+
end
|
|
59
|
+
{
|
|
60
|
+
ssoProvider: {
|
|
61
|
+
model_name: normalized[:model_name] || "ssoProviders",
|
|
62
|
+
fields: fields
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def oidc_config_key?(key)
|
|
68
|
+
OIDC_CONFIG_KEYS.include?(BetterAuth::Plugins.normalize_key(key))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def saml_config_key?(key)
|
|
72
|
+
SAML_CONFIG_KEYS.include?(BetterAuth::Plugins.normalize_key(key))
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuth
|
|
4
|
+
module SSO
|
|
5
|
+
module Routes
|
|
6
|
+
module SSO
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def endpoints(config = {})
|
|
10
|
+
normalized = BetterAuth::Plugins.normalize_hash(config || {})
|
|
11
|
+
endpoints = {
|
|
12
|
+
sp_metadata: BetterAuth::Plugins.sso_sp_metadata_endpoint(normalized),
|
|
13
|
+
register_sso_provider: BetterAuth::Plugins.sso_register_provider_endpoint(normalized),
|
|
14
|
+
sign_in_sso: BetterAuth::Plugins.sso_sign_in_endpoint(normalized),
|
|
15
|
+
callback_sso: BetterAuth::Plugins.sso_oidc_callback_endpoint(normalized),
|
|
16
|
+
callback_sso_shared: BetterAuth::Plugins.sso_oidc_shared_callback_endpoint(normalized),
|
|
17
|
+
callback_sso_saml: BetterAuth::Plugins.sso_saml_callback_endpoint(normalized),
|
|
18
|
+
acs_endpoint: BetterAuth::Plugins.sso_saml_acs_endpoint(normalized),
|
|
19
|
+
slo_endpoint: BetterAuth::Plugins.sso_saml_slo_endpoint(normalized),
|
|
20
|
+
initiate_slo: BetterAuth::Plugins.sso_initiate_slo_endpoint(normalized),
|
|
21
|
+
list_sso_providers: BetterAuth::Plugins.sso_list_providers_endpoint,
|
|
22
|
+
get_sso_provider: BetterAuth::Plugins.sso_get_provider_endpoint,
|
|
23
|
+
update_sso_provider: BetterAuth::Plugins.sso_update_provider_endpoint,
|
|
24
|
+
delete_sso_provider: BetterAuth::Plugins.sso_delete_provider_endpoint
|
|
25
|
+
}
|
|
26
|
+
if normalized.dig(:domain_verification, :enabled)
|
|
27
|
+
endpoints[:request_domain_verification] = BetterAuth::Plugins.sso_request_domain_verification_endpoint(normalized)
|
|
28
|
+
endpoints[:verify_domain] = BetterAuth::Plugins.sso_verify_domain_endpoint(normalized)
|
|
29
|
+
end
|
|
30
|
+
endpoints
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def oidc_redirect_uri(context, provider_id)
|
|
34
|
+
BetterAuth::Plugins.sso_oidc_redirect_uri(context, provider_id)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def saml_authorization_url(provider, relay_state, ctx = nil, config = {})
|
|
38
|
+
BetterAuth::Plugins.sso_saml_authorization_url(provider, relay_state, ctx, config)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuth
|
|
4
|
+
module SSO
|
|
5
|
+
module SAML
|
|
6
|
+
module Algorithms
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
SignatureAlgorithm = BetterAuth::Plugins::SSO_SAML_SIGNATURE_ALGORITHMS.merge(
|
|
10
|
+
RSA_SHA1: "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
|
|
11
|
+
RSA_SHA256: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
|
|
12
|
+
RSA_SHA384: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384",
|
|
13
|
+
RSA_SHA512: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512",
|
|
14
|
+
ECDSA_SHA256: "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256",
|
|
15
|
+
ECDSA_SHA384: "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384",
|
|
16
|
+
ECDSA_SHA512: "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512"
|
|
17
|
+
).freeze
|
|
18
|
+
DigestAlgorithm = BetterAuth::Plugins::SSO_SAML_DIGEST_ALGORITHMS.merge(
|
|
19
|
+
SHA1: "http://www.w3.org/2000/09/xmldsig#sha1",
|
|
20
|
+
SHA256: "http://www.w3.org/2001/04/xmlenc#sha256",
|
|
21
|
+
SHA384: "http://www.w3.org/2001/04/xmldsig-more#sha384",
|
|
22
|
+
SHA512: "http://www.w3.org/2001/04/xmlenc#sha512"
|
|
23
|
+
).freeze
|
|
24
|
+
KEY_ENCRYPTION_ALGORITHM = {
|
|
25
|
+
RSA_1_5: "http://www.w3.org/2001/04/xmlenc#rsa-1_5",
|
|
26
|
+
RSA_OAEP: "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p",
|
|
27
|
+
RSA_OAEP_SHA256: "http://www.w3.org/2009/xmlenc11#rsa-oaep"
|
|
28
|
+
}.freeze
|
|
29
|
+
DATA_ENCRYPTION_ALGORITHM = {
|
|
30
|
+
TRIPLEDES_CBC: "http://www.w3.org/2001/04/xmlenc#tripledes-cbc",
|
|
31
|
+
AES_128_CBC: "http://www.w3.org/2001/04/xmlenc#aes128-cbc",
|
|
32
|
+
AES_192_CBC: "http://www.w3.org/2001/04/xmlenc#aes192-cbc",
|
|
33
|
+
AES_256_CBC: "http://www.w3.org/2001/04/xmlenc#aes256-cbc",
|
|
34
|
+
AES_128_GCM: "http://www.w3.org/2009/xmlenc11#aes128-gcm",
|
|
35
|
+
AES_192_GCM: "http://www.w3.org/2009/xmlenc11#aes192-gcm",
|
|
36
|
+
AES_256_GCM: "http://www.w3.org/2009/xmlenc11#aes256-gcm"
|
|
37
|
+
}.freeze
|
|
38
|
+
SecureSignatureAlgorithms = BetterAuth::Plugins::SSO_SAML_SECURE_SIGNATURE_ALGORITHMS
|
|
39
|
+
SecureDigestAlgorithms = BetterAuth::Plugins::SSO_SAML_SECURE_DIGEST_ALGORITHMS
|
|
40
|
+
|
|
41
|
+
def validate(xml, **options)
|
|
42
|
+
if xml.is_a?(Hash)
|
|
43
|
+
return validate_response(
|
|
44
|
+
sig_alg: xml[:sig_alg] || xml[:sigAlg] || xml["sig_alg"] || xml["sigAlg"],
|
|
45
|
+
saml_content: xml[:saml_content] || xml[:samlContent] || xml["saml_content"] || xml["samlContent"],
|
|
46
|
+
**options
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
BetterAuth::Plugins.sso_validate_saml_algorithms!(xml, normalize_options(options))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def validate_response(sig_alg: nil, saml_content: "", **options)
|
|
54
|
+
xml = +""
|
|
55
|
+
xml << "<ds:SignatureMethod Algorithm=\"#{sig_alg}\"/>" unless sig_alg.to_s.empty?
|
|
56
|
+
xml << saml_content.to_s
|
|
57
|
+
BetterAuth::Plugins.sso_validate_saml_algorithms!(xml, normalize_options(options))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def validate_config(config = {}, **options)
|
|
61
|
+
config_keys = %i[signature_algorithm signatureAlgorithm digest_algorithm digestAlgorithm]
|
|
62
|
+
inline_config = options.slice(*config_keys)
|
|
63
|
+
normalized = BetterAuth::Plugins.normalize_hash((config || {}).merge(inline_config))
|
|
64
|
+
options = options.except(*config_keys)
|
|
65
|
+
xml = +""
|
|
66
|
+
unless normalized[:signature_algorithm].to_s.empty?
|
|
67
|
+
xml << "<ds:SignatureMethod Algorithm=\"#{normalized[:signature_algorithm]}\"/>"
|
|
68
|
+
end
|
|
69
|
+
unless normalized[:digest_algorithm].to_s.empty?
|
|
70
|
+
xml << "<ds:DigestMethod Algorithm=\"#{normalized[:digest_algorithm]}\"/>"
|
|
71
|
+
end
|
|
72
|
+
BetterAuth::Plugins.sso_validate_saml_algorithms!(xml, normalize_options(options))
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def normalize_signature(algorithm)
|
|
76
|
+
BetterAuth::Plugins.sso_normalize_saml_signature_algorithm(algorithm)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def normalize_digest(algorithm)
|
|
80
|
+
BetterAuth::Plugins.sso_normalize_saml_digest_algorithm(algorithm)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def normalize_options(options)
|
|
84
|
+
normalized = BetterAuth::Plugins.normalize_hash(options || {})
|
|
85
|
+
{
|
|
86
|
+
on_deprecated: normalized[:on_deprecated],
|
|
87
|
+
allowed_signature_algorithms: normalized[:allowed_signature_algorithms],
|
|
88
|
+
allowed_digest_algorithms: normalized[:allowed_digest_algorithms],
|
|
89
|
+
allowed_key_encryption_algorithms: normalized[:allowed_key_encryption_algorithms],
|
|
90
|
+
allowed_data_encryption_algorithms: normalized[:allowed_data_encryption_algorithms]
|
|
91
|
+
}.compact
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuth
|
|
4
|
+
module SSO
|
|
5
|
+
module SAML
|
|
6
|
+
module Assertions
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def validate_single_assertion!(saml_response)
|
|
10
|
+
BetterAuth::Plugins.sso_validate_single_saml_assertion!(saml_response)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def count(xml)
|
|
14
|
+
assertions = xml.to_s.scan(/<(?:\w+:)?Assertion(?:\s|>|\/)/).length
|
|
15
|
+
encrypted_assertions = xml.to_s.scan(/<(?:\w+:)?EncryptedAssertion(?:\s|>|\/)/).length
|
|
16
|
+
{assertions: assertions, encrypted_assertions: encrypted_assertions, total: assertions + encrypted_assertions}
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuth
|
|
4
|
+
module SSO
|
|
5
|
+
module SAML
|
|
6
|
+
module ErrorCodes
|
|
7
|
+
SAML_ERROR_CODES = {
|
|
8
|
+
single_logout_not_enabled: "Single Logout is not enabled",
|
|
9
|
+
invalid_logout_response: "Invalid LogoutResponse",
|
|
10
|
+
invalid_logout_request: "Invalid LogoutRequest",
|
|
11
|
+
logout_failed_at_idp: "Logout failed at IdP",
|
|
12
|
+
idp_slo_not_supported: "IdP does not support Single Logout Service",
|
|
13
|
+
saml_provider_not_found: "SAML provider not found"
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
def message(code)
|
|
19
|
+
SAML_ERROR_CODES[BetterAuth::Plugins.normalize_key(code)]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuth
|
|
4
|
+
module SSO
|
|
5
|
+
module SAML
|
|
6
|
+
module Parser
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def parse_response(value, config = {}, provider = nil, ctx = nil)
|
|
10
|
+
BetterAuth::Plugins.sso_parse_saml_response(value, config, provider, ctx)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def base64_xml?(value)
|
|
14
|
+
BetterAuth::Plugins.sso_base64_xml?(value)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuth
|
|
4
|
+
module SSO
|
|
5
|
+
module SAML
|
|
6
|
+
module Timestamp
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def validate!(conditions, config = {}, now: Time.now.utc)
|
|
10
|
+
BetterAuth::Plugins.sso_validate_saml_timestamp!(conditions, config, now: now)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def conditions(assertion)
|
|
14
|
+
BetterAuth::Plugins.sso_saml_timestamp_conditions(assertion)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/lib/better_auth/sso/saml.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "base64"
|
|
4
|
+
require "logger"
|
|
4
5
|
require "onelogin/ruby-saml"
|
|
5
6
|
require "uri"
|
|
6
7
|
|
|
@@ -25,6 +26,18 @@ module BetterAuth
|
|
|
25
26
|
}
|
|
26
27
|
end
|
|
27
28
|
|
|
29
|
+
def validate_config_algorithms(config = {}, **options)
|
|
30
|
+
Algorithms.validate_config(config, **options)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def validate_saml_algorithms(xml, **options)
|
|
34
|
+
Algorithms.validate(xml, **options)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def validate_single_assertion(saml_response)
|
|
38
|
+
Assertions.validate_single_assertion!(saml_response)
|
|
39
|
+
end
|
|
40
|
+
|
|
28
41
|
def auth_request_url(settings: nil, request_options: {}, **_options)
|
|
29
42
|
lambda do |provider:, relay_state:, context:|
|
|
30
43
|
config = BetterAuth::Plugins.normalize_hash(provider["samlConfig"] || provider[:samlConfig] || {})
|
|
@@ -69,11 +82,18 @@ module BetterAuth
|
|
|
69
82
|
settings = overrides || OneLogin::RubySaml::Settings.new
|
|
70
83
|
provider_id = provider.fetch("providerId")
|
|
71
84
|
base_url = context.context.base_url
|
|
72
|
-
|
|
85
|
+
idp_metadata = BetterAuth::Plugins.respond_to?(:sso_saml_idp_metadata) ? BetterAuth::Plugins.sso_saml_idp_metadata(config) : {}
|
|
86
|
+
sso_service = BetterAuth::Plugins.respond_to?(:sso_saml_preferred_service) ? BetterAuth::Plugins.sso_saml_preferred_service(idp_metadata[:single_sign_on_service]) : nil
|
|
87
|
+
sso_service = BetterAuth::Plugins.normalize_hash(sso_service || {})
|
|
88
|
+
settings.assertion_consumer_service_url = if BetterAuth::Plugins.respond_to?(:sso_saml_acs_url?) && BetterAuth::Plugins.sso_saml_acs_url?(config[:callback_url])
|
|
89
|
+
config[:callback_url]
|
|
90
|
+
else
|
|
91
|
+
"#{base_url}/sso/saml2/sp/acs/#{URI.encode_www_form_component(provider_id)}"
|
|
92
|
+
end
|
|
73
93
|
settings.sp_entity_id = config.dig(:sp_metadata, :entity_id) || config[:audience] || "#{base_url}/sso/saml2/sp/metadata?providerId=#{URI.encode_www_form_component(provider_id)}"
|
|
74
|
-
settings.idp_entity_id = provider["issuer"] || provider[:issuer]
|
|
75
|
-
settings.idp_sso_service_url = config[:entry_point]
|
|
76
|
-
settings.idp_cert = config[:cert] unless config[:cert].to_s.empty?
|
|
94
|
+
settings.idp_entity_id = idp_metadata[:entity_id] || provider["issuer"] || provider[:issuer]
|
|
95
|
+
settings.idp_sso_service_url = config[:entry_point] || sso_service[:location]
|
|
96
|
+
settings.idp_cert = config[:cert] || idp_metadata[:cert] unless (config[:cert] || idp_metadata[:cert]).to_s.empty?
|
|
77
97
|
settings.name_identifier_format = config[:identifier_format] unless config[:identifier_format].to_s.empty?
|
|
78
98
|
private_key = config.dig(:sp_metadata, :private_key) || config[:private_key] || config[:sp_private_key]
|
|
79
99
|
authn_requests_signed = config.fetch(:authn_requests_signed, config[:want_authn_requests_signed])
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuth
|
|
4
|
+
module SSO
|
|
5
|
+
module SAMLState
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def generate_relay_state(ctx, link = nil, additional_data = {})
|
|
9
|
+
callback_url = BetterAuth::Plugins.sso_fetch(ctx.body, :callback_url)
|
|
10
|
+
raise APIError.new("BAD_REQUEST", message: "callbackURL is required") if callback_url.to_s.empty?
|
|
11
|
+
|
|
12
|
+
extra = (additional_data == false) ? {} : (additional_data || {})
|
|
13
|
+
BetterAuth::Plugins.sso_generate_saml_relay_state(
|
|
14
|
+
ctx,
|
|
15
|
+
extra.merge(
|
|
16
|
+
callbackURL: callback_url,
|
|
17
|
+
errorURL: BetterAuth::Plugins.sso_fetch(ctx.body, :error_callback_url),
|
|
18
|
+
newUserURL: BetterAuth::Plugins.sso_fetch(ctx.body, :new_user_callback_url),
|
|
19
|
+
requestSignUp: BetterAuth::Plugins.sso_fetch(ctx.body, :request_sign_up),
|
|
20
|
+
link: link
|
|
21
|
+
)
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def parse_relay_state(ctx)
|
|
26
|
+
BetterAuth::Plugins.sso_parse_saml_relay_state(ctx, BetterAuth::Plugins.sso_fetch(ctx.body, :relay_state))
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuth
|
|
4
|
+
module SSO
|
|
5
|
+
module Types
|
|
6
|
+
PROVIDER_TYPES = %w[oidc saml].freeze
|
|
7
|
+
OIDC_TOKEN_ENDPOINT_AUTH_METHODS = %w[client_secret_post client_secret_basic].freeze
|
|
8
|
+
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def provider_type?(value)
|
|
12
|
+
PROVIDER_TYPES.include?(value.to_s)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def oidc_token_endpoint_auth_method?(value)
|
|
16
|
+
OIDC_TOKEN_ENDPOINT_AUTH_METHODS.include?(value.to_s)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "openssl"
|
|
5
|
+
|
|
6
|
+
module BetterAuth
|
|
7
|
+
module SSO
|
|
8
|
+
module Utils
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def safe_json_parse(value)
|
|
12
|
+
return nil if value.nil? || value == ""
|
|
13
|
+
return value if value.is_a?(Hash) || value.is_a?(Array)
|
|
14
|
+
|
|
15
|
+
JSON.parse(value.to_s)
|
|
16
|
+
rescue JSON::ParserError => error
|
|
17
|
+
raise Error, "Failed to parse JSON: #{error.message}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def domain_matches?(search_domain, domain_list)
|
|
21
|
+
BetterAuth::Plugins.sso_email_domain_matches?(search_domain, domain_list)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def validate_email_domain(email, domain)
|
|
25
|
+
BetterAuth::Plugins.sso_email_domain_matches?(email, domain)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def parse_certificate(cert)
|
|
29
|
+
value = cert.to_s
|
|
30
|
+
normalized = if value.include?("-----BEGIN")
|
|
31
|
+
value
|
|
32
|
+
else
|
|
33
|
+
body = value.delete("\n\r\t ")
|
|
34
|
+
"-----BEGIN CERTIFICATE-----\n#{body.scan(/.{1,64}/).join("\n")}\n-----END CERTIFICATE-----"
|
|
35
|
+
end
|
|
36
|
+
certificate = OpenSSL::X509::Certificate.new(normalized)
|
|
37
|
+
fingerprint = OpenSSL::Digest::SHA256.hexdigest(certificate.to_der).upcase.scan(/../).join(":")
|
|
38
|
+
{
|
|
39
|
+
fingerprint_sha256: fingerprint,
|
|
40
|
+
not_before: certificate.not_before,
|
|
41
|
+
not_after: certificate.not_after,
|
|
42
|
+
public_key_algorithm: certificate.public_key.class.name.split("::").last.upcase
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def hostname_from_domain(domain)
|
|
47
|
+
BetterAuth::Plugins.sso_hostname_from_domain(domain)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def mask_client_id(client_id)
|
|
51
|
+
BetterAuth::Plugins.sso_mask_client_id(client_id)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
data/lib/better_auth/sso.rb
CHANGED
|
@@ -2,9 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
require "better_auth"
|
|
4
4
|
require_relative "sso/version"
|
|
5
|
+
require_relative "sso/client"
|
|
5
6
|
require_relative "sso/saml_hooks"
|
|
6
7
|
require_relative "sso/saml"
|
|
7
8
|
require_relative "plugins/sso"
|
|
9
|
+
require_relative "sso/constants"
|
|
10
|
+
require_relative "sso/types"
|
|
11
|
+
require_relative "sso/utils"
|
|
12
|
+
require_relative "sso/domain_verification"
|
|
13
|
+
require_relative "sso/oidc"
|
|
14
|
+
require_relative "sso/oidc/discovery"
|
|
15
|
+
require_relative "sso/oidc/errors"
|
|
16
|
+
require_relative "sso/oidc/types"
|
|
17
|
+
require_relative "sso/linking"
|
|
18
|
+
require_relative "sso/linking/types"
|
|
19
|
+
require_relative "sso/linking/org_assignment"
|
|
20
|
+
require_relative "sso/saml/algorithms"
|
|
21
|
+
require_relative "sso/saml/assertions"
|
|
22
|
+
require_relative "sso/saml/error_codes"
|
|
23
|
+
require_relative "sso/saml/timestamp"
|
|
24
|
+
require_relative "sso/saml/parser"
|
|
25
|
+
require_relative "sso/routes/helpers"
|
|
26
|
+
require_relative "sso/routes/providers"
|
|
27
|
+
require_relative "sso/routes/schemas"
|
|
28
|
+
require_relative "sso/routes/domain_verification"
|
|
29
|
+
require_relative "sso/routes/saml_pipeline"
|
|
30
|
+
require_relative "sso/routes/sso"
|
|
31
|
+
require_relative "sso/saml_state"
|
|
8
32
|
|
|
9
33
|
module BetterAuth
|
|
10
34
|
module SSO
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: better_auth-sso
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2
|
|
4
|
+
version: 0.6.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sebastian Sala
|
|
@@ -43,6 +43,26 @@ dependencies:
|
|
|
43
43
|
- - "<"
|
|
44
44
|
- !ruby/object:Gem::Version
|
|
45
45
|
version: '1.0'
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: logger
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '1.6'
|
|
53
|
+
- - "<"
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: '2.0'
|
|
56
|
+
type: :runtime
|
|
57
|
+
prerelease: false
|
|
58
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '1.6'
|
|
63
|
+
- - "<"
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: '2.0'
|
|
46
66
|
- !ruby/object:Gem::Dependency
|
|
47
67
|
name: ruby-saml
|
|
48
68
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -131,8 +151,32 @@ files:
|
|
|
131
151
|
- README.md
|
|
132
152
|
- lib/better_auth/plugins/sso.rb
|
|
133
153
|
- lib/better_auth/sso.rb
|
|
154
|
+
- lib/better_auth/sso/client.rb
|
|
155
|
+
- lib/better_auth/sso/constants.rb
|
|
156
|
+
- lib/better_auth/sso/domain_verification.rb
|
|
157
|
+
- lib/better_auth/sso/linking.rb
|
|
158
|
+
- lib/better_auth/sso/linking/org_assignment.rb
|
|
159
|
+
- lib/better_auth/sso/linking/types.rb
|
|
160
|
+
- lib/better_auth/sso/oidc.rb
|
|
161
|
+
- lib/better_auth/sso/oidc/discovery.rb
|
|
162
|
+
- lib/better_auth/sso/oidc/errors.rb
|
|
163
|
+
- lib/better_auth/sso/oidc/types.rb
|
|
164
|
+
- lib/better_auth/sso/routes/domain_verification.rb
|
|
165
|
+
- lib/better_auth/sso/routes/helpers.rb
|
|
166
|
+
- lib/better_auth/sso/routes/providers.rb
|
|
167
|
+
- lib/better_auth/sso/routes/saml_pipeline.rb
|
|
168
|
+
- lib/better_auth/sso/routes/schemas.rb
|
|
169
|
+
- lib/better_auth/sso/routes/sso.rb
|
|
134
170
|
- lib/better_auth/sso/saml.rb
|
|
171
|
+
- lib/better_auth/sso/saml/algorithms.rb
|
|
172
|
+
- lib/better_auth/sso/saml/assertions.rb
|
|
173
|
+
- lib/better_auth/sso/saml/error_codes.rb
|
|
174
|
+
- lib/better_auth/sso/saml/parser.rb
|
|
175
|
+
- lib/better_auth/sso/saml/timestamp.rb
|
|
135
176
|
- lib/better_auth/sso/saml_hooks.rb
|
|
177
|
+
- lib/better_auth/sso/saml_state.rb
|
|
178
|
+
- lib/better_auth/sso/types.rb
|
|
179
|
+
- lib/better_auth/sso/utils.rb
|
|
136
180
|
- lib/better_auth/sso/version.rb
|
|
137
181
|
homepage: https://github.com/sebasxsala/better-auth
|
|
138
182
|
licenses:
|