# 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.
[](https://hex.pm/packages/smee_gen)
[](http://hexdocs.pm/smee_gen/)

[](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.