{"id":13878047,"url":"https://github.com/saml-idp/saml_idp","last_synced_at":"2025-05-14T04:08:10.547Z","repository":{"id":9901753,"uuid":"11909053","full_name":"saml-idp/saml_idp","owner":"saml-idp","description":"Ruby SAML Identity Provider, best used with Rails (though not required)","archived":false,"fork":false,"pushed_at":"2025-01-21T17:29:50.000Z","size":462,"stargazers_count":270,"open_issues_count":19,"forks_count":192,"subscribers_count":71,"default_branch":"master","last_synced_at":"2025-05-13T11:47:34.336Z","etag":null,"topics":["identity-provider","rails","ruby","ruby-saml","saml"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/saml-idp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2013-08-05T21:18:16.000Z","updated_at":"2025-04-24T22:02:52.000Z","dependencies_parsed_at":"2024-09-18T01:14:36.700Z","dependency_job_id":"fe3a0078-ea36-4e91-931f-e749a539820a","html_url":"https://github.com/saml-idp/saml_idp","commit_stats":{"total_commits":356,"total_committers":44,"mean_commits":8.090909090909092,"dds":0.4662921348314607,"last_synced_commit":"78c18684fb232228c35e14667383ecaf55e603fe"},"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saml-idp%2Fsaml_idp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saml-idp%2Fsaml_idp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saml-idp%2Fsaml_idp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saml-idp%2Fsaml_idp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/saml-idp","download_url":"https://codeload.github.com/saml-idp/saml_idp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254069708,"owners_count":22009558,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["identity-provider","rails","ruby","ruby-saml","saml"],"created_at":"2024-08-06T08:01:38.358Z","updated_at":"2025-05-14T04:08:05.525Z","avatar_url":"https://github.com/saml-idp.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# Ruby SAML Identity Provider (IdP)\n\nForked from \u003chttps://github.com/lawrencepit/ruby-saml-idp\u003e\n\n[![Gem Version](https://badge.fury.io/rb/saml_idp.svg)](http://badge.fury.io/rb/saml_idp)\n\nThe ruby SAML Identity Provider library is for implementing the server side of SAML authentication. It allows\nyour application to act as an IdP (Identity Provider) using the\n[SAML v2.0](http://en.wikipedia.org/wiki/Security_Assertion_Markup_Language)\nprotocol. It provides a means for managing authentication requests and confirmation responses for SPs (Service Providers).\n\nThis was originally setup by @lawrencepit to test SAML Clients. I took it closer to a real\nSAML IDP implementation.\n\n## Installation and Usage\n\nAdd this to your Gemfile:\n\n```ruby\n    gem 'saml_idp'\n```\n\n### Not using rails?\n\nInclude `SamlIdp::Controller` and see the examples that use rails. It should be straightforward for you.\n\nBasically, you call `decode_request(params[:SAMLRequest])` on an incoming request and then use the value \n`saml_acs_url` to determine the source for which you need to authenticate a user. \nIf the signature (`Signature`) and signing algorithm (`SigAlg`) are provided as external parameters in the request,\nyou can pass those parameters as `decode_request(params[:SAMLRequest], params[:Signature], params[:SigAlg], params[:RelayState])`.\nThen, you can verify the request signature with the `valid?` method.\n\nHow you authenticate a user is entirely up to you.\n\nOnce a user has successfully authenticated on your system send the Service Provider a SAMLResponse by\nposting to `saml_acs_url` the parameter `SAMLResponse` with the return value from a call to\n`encode_response(user_email)`.\n\n### Using rails?\n\nCheck out our Wiki page for Rails integration\n[Rails Integration guide](https://github.com/saml-idp/saml_idp/wiki/Rails_Integration)\n\n### Configuration\n\n#### Signed assertions and Signed Response\n\nBy default SAML Assertion will be signed with an algorithm which defined to `config.algorithm`, because SAML assertions contain secure information used for authentication such as NameID.\nBesides that, signing assertions could be optional and can be defined with `config.signed_assertion` option. Setting this configuration flag to `false` will add raw assertions on the response instead of signed ones. If the response is encrypted the `config.signed_assertion` will be ignored and all assertions will be signed.\n\nSigning SAML Response is optional, but some security perspective SP services might require Response message itself must be signed.\nFor that, you can enable it with `signed_message: true` option for `encode_response(user_email, signed_message: true)` method. [More about SAML spec](https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=68)\n\n#### Signing algorithm\n\nFollowing algorithms you can set in your response signing algorithm\n:sha1 - RSA-SHA1 default value but not recommended to production environment\nHighly recommended to use one of following algorithm, suit with your computing power.\n:sha256 - RSA-SHA256\n:sha384 - RSA-SHA384\n:sha512 - RSA-SHA512\n\nBe sure to load a file like this during your app initialization:\n\n```ruby\nSamlIdp.configure do |config|\n  base = \"http://example.com\"\n\n  config.x509_certificate = \u003c\u003c-CERT\n-----BEGIN CERTIFICATE-----\nCERTIFICATE DATA\n-----END CERTIFICATE-----\nCERT\n\n  config.secret_key = \u003c\u003c-CERT\n-----BEGIN RSA PRIVATE KEY-----\nKEY DATA\n-----END RSA PRIVATE KEY-----\nCERT\n\n  # x509_certificate, secret_key, and password may also be set from within a proc, for example:\n  # config.x509_certificate = -\u003e { File.read(\"cert.pem\") }\n  # config.secret_key = -\u003e { SecretKeyFinder.key_for(id: 1) }\n  # config.password = -\u003e { \"password\" }\n\n  # config.password = \"secret_key_password\"\n  # config.algorithm = :sha256                                    # Default: sha1 only for development.\n  # config.organization_name = \"Your Organization\"\n  # config.organization_url = \"http://example.com\"\n  # config.base_saml_location = \"#{base}/saml\"\n  # config.reference_id_generator                                 # Default: -\u003e { SecureRandom.uuid }\n  # config.single_logout_service_post_location = \"#{base}/saml/logout\"\n  # config.single_logout_service_redirect_location = \"#{base}/saml/logout\"\n  # config.attribute_service_location = \"#{base}/saml/attributes\"\n  # config.single_service_post_location = \"#{base}/saml/auth\"\n  # config.session_expiry = 86400                                 # Default: 0 which means never\n  # config.signed_assertion = false                               # Default: true which means signed assertions on the SAML Response\n  # config.compress = true                                        # Default: false which means the SAML Response is not being compressed\n  # config.logger = ::Logger.new($stdout)                         # Default: if in Rails context - Rails.logger, else -\u003e(msg) { puts msg }. Works with either a Ruby Logger or a lambda\n\n  # Principal (e.g. User) is passed in when you `encode_response`\n  #\n  # config.name_id.formats =\n  #   {                         # All 2.0\n  #     email_address: -\u003e (principal) { principal.email_address },\n  #     transient: -\u003e (principal) { principal.id },\n  #     persistent: -\u003e (p) { p.id },\n  #   }\n  #   OR\n  #\n  #   {\n  #     \"1.1\" =\u003e {\n  #       email_address: -\u003e (principal) { principal.email_address },\n  #     },\n  #     \"2.0\" =\u003e {\n  #       transient: -\u003e (principal) { principal.email_address },\n  #       persistent: -\u003e (p) { p.id },\n  #     },\n  #   }\n\n  # If Principal responds to a method called `asserted_attributes`\n  # the return value of that method will be used in lieu of the\n  # attributes defined here in the global space. This allows for\n  # per-user attribute definitions.\n  #\n  ## EXAMPLE **\n  # class User\n  #   def asserted_attributes\n  #     {\n  #       phone: { getter: :phone },\n  #       email: {\n  #         getter: :email,\n  #         name_format: Saml::XML::Namespaces::Formats::NameId::EMAIL_ADDRESS,\n  #         name_id_format: Saml::XML::Namespaces::Formats::NameId::EMAIL_ADDRESS\n  #       }\n  #     }\n  #   end\n  # end\n  #\n  # If you have a method called `asserted_attributes` in your Principal class,\n  # there is no need to define it here in the config.\n\n  # config.attributes # =\u003e\n  #   {\n  #     \u003cfriendly_name\u003e =\u003e {                                                  # required (ex \"eduPersonAffiliation\")\n  #       \"name\" =\u003e \u003cattrname\u003e                                                # required (ex \"urn:oid:1.3.6.1.4.1.5923.1.1.1.1\")\n  #       \"name_format\" =\u003e \"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\", # not required\n  #       \"getter\" =\u003e -\u003e(principal) {                                         # not required\n  #         principal.get_eduPersonAffiliation                                # If no \"getter\" defined, will try\n  #       }                                                                   # `principal.eduPersonAffiliation`, or no values will\n  #    }                                                                      # be output\n  #\n  ## EXAMPLE ##\n  # config.attributes = {\n  #   GivenName: {\n  #     getter: :first_name,\n  #   },\n  #   SurName: {\n  #     getter: :last_name,\n  #   },\n  # }\n  ## EXAMPLE ##\n\n  # config.technical_contact.company = \"Example\"\n  # config.technical_contact.given_name = \"Jonny\"\n  # config.technical_contact.sur_name = \"Support\"\n  # config.technical_contact.telephone = \"55555555555\"\n  # config.technical_contact.email_address = \"example@example.com\"\n\n  service_providers = {\n    \"some-issuer-url.com/saml\" =\u003e {\n      fingerprint: \"9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D\",\n      metadata_url: \"http://some-issuer-url.com/saml/metadata\",\n\n      # We now validate AssertionConsumerServiceURL will match the MetadataURL set above.\n      # *If* it's not going to match your Metadata URL's Host, then set this so we can validate the host using this list\n      response_hosts: [\"foo.some-issuer-url.com\"]\n    },\n  }\n\n  # `identifier` is the entity_id or issuer of the Service Provider,\n  # settings is an IncomingMetadata object which has a to_h method that needs to be persisted\n  config.service_provider.metadata_persister = -\u003e(identifier, settings) {\n    fname = identifier.to_s.gsub(/\\/|:/,\"_\")\n    FileUtils.mkdir_p(Rails.root.join('cache', 'saml', 'metadata').to_s)\n    File.open Rails.root.join(\"cache/saml/metadata/#{fname}\"), \"r+b\" do |f|\n      Marshal.dump settings.to_h, f\n    end\n  }\n\n  # `identifier` is the entity_id or issuer of the Service Provider,\n  # `service_provider` is a ServiceProvider object. Based on the `identifier` or the\n  # `service_provider` you should return the settings.to_h from above\n  config.service_provider.persisted_metadata_getter = -\u003e(identifier, service_provider){\n    fname = identifier.to_s.gsub(/\\/|:/,\"_\")\n    FileUtils.mkdir_p(Rails.root.join('cache', 'saml', 'metadata').to_s)\n    full_filename = Rails.root.join(\"cache/saml/metadata/#{fname}\")\n    if File.file?(full_filename)\n      File.open full_filename, \"rb\" do |f|\n        Marshal.load f\n      end\n    end\n  }\n\n  # Find ServiceProvider metadata_url and fingerprint based on our settings\n  config.service_provider.finder = -\u003e(issuer_or_entity_id) do\n    service_providers[issuer_or_entity_id]\n  end\nend\n```\n\n## Keys and Secrets\n\nTo generate the SAML Response it uses a default X.509 certificate and secret key... which isn't so secret.\nYou can find them in `SamlIdp::Default`. The X.509 certificate is valid until year 2032.\nObviously you shouldn't use these if you intend to use this in production environments. In that case,\nwithin the controller set the properties `x509_certificate` and `secret_key` using a `prepend_before_action`\ncallback within the current request context or set them globally via the `SamlIdp.config.x509_certificate`\nand `SamlIdp.config.secret_key` properties.\n\nThe fingerprint to use, if you use the default X.509 certificate of this gem, is:\n\n```bash\n  9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D\n```\n\n## Fingerprint\n\nThe gem provides an helper to generate a fingerprint for a X.509 certificate.\nThe second parameter is optional and default to your configuration `SamlIdp.config.algorithm`\n\n```ruby\n  SamlIdp::Fingerprint.certificate_digest(x509_cert, :sha512)\n```\n\n## Service Providers\n\nTo act as a Service Provider which generates SAML Requests and can react to SAML Responses use the\nexcellent [ruby-saml](https://github.com/onelogin/ruby-saml) gem.\n\n## Author\n\nJon Phenow, jon@jphenow.com, jphenow.com, @jphenow\n\nLawrence Pit, lawrence.pit@gmail.com, lawrencepit.com, @lawrencepit\n\n## Copyright\n\nCopyright (c) 2012 Sport Ngin.\nPortions Copyright (c) 2010 OneLogin, LLC\nPortions Copyright (c) 2012 Lawrence Pit (http://lawrencepit.com)\n\nSee LICENSE for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaml-idp%2Fsaml_idp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaml-idp%2Fsaml_idp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaml-idp%2Fsaml_idp/lists"}