Skip to content

Commit 385a342

Browse files
committed
Improve the README.md particularly around the signing/encryption features.
Also adds a missed line to UPGRADING.md
1 parent f1b6c98 commit 385a342

File tree

2 files changed

+171
-92
lines changed

2 files changed

+171
-92
lines changed

README.md

Lines changed: 156 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ be taken in order to validate such URL inputs and avoid attacks like SSRF.
7777

7878
## Getting Started
7979

80-
In order to use the toolkit you will need to install the gem (either manually or using Bundler),
80+
In order to use Ruby SAML you will need to install the gem (either manually or using Bundler),
8181
and require the library in your Ruby application:
8282

8383
Using `Gemfile`
@@ -96,7 +96,8 @@ Using RubyGems
9696
gem install ruby-saml
9797
```
9898

99-
When requiring the gem, you can add the whole toolkit
99+
You may require the entire Ruby SAML gem:
100+
100101
```ruby
101102
require 'onelogin/ruby-saml'
102103
```
@@ -240,7 +241,7 @@ validations by initializing the response with different options:
240241
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_authnstatement: true}) # skips AuthnStatement
241242
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_conditions: true}) # skips conditions
242243
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_subject_confirmation: true}) # skips subject confirmation
243-
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_recipient_check: true}) # doens't skip subject confirmation, but skips the recipient check which is a sub check of the subject_confirmation check
244+
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_recipient_check: true}) # doesn't skip subject confirmation, but skips the recipient check which is a sub check of the subject_confirmation check
244245
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_audience: true}) # skips audience check
245246
```
246247
@@ -298,16 +299,16 @@ class SamlController < ApplicationController
298299
end
299300
```
300301
302+
## Signature Validation
301303
302-
## Signature validation
303-
304-
On the ruby-saml toolkit there are different ways to validate the signature of the SAMLResponse:
305-
- You can provide the IdP x509 public certificate at the 'idp_cert' setting.
306-
- You can provide the IdP x509 public certificate in fingerprint format using the 'idp_cert_fingerprint' setting parameter and additionally the 'idp_cert_fingerprint_algorithm' parameter.
304+
Ruby SAML allows different ways to validate the signature of the SAMLResponse:
305+
- You can provide the IdP X.509 public certificate at the `idp_cert` setting.
306+
- You can provide the IdP X.509 public certificate in fingerprint format using the
307+
`idp_cert_fingerprint` setting parameter and additionally the `idp_cert_fingerprint_algorithm` parameter.
307308
308309
When validating the signature of redirect binding, the fingerprint is useless and the certificate
309310
of the IdP is required in order to execute the validation. You can pass the option
310-
`:relax_signature_validation` to SloLogoutrequest and Logoutresponse if want to avoid signature
311+
`:relax_signature_validation` to `SloLogoutrequest` and `Logoutresponse` if want to avoid signature
311312
validation if no certificate of the IdP is provided.
312313
313314
In production also we highly recommend to register on the settings the IdP certificate instead
@@ -318,34 +319,35 @@ we maintain it for compatibility and also to be used on test environment.
318319
In some scenarios the IdP uses different certificates for signing/encryption, or is under key
319320
rollover phase and more than one certificate is published on IdP metadata.
320321
321-
In order to handle that the toolkit offers the 'idp_cert_multi' parameter.
322-
When used, 'idp_cert' and 'idp_cert_fingerprint' values are ignored.
322+
In order to handle that Ruby SAML offers the `idp_cert_multi` parameter.
323+
When used, `idp_cert` and `idp_cert_fingerprint` values are ignored.
323324
324325
The `idp_cert_multi` must be a Hash as follows:
325326
327+
```ruby
326328
{
327329
:signing => [],
328330
:encryption => []
329331
}
332+
```
330333
331-
And on `:signing` and `:encryption` arrays, add the different IdP x509 public certificates
334+
And on `:signing` and `:encryption` arrays, add the different IdP X.509 public certificates
332335
published on the IdP metadata.
333336
334337
## Metadata Based Configuration
335338
336-
The method above requires a little extra work to manually specify attributes about the IdP.
337-
(And your SP application) There's an easier method -- use a metadata exchange.
338-
Metadata is just an XML file that defines the capabilities of both the IdP and the SP application.
339-
It also contains the X.509 public key certificates which add to the trusted relationship.
339+
The method above requires a little extra work to manually specify attributes about both the IdP and your SP application.
340+
There's an easier method: use a metadata exchange. Metadata is an XML file that defines the capabilities of both the IdP
341+
and the SP application. It also contains the X.509 public key certificates which add to the trusted relationship.
340342
The IdP administrator can also configure custom settings for an SP based on the metadata.
341343
342-
Using ```idp_metadata_parser.parse_remote``` IdP metadata will be added to the settings without further ado.
344+
Using `IdpMetadataParser#parse_remote`, the IdP metadata will be added to the settings.
343345
344346
```ruby
345347
def saml_settings
346348
347349
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
348-
# Returns OneLogin::RubySaml::Settings prepopulated with idp metadata
350+
# Returns OneLogin::RubySaml::Settings pre-populated with IdP metadata
349351
settings = idp_metadata_parser.parse_remote("https://example.com/auth/saml2/idp/metadata")
350352
351353
settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
@@ -357,6 +359,7 @@ def saml_settings
357359
settings
358360
end
359361
```
362+
360363
The following attributes are set:
361364
* idp_entity_id
362365
* name_identifier_format
@@ -375,11 +378,11 @@ IdpMetadataParser by its Entity Id value:
375378
376379
```ruby
377380
validate_cert = true
378-
settings = idp_metadata_parser.parse_remote(
379-
"https://example.com/auth/saml2/idp/metadata",
380-
validate_cert,
381-
entity_id: "http//example.com/target/entity"
382-
)
381+
settings = idp_metadata_parser.parse_remote(
382+
"https://example.com/auth/saml2/idp/metadata",
383+
validate_cert,
384+
entity_id: "http//example.com/target/entity"
385+
)
383386
```
384387
385388
### Parsing Metadata into an Hash
@@ -394,7 +397,7 @@ If you are using `saml:AttributeStatement` to transfer data like the username, y
394397
`single_value_compatibility` (when activated, only the first value is returned)
395398
396399
```ruby
397-
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
400+
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
398401
response.settings = saml_settings
399402
400403
response.attributes[:username]
@@ -479,7 +482,7 @@ pp(response.attributes.multi(:not_exists))
479482
pp(response.attributes.fetch(/givenname/))
480483
# => "usersName"
481484
482-
# Deactive single_value_compatibility
485+
# Deprecated single_value_compatibility
483486
OneLogin::RubySaml::Attributes.single_value_compatibility = false
484487
485488
pp(response.attributes[:uid])
@@ -522,72 +525,169 @@ To add a `saml:AuthnContextDeclRef`, define `settings.authn_context_decl_ref`.
522525
In a SP-initiated flow, the SP can indicate to the IdP the subject that should be authenticated. This is done by defining the `settings.name_identifier_value_requested` before
523526
building the authrequest object.
524527
528+
## Service Provider Metadata
529+
530+
To form a trusted pair relationship with the IdP, the SP (you) need to provide metadata XML
531+
to the IdP for various good reasons. (Caching, certificate lookups, relaying party permissions, etc)
525532
526-
## Signing
533+
The class `OneLogin::RubySaml::Metadata` takes care of this by reading the Settings and returning XML. All you have to do is add a controller to return the data, then give this URL to the IdP administrator.
534+
535+
The metadata will be polled by the IdP every few minutes, so updating your settings should propagate
536+
to the IdP settings.
527537
528-
The Ruby Toolkit supports 2 different kinds of signature: Embeded and `GET` parameters
538+
```ruby
539+
class SamlController < ApplicationController
540+
# ... the rest of your controller definitions ...
541+
def metadata
542+
settings = Account.get_saml_settings
543+
meta = OneLogin::RubySaml::Metadata.new
544+
render :xml => meta.generate(settings), :content_type => "application/samlmetadata+xml"
545+
end
546+
end
547+
```
529548
530-
In order to be able to sign, define the private key and the public cert of the service provider:
549+
You can add `ValidUntil` and `CacheDuration` to the SP Metadata XML using instead:
531550
532551
```ruby
533-
settings.certificate = "CERTIFICATE TEXT WITH HEAD AND FOOT"
534-
settings.private_key = "PRIVATE KEY TEXT WITH HEAD AND FOOT"
552+
# Valid until => 2 days from now
553+
# Cache duration = 604800s = 1 week
554+
valid_until = Time.now + 172800
555+
cache_duration = 604800
556+
meta.generate(settings, false, valid_until, cache_duration)
535557
```
536558
537-
The settings related to sign are stored in the `security` attribute of the settings:
559+
## Signing and Encryption
560+
561+
Ruby SAML supports supports the signing and encryption functionality:
562+
563+
1. Signing your SP Metadata XML
564+
2. Signing your SP SAML messages
565+
3. Encrypting IdP Assertion messages, and decrypting them upon receipt (EncryptedAssertion)
566+
4. Verifying signatures on IdP Assertion messages
567+
568+
In order to use functions 1-3 above, you must first define your SP public certificate and private key:
538569
539570
```ruby
540-
settings.security[:authn_requests_signed] = true # Enable or not signature on AuthNRequest
541-
settings.security[:logout_requests_signed] = true # Enable or not signature on Logout Request
542-
settings.security[:logout_responses_signed] = true # Enable or not signature on Logout Response
543-
settings.security[:want_assertions_signed] = true # Enable or not the requirement of signed assertion
544-
settings.security[:metadata_signed] = true # Enable or not signature on Metadata
571+
settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
572+
settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
573+
```
574+
575+
Note that the same certificate and private key are used for all SP encryption and signing-related functions.
576+
Ruby SAML does not currently allow to specify different certificates for each function.
545577
578+
You may also globally set the SP signature and digest method, to be used in SP signing (functions 1 and 2 above):
579+
580+
```ruby
546581
settings.security[:digest_method] = XMLSecurity::Document::SHA1
547582
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
583+
```
584+
585+
#### Signing SP Metadata
586+
587+
You may add a `<ds:Signature>` digital signature element to your SP Metadata XML using the following setting:
588+
589+
```ruby
590+
settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
591+
settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
592+
593+
settings.security[:metadata_signed] = true # Enable signature on Metadata
594+
```
595+
596+
#### Signing SP SAML Messages
597+
598+
Ruby SAML supports SAML request signing. The Service Provider will sign the
599+
request/responses with its private key. The Identity Provider will then validate the signature
600+
of the received request/responses with the public X.509 cert of the Service Provider.
601+
602+
To enable, please first set your certificate and private key. This will add `<md:KeyDescriptor use="signing">`
603+
to your SP Metadata XML, to be read by the IdP.
604+
605+
```ruby
606+
settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
607+
settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
608+
```
548609
549-
settings.security[:check_idp_cert_expiration] = false # Enable or not IdP x509 cert expiration check
550-
settings.security[:check_sp_cert_expiration] = false # Enable or not SP x509 cert expiration check
610+
Next, you may specify the specific SP SAML messages you would like to sign:
611+
612+
```ruby
613+
settings.security[:authn_requests_signed] = true # Enable signature on AuthNRequest
614+
settings.security[:logout_requests_signed] = true # Enable signature on Logout Request
615+
settings.security[:logout_responses_signed] = true # Enable signature on Logout Response
551616
```
552617
553-
Signatures will be handled for both `HTTP-Redirect` and `HTTP-Redirect` Bindings.
618+
Signatures will be handled automatically for both `HTTP-Redirect` and `HTTP-Redirect` Binding.
554619
Note that the RelayState parameter is used when creating the Signature on the `HTTP-Redirect` Binding.
555620
Remember to provide it to the Signature builder if you are sending a `GET RelayState` parameter or the
556621
signature validation process will fail at the Identity Provider.
557622
558-
The Service Provider will sign the request/responses with its private key.
559-
The Identity Provider will validate the sign of the received request/responses with the public x509 cert of the
560-
Service Provider.
623+
#### Encrypting SAML Assertions
624+
625+
Ruby SAML supports EncryptedAssertion. The Identity Provider will encrypt the Assertion with the
626+
public cert of the Service Provider. The Service Provider will decrypt the EncryptedAssertion with its private key.
627+
628+
You may enable EncryptedAssertion as follows. This will add `<md:KeyDescriptor use="encrytion">` to your
629+
SP Metadata XML, to be read by the IdP.
630+
631+
```ruby
632+
settings.certificate = "CERTIFICATE TEXT WITH BEGIN/END HEADER AND FOOTER"
633+
settings.private_key = "PRIVATE KEY TEXT WITH BEGIN/END HEADER AND FOOTER"
634+
635+
settings.security[:want_assertions_encrypted] = true # Enable EncryptedAssertion
636+
```
637+
638+
#### Verifying Signature on IdP Assertions
561639
562-
Notice that this toolkit uses 'settings.certificate' and 'settings.private_key' for the sign and decrypt processes.
640+
You may require the IdP to sign its SAML Assertions using the following setting.
641+
With will add `<md:SPSSODescriptor WantAssertionsSigned="true">` to your SP Metadata XML.
642+
The signature will be checked against the `<md:KeyDescriptor use="signing">` element
643+
present in the IdP's metadata.
644+
645+
```ruby
646+
settings.security[:want_assertions_signed] = true # Require the IdP to sign its SAML Assertions
647+
```
563648
564-
Enable/disable the soft mode with the `settings.soft` parameter. When set to `false`, saml validations errors will raise an exception.
649+
#### Certificate and Signature Validation
565650
566-
## Decrypting
651+
You may require SP and IdP certificates to be non-expired using the following settings:
567652
568-
The Ruby Toolkit supports EncryptedAssertion.
653+
```ruby
654+
settings.security[:check_idp_cert_expiration] = true # Raise error if IdP X.509 cert is expired
655+
settings.security[:check_sp_cert_expiration] = true # Raise error SP X.509 cert is expired
656+
```
569657
570-
In order to be able to decrypt a SAML Response that contains a EncryptedAssertion you need define the private key and the public cert of the service provider, then share this with the Identity Provider.
658+
By default, Ruby SAML will raise a `OneLogin::RubySaml::ValidationError` if a signature or certificate
659+
validation fails. You may disable such exceptions using the `settings.security[:soft]` parameter.
571660
572661
```ruby
573-
settings.certificate = "CERTIFICATE TEXT WITH HEAD AND FOOT"
574-
settings.private_key = "PRIVATE KEY TEXT WITH HEAD AND FOOT"
662+
settings.security[:soft] = true # Do not raise error on failed signature/certificate validations
575663
```
576664
577-
The Identity Provider will encrypt the Assertion with the public cert of the Service Provider.
578-
The Service Provider will decrypt the EncryptedAssertion with its private key.
665+
#### Key Rollover
579666
580-
Notice that this toolkit uses 'settings.certificate' and 'settings.private_key' for the sign and decrypt processes.
667+
To update the SP X.509 certificate and private key without disruption of service, you may define the parameter
668+
`settings.certificate_new`. This will publish the new SP certificate in your metadata so that your IdP counterparties
669+
may cache it in preparation for rollover.
581670
671+
For example, if you to rollover from `CERT A` to `CERT B`. Before rollover, your settings should look as follows.
672+
Both `CERT A` and `CERT B` will now appear in your SP metadata, however `CERT A` will still be used for signing
673+
and encryption at this time.
582674
583-
## Key rollover
675+
```ruby
676+
settings.certificate = "CERT A"
677+
settings.private_key = "PRIVATE KEY FOR CERT A"
678+
settings.certificate_new = "CERT B"
679+
```
584680
585-
If you plan to update the SP x509cert and privateKey you can define the parameter 'certificate_new' at the settings and that new SP public certificate will be published on the SP metadata so Identity Providers can read them and get ready for rollover.
681+
After the IdP has cached `CERT B`, you may then change your settings as follows:
586682
683+
```ruby
684+
settings.certificate = "CERT B"
685+
settings.private_key = "PRIVATE KEY FOR CERT B"
686+
```
587687
588688
## Single Log Out
589689
590-
The Ruby Toolkit supports SP-initiated Single Logout and IdP-Initiated Single Logout.
690+
Ruby SAML supports SP-initiated Single Logout and IdP-Initiated Single Logout.
591691
592692
Here is an example that we could add to our previous controller to generate and send a SAML Logout Request to the IdP:
593693
@@ -700,38 +800,6 @@ def logout
700800
end
701801
```
702802
703-
704-
705-
## Service Provider Metadata
706-
707-
To form a trusted pair relationship with the IdP, the SP (you) need to provide metadata XML
708-
to the IdP for various good reasons. (Caching, certificate lookups, relaying party permissions, etc)
709-
710-
The class `OneLogin::RubySaml::Metadata` takes care of this by reading the Settings and returning XML. All you have to do is add a controller to return the data, then give this URL to the IdP administrator.
711-
712-
The metadata will be polled by the IdP every few minutes, so updating your settings should propagate
713-
to the IdP settings.
714-
715-
```ruby
716-
class SamlController < ApplicationController
717-
# ... the rest of your controller definitions ...
718-
def metadata
719-
settings = Account.get_saml_settings
720-
meta = OneLogin::RubySaml::Metadata.new
721-
render :xml => meta.generate(settings), :content_type => "application/samlmetadata+xml"
722-
end
723-
end
724-
```
725-
726-
You can add ValidUntil and CacheDuration to the XML Metadata using instead
727-
```ruby
728-
# Valid until => 2 days from now
729-
# Cache duration = 604800s = 1 week
730-
valid_until = Time.now + 172800
731-
cache_duration = 604800
732-
meta.generate(settings, false, valid_until, cache_duration)
733-
```
734-
735803
## Clock Drift
736804
737805
Server clocks tend to drift naturally. If during validation of the response you get the error "Current time is earlier than NotBefore condition", this may be due to clock differences between your system and that of the Identity Provider.
@@ -766,7 +834,7 @@ The `attribute_value` option additionally accepts an array of possible values.
766834
## Custom Metadata Fields
767835
768836
Some IdPs may require to add SPs to add additional fields (Organization, ContactPerson, etc.)
769-
into the SP metadata. This can be acheived by extending the `OneLogin::RubySaml::Metadata`
837+
into the SP metadata. This can be achieved by extending the `OneLogin::RubySaml::Metadata`
770838
class and overriding the `#add_extras` method as per the following example:
771839
772840
```ruby

0 commit comments

Comments
 (0)