better_auth-sso 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +10 -0
- data/lib/better_auth/plugins/sso.rb +1004 -104
- data/lib/better_auth/sso/saml.rb +52 -13
- data/lib/better_auth/sso/version.rb +1 -1
- metadata +1 -1
data/lib/better_auth/sso/saml.rb
CHANGED
|
@@ -44,18 +44,24 @@ module BetterAuth
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
attributes = response.attributes
|
|
47
|
-
|
|
47
|
+
mapping = BetterAuth::Plugins.normalize_hash(config[:mapping] || {})
|
|
48
|
+
email = mapped_attribute(attributes, mapping[:email]) || first_attribute(attributes, attribute_map.fetch(:email)) || response.nameid
|
|
48
49
|
raise BetterAuth::APIError.new("BAD_REQUEST", message: "Invalid SAML response") if email.to_s.empty?
|
|
49
50
|
|
|
50
|
-
given_name = first_attribute(attributes, attribute_map.fetch(:given_name))
|
|
51
|
-
family_name = first_attribute(attributes, attribute_map.fetch(:family_name))
|
|
52
|
-
name =
|
|
53
|
-
|
|
51
|
+
given_name = mapped_attribute(attributes, mapping[:first_name]) || first_attribute(attributes, attribute_map.fetch(:given_name))
|
|
52
|
+
family_name = mapped_attribute(attributes, mapping[:last_name]) || first_attribute(attributes, attribute_map.fetch(:family_name))
|
|
53
|
+
name = [given_name, family_name].compact.join(" ").strip
|
|
54
|
+
name = mapped_attribute(attributes, mapping[:name]) || first_attribute(attributes, attribute_map.fetch(:name)) if name.empty?
|
|
55
|
+
extra_fields = mapped_extra_fields(attributes, mapping)
|
|
56
|
+
email_verified = mapping[:email_verified] ? mapped_attribute(attributes, mapping[:email_verified]) : true
|
|
57
|
+
extra_fields.merge(
|
|
54
58
|
email: email.to_s.downcase,
|
|
55
59
|
name: name.to_s.empty? ? email.to_s : name.to_s,
|
|
56
|
-
id: assertion_identifier(response, email),
|
|
57
|
-
|
|
58
|
-
|
|
60
|
+
id: mapped_attribute(attributes, mapping[:id]) || assertion_identifier(response, email),
|
|
61
|
+
name_id: response.nameid,
|
|
62
|
+
session_index: response.sessionindex,
|
|
63
|
+
email_verified: (email_verified == false) ? false : !email_verified.to_s.empty?
|
|
64
|
+
)
|
|
59
65
|
end
|
|
60
66
|
end
|
|
61
67
|
|
|
@@ -69,14 +75,26 @@ module BetterAuth
|
|
|
69
75
|
settings.idp_sso_service_url = config[:entry_point]
|
|
70
76
|
settings.idp_cert = config[:cert] unless config[:cert].to_s.empty?
|
|
71
77
|
settings.name_identifier_format = config[:identifier_format] unless config[:identifier_format].to_s.empty?
|
|
72
|
-
|
|
73
|
-
|
|
78
|
+
private_key = config.dig(:sp_metadata, :private_key) || config[:private_key] || config[:sp_private_key]
|
|
79
|
+
authn_requests_signed = config.fetch(:authn_requests_signed, config[:want_authn_requests_signed])
|
|
80
|
+
if authn_requests_signed && private_key.to_s.empty?
|
|
81
|
+
raise BetterAuth::APIError.new("BAD_REQUEST", message: "SAML authnRequestsSigned requires privateKey")
|
|
82
|
+
end
|
|
83
|
+
settings.private_key = private_key unless private_key.to_s.empty?
|
|
84
|
+
certificate = config.dig(:sp_metadata, :certificate) || config[:sp_certificate]
|
|
85
|
+
certificate ||= config[:certificate] if config[:certificate].is_a?(String)
|
|
86
|
+
settings.certificate = certificate unless certificate.to_s.empty?
|
|
74
87
|
settings.security[:want_assertions_signed] = config.fetch(:want_assertions_signed, true)
|
|
75
88
|
settings.security[:want_messages_signed] = config.fetch(:want_messages_signed, false)
|
|
76
89
|
settings.security[:want_assertions_encrypted] = config.fetch(:want_assertions_encrypted, false)
|
|
90
|
+
settings.security[:authn_requests_signed] = !!authn_requests_signed
|
|
77
91
|
settings.security[:strict_audience_validation] = true
|
|
78
92
|
settings.security[:digest_method] = config[:digest_algorithm] || XMLSecurity::Document::SHA256
|
|
79
|
-
settings.security[:signature_method] = config[:signature_algorithm]
|
|
93
|
+
settings.security[:signature_method] = if config[:signature_algorithm]
|
|
94
|
+
BetterAuth::Plugins.sso_normalize_saml_signature_algorithm(config[:signature_algorithm])
|
|
95
|
+
else
|
|
96
|
+
XMLSecurity::Document::RSA_SHA256
|
|
97
|
+
end
|
|
80
98
|
settings
|
|
81
99
|
end
|
|
82
100
|
|
|
@@ -92,20 +110,41 @@ module BetterAuth
|
|
|
92
110
|
allowed_data_encryption_algorithms: config[:allowed_data_encryption_algorithms]
|
|
93
111
|
)
|
|
94
112
|
rescue BetterAuth::APIError
|
|
95
|
-
raise
|
|
113
|
+
raise BetterAuth::APIError.new("BAD_REQUEST", message: "Invalid SAML response")
|
|
96
114
|
rescue
|
|
97
115
|
raise BetterAuth::APIError.new("BAD_REQUEST", message: "Invalid SAML response")
|
|
98
116
|
end
|
|
99
117
|
|
|
100
118
|
def first_attribute(attributes, names)
|
|
101
119
|
Array(names).each do |name|
|
|
102
|
-
value = attributes
|
|
120
|
+
value = attribute_value(attributes, name)
|
|
103
121
|
value = value.first if value.is_a?(Array)
|
|
104
122
|
return value unless value.to_s.empty?
|
|
105
123
|
end
|
|
106
124
|
nil
|
|
107
125
|
end
|
|
108
126
|
|
|
127
|
+
def mapped_attribute(attributes, name)
|
|
128
|
+
return nil if name.to_s.empty?
|
|
129
|
+
|
|
130
|
+
value = attribute_value(attributes, name)
|
|
131
|
+
value = value.first if value.is_a?(Array)
|
|
132
|
+
value unless value.to_s.empty?
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def mapped_extra_fields(attributes, mapping)
|
|
136
|
+
BetterAuth::Plugins.normalize_hash(mapping[:extra_fields] || {}).each_with_object({}) do |(target, source), result|
|
|
137
|
+
result[target] = mapped_attribute(attributes, source)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def attribute_value(attributes, name)
|
|
142
|
+
[name, name.to_s, BetterAuth::Plugins.normalize_key(name)].each do |key|
|
|
143
|
+
return attributes[key] if attributes.respond_to?(:key?) && attributes.key?(key)
|
|
144
|
+
end
|
|
145
|
+
attributes[name] || attributes[name.to_s] || attributes[BetterAuth::Plugins.normalize_key(name)]
|
|
146
|
+
end
|
|
147
|
+
|
|
109
148
|
def assertion_identifier(response, email)
|
|
110
149
|
response.assertion_id || response.nameid || response.sessionindex || email
|
|
111
150
|
end
|