Skip to main content

README.md

# SmeeGen
<img src="https://raw.githubusercontent.com/Digital-Identity-Labs/smee/a897646d54d5c6c7ef852b11a0e5d64611147580/logo.png" width="128px" alt="Smee Logo" style="float: right; margin: 6px;">

`SmeeGen` is an extension to [Smee](https://github.com/Digital-Identity-Labs/smee) that generates new SAML metadata. It's mainly intended to create
  SAML-format metadata for the *non-SAML* authentication protocols, CAS and OIDC, that are supported by the
  Shibboleth IdP, but will also be able to create vanilla SAML metadata too. At present only SAML SPs, CAS 
  clients and OIDC RPs are supported.

Unlike other Smee extensions SmeeGen does not depend on the Smee package and can be installed on its own.


[![Hex pm](http://img.shields.io/hexpm/v/smee_gen.svg?style=flat)](https://hex.pm/packages/smee_gen)
[![API Docs](https://img.shields.io/badge/api-docs-yellow.svg?style=flat)](http://hexdocs.pm/smee_gen/)
![Github Elixir CI](https://github.com/Digital-Identity-Labs/smee_gen/workflows/Elixir%20CI/badge.svg)

[![Run in Livebook](https://livebook.dev/badge/v1/blue.svg)](https://livebook.dev/run?url=https%3A%2F%2Fraw.githubusercontent.com%2FDigital-Identity-Labs%2Fsmee_gen%2Fmain%2Fsmee_gen_notebook.livemd)

## Features

* Create new CAS client records in SAML metadata format
* Create new OpenID Connect and Oauth RP records in SAML metadata format
* Create new metadata records for SAML SPs (work in progress)
* And that's it - this is profoundly uninteresting software unless you need to automate the creation of metadata

## Uses

* Converting OIDC and CAS configurations into SAML format allows the use of many more Shibboleth IdP features:
  * Friendly MDUI names, logos and privacy information
  * Registration information
  * Organization data
  * Metadata-driven configuration
  * Contacts - these are incredibly helpful when managing lots of metadata in a large organization
* Moving CAS and OIDC services over to an existing Shibboleth IdP can reduce the number of services needed, 
  and reduce costs and risks. SmeeGen can be used to automate the migration of services by converting their 
  configuration into SAML metadata
* SmeeGen can be combined with a UI to manage metadata for local services.

SmeeGen is **not** intended to be used for federated services - it's focused on metadata for services within an 
  organization.

## Progress

- [x] OIDC RP metadata
- [x] CAS client metadata
- [ ] SAML SP metadata (WIP)
- [ ] Templates for common software and apps
- [ ] Comprehensive tests

## Examples

### Create a very minimal CAS metadata entry
```elixir
SmeeGen.cas!("https://sp.example.org")
```
outputs:
```xml

<EntityDescriptor entityID="https://sp.example.org" xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
                  xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
                  xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute"
                  xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi">
  <SPSSODescriptor protocolSupportEnumeration="https://www.apereo.org/cas/protocol">
    <SingleLogoutService Binding="https://www.apereo.org/cas/protocol/logout"
                         Location="urn:mace:shibboleth:profile:CAS:logout"/>
    <AssertionConsumerService Binding="https://www.apereo.org/cas/protocol/login" Location="https//sp.example.org/"
                              index="1"/>
  </SPSSODescriptor>
</EntityDescriptor>
```

### Create a much larger CAS metadata entry
```elixir


xml = SmeeGen.cas!(
  "https://example.com/cas",
  service_urls: ["https://example.com/secure", "https://example.com/admin"],
  proxy_urls: ["https://whatever.org/proxy"],
  fragment: false,
  id: "1001",
  valid_until: expires_at,
  cache_duration: duration,
  proxy_keys: [cert],
  displayname: "Test XML metadata",
  information_url: "https://example.com/info",
  description: "Just testing things",
  privacy_url: "https://example.com/privacy_policy",
  privacy_urls: %{
    "fr" => "https://example.com/privacy_policy_fr"
  },
  keywords: %{
    "en" => ["one", "two", "something else"]
  },
  logos: [[url: "https://example.com/logo.png", width: 50, height: 50]],
  contacts: [
    [
      company: "Example Ltd",
      email: "security@example.com",
      surname: "Security",
      givenname: "Security",
      phone: "0161 496 0001",
      type: :sirtfi
    ],
    [
      company: "Example Ltd",
      email: "support@example.com",
      surname: "Support",
      givenname: "Online",
      type: :technical
    ]
  ],
  organization: [
    name: "Example",
    url: "https://example.com"
  ],
  ras: true,
  registration_authority: "https://regauth.example.org",
  registration_instant: DateTime.utc_now(),
  registration_policy_url: "https://blah.example.org"
)

```
outputs:
```xml
<EntityDescriptor entityID="https://example.com/cas" id="1001" validUntil="2026-07-29T16:07:46Z" cacheDuration="PT6H"
                  xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
                  xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute"
                  xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi">
  <Extensions>
    <mdrpi:RegistrationInfo registrationAuthority="https://regauth.example.org"
                            registrationInstant="2026-07-15T16:09:24Z">
      <mdrpi:RegistrationPolicy xml:lang="en">https://blah.example.org</mdrpi:RegistrationPolicy>
    </mdrpi:RegistrationInfo>
    <mdattr:EntityAttributes>
      <saml:Attribute Name="http://macedir.org/entity-category-support"
                      NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
        <saml:AttributeValue>http://refeds.org/category/research-and-scholarship</saml:AttributeValue>
      </saml:Attribute>
    </mdattr:EntityAttributes>
  </Extensions>
  <SPSSODescriptor protocolSupportEnumeration="https://www.apereo.org/cas/protocol">
    <Extensions>
      <mdui:UIInfo>
        <mdui:DisplayName xml:lang="en">Test XML metadata</mdui:DisplayName>
        <mdui:Description xml:lang="en">Just testing things</mdui:Description>
        <mdui:Keywords xml:lang="en">one two something+else</mdui:Keywords>
        <mdui:InformationURL xml:lang="en">https://example.com/info</mdui:InformationURL>
        <mdui:PrivacyStatementURL xml:lang="en">https://example.com/privacy_policy</mdui:PrivacyStatementURL>
        <mdui:PrivacyStatementURL xml:lang="fr">https://example.com/privacy_policy_fr</mdui:PrivacyStatementURL>
        <mdui:Logo xml:lang="en" width="50" height="50">https://example.com/logo.png</mdui:Logo>
      </mdui:UIInfo>
    </Extensions>
    <KeyDescriptor use="signing">
      <ds:KeyInfo>
        <ds:X509Data>
          <ds:X509Certificate>
            MIIDODCCAiCgAwIBAgIJAKpLQTw/WPXCMA0GCSqGSIb3DQEBCwUAMBwxGjAYBgNV
            BAMTEWFscGhhLmV4YW1wbGUub3JnMB4XDTE4MDYxODE2NDE0NVoXDTE4MDcxODE2
            NDE0NVowHDEaMBgGA1UEAxMRYWxwaGEuZXhhbXBsZS5vcmcwggEiMA0GCSqGSIb3
            DQEBAQUAA4IBDwAwggEKAoIBAQDHSzRUcM0WBtAjR3P1vHYkaaATjNKTxbNHn3zS
            3mLnEgukOVFrr+cRByKKUQQb8MIPkuvKrz3lnoCoOwlFMRPigtChjo3UJGTYEMY9
            2SQQr24U6nE/3d2qFaf2PNIW1SinSjxbE1xeT0bdLcTZHUcE2yEfHKFhcgXIJprv
            R1ceBJBvYYnATuPgUxMjq2ks4kXxG0nNlT13QwBfykBv6I1Wkkc06mEvkMzKNtzr
            ayBK1PygVBNVMUQAFn7Tv6c28BtVLFE9SIKj+5ZcpuWkujVNJF1dYdNmfAz3PiuE
            dPt2yl3t2r/v4CP+U8kBlQs6A83xYrA0MsHnUYOrfL3UTWtZAgMBAAGjfTB7MB0G
            A1UdDgQWBBT/5yBm3mXtsYDvz11kTHsPVGeRcDBMBgNVHSMERTBDgBT/5yBm3mXt
            sYDvz11kTHsPVGeRcKEgpB4wHDEaMBgGA1UEAxMRYWxwaGEuZXhhbXBsZS5vcmeC
            CQCqS0E8P1j1wjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAb/o/M
            mt/nSHOfcjnNJS/LpouaewkoWkQn+FaXZOOvHDYhWur+mHVDpjoszUfgrTX2npmL
            e8Q94bHd+cQrJpZFiYRX8l0p7dAH5Q6Ya/AnHuzGeyQ9fXiDMSWcsg2INcWi7oL9
            h9+V3idcSzgAo1b7+ESSToPj7OG8tgjEp2C9jy0IKEwoApuQtRzxD1XHZFBFwwuH
            nIXWxgctJPU1C+1W9b4bkFSyEGz8/HM7D9feDHbn2AKuRgd99aaOY9D59topf2Zg
            t5sUTWWl54eaF5qoXKY/jdl84Tnmo8GeUufCrS0T6YQGI1LTpicPbqf7zHihQTao
            I1TQuJgghwPvPE9x
          </ds:X509Certificate>
        </ds:X509Data>
      </ds:KeyInfo>
    </KeyDescriptor>
    <SingleLogoutService Binding="https://www.apereo.org/cas/protocol/logout"
                         Location="urn:mace:shibboleth:profile:CAS:logout"/>
    <AssertionConsumerService Binding="https://www.apereo.org/cas/protocol/login" Location="https://example.com/secure"
                              index="1"/>
    <AssertionConsumerService Binding="https://www.apereo.org/cas/protocol/login" Location="https://example.com/admin"
                              index="2"/>
    <AssertionConsumerService Binding="https://www.apereo.org/cas/protocol/proxy" Location="https://whatever.org/proxy"
                              index="3"/>
  </SPSSODescriptor>
  <Organization>
    <OrganizationName xml:lang="en">Example</OrganizationName>
    <OrganizationDisplayName xml:lang="en">Example</OrganizationDisplayName>
    <OrganizationURL xml:lang="en">https://example.com</OrganizationURL>
  </Organization>
  <ContactPerson contactType="other" xmlns:remd="https://refeds.org/metadata"
                 remd:contactType="http://refeds.org/metadata/contactType/security">
    <Company>Example Ltd</Company>
    <GivenName>Security</GivenName>
    <SurName>Security</SurName>
    <EmailAddress>mailto:security@example.com</EmailAddress>
    <TelephoneNumber>0161 496 0001</TelephoneNumber>
  </ContactPerson>
  <ContactPerson contactType="technical">
    <Company>Example Ltd</Company>
    <GivenName>Online</GivenName>
    <SurName>Support</SurName>
    <EmailAddress>mailto:support@example.com</EmailAddress>
  </ContactPerson>
</EntityDescriptor>
```

### Validating the resulting XML with Smee

```elixir
SmeeGen.cas!("https://sp.example.org")
|> Smee.Entity.new()
|> Smee.Entity.validate!()
```

## Installation

If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `smee_gen` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:smee_gen, "~> 0.1"}
  ]
end
```

> [!WARNING]  
> Please make sure you validate any metadata produced by SmeeGen before deploying it: this is an early release
> and it will absolutely contain mistakes and may produce invalid XML.

## Alternatives

I normally list other projects that provide similar functionality but in this case I can't think of any. If you 
  know of one please let me know and I will include it here.

## Additional Information

### SAML metadata standards

* [Metadata for SAML](https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf)
* [SAML Metadata UI - MDUI](https://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-metadata-ui/v1.0/sstc-saml-metadata-ui-v1.0.html)
* [Metadata Extensions for Registration and Publication Information](https://docs.oasis-open.org/security/saml/Post2.0/saml-metadata-rpi/v1.0/cs01/saml-metadata-rpi-v1.0-cs01.html)

### Shibboleth's SAML metadata profiles for CAS

* [CAS Service SAML Metadata](https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199506569/CASServiceSAMLMetadata)
* [CAS Metadata Profile](https://shibboleth.atlassian.net/wiki/spaces/SC/pages/1879344655/CASMetadataProfile)

### Shibboleth's SAML metadata profiles for OIDC

* [OP Metadata Client Registration - SAML](https://shibboleth.atlassian.net/wiki/spaces/IDPPLUGINS/pages/1376879133/OPMetadataClientRegistration#SAML/XML-Format)
* [OAuth RP Metadata Profile](https://shibboleth.atlassian.net/wiki/spaces/SC/pages/1912406916/OAuthRPMetadataProfile)

## Documentation

Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at <https://hexdocs.pm/smee_gen>.

## Contributing

You can request new features by creating an [issue](https://github.com/Digital-Identity-Labs/smee_gen/issues),
or submit a [pull request](https://github.com/Digital-Identity-Labs/smee_gen/pulls) with your contribution.

If you are comfortable working with Python but Smee's Elixir code is unfamiliar then this blog post may help:
[Elixir For Humans Who Know Python](https://hibox.live/elixir-for-humans-who-know-python)

Please do not submit any PRs or issues generated by "AI". This is a slop-free project and all mistakes are carefully
hand-crafted by humans.

## Copyright and License

Copyright (c) 2026 Digital Identity Ltd, UK

SmeeGen is Apache licensed.

## Disclaimers
SmeeGen is not endorsed by The Shibboleth Foundation or any of the organizations mentioned within the code or data.
The API may change considerably in the first few releases after 0.1.0.