Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cacheDuration, validUntil to RoleDescriptors #33

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ jobs:
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: v1.46.2
version: v1.54.2
7 changes: 6 additions & 1 deletion example/trivial/trivial.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"log"
"net/http"
"net/url"
"time"

"github.com/crewjam/saml/samlsp"
)
Expand Down Expand Up @@ -72,5 +73,9 @@ func main() {
http.Handle("/hello", samlMiddleware.RequireAccount(app))
http.Handle("/saml/", samlMiddleware)
http.Handle("/logout", slo)
log.Fatal(http.ListenAndServe(":8000", nil))
server := &http.Server{
Addr: ":8000",
ReadHeaderTimeout: 3 * time.Second,
}
log.Fatal(server.ListenAndServe())
}
2 changes: 2 additions & 0 deletions identity_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ func (idp *IdentityProvider) Metadata() *EntityDescriptor {
SSODescriptor: SSODescriptor{
RoleDescriptor: RoleDescriptor{
ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol",
CacheDuration: validDuration,
ValidUntil: TimeNow().Add(validDuration),
KeyDescriptors: []KeyDescriptor{
{
Use: "signing",
Expand Down
5 changes: 4 additions & 1 deletion identity_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ func TestIDPCanProduceMetadata(t *testing.T) {
{
SSODescriptor: SSODescriptor{
RoleDescriptor: RoleDescriptor{
ValidUntil: TimeNow().Add(DefaultValidDuration),
CacheDuration: DefaultValidDuration,
ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol",
KeyDescriptors: []KeyDescriptor{
{
Expand Down Expand Up @@ -207,7 +209,8 @@ func TestIDPHTTPCanHandleMetadataRequest(t *testing.T) {
test.IDP.Handler().ServeHTTP(w, r)
assert.Check(t, is.Equal(http.StatusOK, w.Code))
assert.Check(t, is.Equal("application/samlmetadata+xml", w.Header().Get("Content-type")))
assert.Check(t, strings.HasPrefix(string(w.Body.Bytes()), "<EntityDescriptor"),
body := string(w.Body.Bytes())
assert.Check(t, strings.HasPrefix(body, "<EntityDescriptor"),
string(w.Body.Bytes()))
}

Expand Down
171 changes: 167 additions & 4 deletions metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ type ContactPerson struct {
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.1
type RoleDescriptor struct {
ID string `xml:",attr,omitempty"`
ValidUntil *time.Time `xml:"validUntil,attr,omitempty"`
ValidUntil time.Time `xml:"validUntil,attr,omitempty"`
CacheDuration time.Duration `xml:"cacheDuration,attr,omitempty"`
ProtocolSupportEnumeration string `xml:"protocolSupportEnumeration,attr"`
ErrorURL string `xml:"errorURL,attr,omitempty"`
Expand Down Expand Up @@ -214,9 +214,9 @@ type SSODescriptor struct {
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.3
type IDPSSODescriptor struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata IDPSSODescriptor"`
SSODescriptor
WantAuthnRequestsSigned *bool `xml:",attr"`
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata IDPSSODescriptor"`
WantAuthnRequestsSigned *bool `xml:",attr"`

SingleSignOnServices []Endpoint `xml:"SingleSignOnService"`
ArtifactResolutionServices []Endpoint `xml:"ArtifactResolutionService"`
Expand All @@ -226,18 +226,82 @@ type IDPSSODescriptor struct {
Attributes []Attribute `xml:"Attribute"`
}

func (m IDPSSODescriptor) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
type Alias IDPSSODescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration Duration `xml:"cacheDuration,attr,omitempty"`
*Alias
}{
ValidUntil: RelaxedTime(m.ValidUntil),
CacheDuration: Duration(m.CacheDuration),
Alias: (*Alias)(&m),
}
return e.Encode(aux)
}

// UnmarshalXML implements xml.Unmarshaler
func (m *IDPSSODescriptor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type Alias IDPSSODescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration Duration `xml:"cacheDuration,attr,omitempty"`
*Alias
}{
Alias: (*Alias)(m),
}
if err := d.DecodeElement(aux, &start); err != nil {
return err
}
m.ValidUntil = time.Time(aux.ValidUntil)
m.CacheDuration = time.Duration(aux.CacheDuration)
return nil
}

// SPSSODescriptor represents the SAML SPSSODescriptorType object.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.2
type SPSSODescriptor struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata SPSSODescriptor"`
SSODescriptor
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata SPSSODescriptor"`
AuthnRequestsSigned *bool `xml:",attr"`
WantAssertionsSigned *bool `xml:",attr"`
AssertionConsumerServices []IndexedEndpoint `xml:"AssertionConsumerService"`
AttributeConsumingServices []AttributeConsumingService `xml:"AttributeConsumingService"`
}

func (m SPSSODescriptor) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
type Alias SPSSODescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration Duration `xml:"cacheDuration,attr,omitempty"`
*Alias
}{
ValidUntil: RelaxedTime(m.ValidUntil),
CacheDuration: Duration(m.CacheDuration),
Alias: (*Alias)(&m),
}
return e.Encode(aux)
}

// UnmarshalXML implements xml.Unmarshaler
func (m *SPSSODescriptor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type Alias SPSSODescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration Duration `xml:"cacheDuration,attr,omitempty"`
*Alias
}{
Alias: (*Alias)(m),
}
if err := d.DecodeElement(aux, &start); err != nil {
return err
}
m.ValidUntil = time.Time(aux.ValidUntil)
m.CacheDuration = time.Duration(aux.CacheDuration)
return nil
}

// AttributeConsumingService represents the SAML AttributeConsumingService object.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.4.1
Expand All @@ -262,33 +326,132 @@ type RequestedAttribute struct {
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.5
type AuthnAuthorityDescriptor struct {
RoleDescriptor
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata AuthnAuthorityDescriptor"`
AuthnQueryServices []Endpoint `xml:"AuthnQueryService"`
AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"`
NameIDFormats []NameIDFormat `xml:"NameIDFormat"`
}

func (m AuthnAuthorityDescriptor) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
type Alias AuthnAuthorityDescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration Duration `xml:"cacheDuration,attr,omitempty"`
*Alias
}{
ValidUntil: RelaxedTime(m.ValidUntil),
CacheDuration: Duration(m.CacheDuration),
Alias: (*Alias)(&m),
}
return e.Encode(aux)
}

// UnmarshalXML implements xml.Unmarshaler
func (m *AuthnAuthorityDescriptor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type Alias AuthnAuthorityDescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration Duration `xml:"cacheDuration,attr,omitempty"`
*Alias
}{
Alias: (*Alias)(m),
}
if err := d.DecodeElement(aux, &start); err != nil {
return err
}
m.ValidUntil = time.Time(aux.ValidUntil)
m.CacheDuration = time.Duration(aux.CacheDuration)
return nil
}

// PDPDescriptor represents the SAML PDPDescriptor object.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.6
type PDPDescriptor struct {
RoleDescriptor
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata PDPDescriptor"`
AuthzServices []Endpoint `xml:"AuthzService"`
AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"`
NameIDFormats []NameIDFormat `xml:"NameIDFormat"`
}

func (m PDPDescriptor) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
type Alias PDPDescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration Duration `xml:"cacheDuration,attr,omitempty"`
*Alias
}{
ValidUntil: RelaxedTime(m.ValidUntil),
CacheDuration: Duration(m.CacheDuration),
Alias: (*Alias)(&m),
}
return e.Encode(aux)
}

// UnmarshalXML implements xml.Unmarshaler
func (m *PDPDescriptor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type Alias PDPDescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration Duration `xml:"cacheDuration,attr,omitempty"`
*Alias
}{
Alias: (*Alias)(m),
}
if err := d.DecodeElement(aux, &start); err != nil {
return err
}
m.ValidUntil = time.Time(aux.ValidUntil)
m.CacheDuration = time.Duration(aux.CacheDuration)
return nil
}

// AttributeAuthorityDescriptor represents the SAML AttributeAuthorityDescriptor object.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.7
type AttributeAuthorityDescriptor struct {
RoleDescriptor
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata AttributeAuthorityDescriptor"`
AttributeServices []Endpoint `xml:"AttributeService"`
AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"`
NameIDFormats []NameIDFormat `xml:"NameIDFormat"`
AttributeProfiles []string `xml:"AttributeProfile"`
Attributes []Attribute `xml:"Attribute"`
}

func (m AttributeAuthorityDescriptor) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
type Alias AttributeAuthorityDescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration Duration `xml:"cacheDuration,attr,omitempty"`
*Alias
}{
ValidUntil: RelaxedTime(m.ValidUntil),
CacheDuration: Duration(m.CacheDuration),
Alias: (*Alias)(&m),
}
return e.Encode(aux)
}

// UnmarshalXML implements xml.Unmarshaler
func (m *AttributeAuthorityDescriptor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type Alias AttributeAuthorityDescriptor
aux := &struct {
ValidUntil RelaxedTime `xml:"validUntil,attr,omitempty"`
CacheDuration Duration `xml:"cacheDuration,attr,omitempty"`
*Alias
}{
Alias: (*Alias)(m),
}
if err := d.DecodeElement(aux, &start); err != nil {
return err
}
m.ValidUntil = time.Time(aux.ValidUntil)
m.CacheDuration = time.Duration(aux.CacheDuration)
return nil
}

// AffiliationDescriptor represents the SAML AffiliationDescriptor object.
//
// See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.5
Expand Down
5 changes: 4 additions & 1 deletion metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ func TestCanParseMetadata(t *testing.T) {
CacheDuration: time.Hour,
SPSSODescriptors: []SPSSODescriptor{
{
XMLName: xml.Name{Space: "urn:oasis:names:tc:SAML:2.0:metadata", Local: "SPSSODescriptor"},
SSODescriptor: SSODescriptor{
RoleDescriptor: RoleDescriptor{
ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol",
ValidUntil: time.Date(2001, time.February, 3, 4, 5, 6, 789000000, time.UTC),
CacheDuration: time.Hour,
},
},
AuthnRequestsSigned: &False,
Expand Down Expand Up @@ -101,6 +102,8 @@ func TestCanProduceSPMetadata(t *testing.T) {
WantAssertionsSigned: &WantAssertionsSigned,
SSODescriptor: SSODescriptor{
RoleDescriptor: RoleDescriptor{
ValidUntil: validUntil,
CacheDuration: DefaultCacheDuration,
ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol",
KeyDescriptors: []KeyDescriptor{
{
Expand Down
2 changes: 1 addition & 1 deletion samlidp/testdata/http_metadata_response.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2015-12-03T01:57:09Z" cacheDuration="PT48H" entityID="https://idp.example.com/metadata">
<IDPSSODescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<IDPSSODescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2015-12-03T01:57:09Z" cacheDuration="PT48H" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<KeyDescriptor use="signing">
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data xmlns="http://www.w3.org/2000/09/xmldsig#">
Expand Down
2 changes: 1 addition & 1 deletion samlsp/testdata/expected_middleware_metadata.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2015-12-03T01:57:09.123Z" entityID="https://15661444.ngrok.io/saml2/metadata">
<SPSSODescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2015-12-03T01:57:09.123456789Z" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" AuthnRequestsSigned="false" WantAssertionsSigned="true">
<SPSSODescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2015-12-03T01:57:09.123Z" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" AuthnRequestsSigned="false" WantAssertionsSigned="true">
<KeyDescriptor use="encryption">
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data xmlns="http://www.w3.org/2000/09/xmldsig#">
Expand Down
2 changes: 1 addition & 1 deletion samlsp/testdata/idp_metadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<mdalg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
<mdalg:SigningMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
</Extensions>
<IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:mace:shibboleth:1.0 urn:oasis:names:tc:SAML:2.0:protocol">
<IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:mace:shibboleth:1.0 urn:oasis:names:tc:SAML:2.0:protocol" cacheDuration='PT1H'>
<Extensions>
<shibmd:Scope regexp="false">testshib.org</shibmd:Scope>
<mdui:UIInfo>
Expand Down
4 changes: 2 additions & 2 deletions schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,7 @@ const (
StatusNoAvailableIDP = "urn:oasis:names:tc:SAML:2.0:status:NoAvailableIDP"

// StatusNoPassive means Indicates the responding provider cannot authenticate the principal passively, as has been requested.
StatusNoPassive = "urn:oasis:names:tc:SAML:2.0:status:NoPassive" //nolint:gosec
StatusNoPassive = "urn:oasis:names:tc:SAML:2.0:status:NoPassive" // #nosec G101

// StatusNoSupportedIDP is used by an intermediary to indicate that none of the identity providers in an <IDPList> are supported by the intermediary.
StatusNoSupportedIDP = "urn:oasis:names:tc:SAML:2.0:status:NoSupportedIDP"
Expand All @@ -667,7 +667,7 @@ const (
StatusRequestUnsupported = "urn:oasis:names:tc:SAML:2.0:status:RequestUnsupported"

// StatusRequestVersionDeprecated means the SAML responder cannot process any requests with the protocol version specified in the request.
StatusRequestVersionDeprecated = "urn:oasis:names:tc:SAML:2.0:status:RequestVersionDeprecated"
StatusRequestVersionDeprecated = "urn:oasis:names:tc:SAML:2.0:status:RequestVersionDeprecated" // #nosec G101

// StatusRequestVersionTooHigh means the SAML responder cannot process the request because the protocol version specified in the request message is a major upgrade from the highest protocol version supported by the responder.
StatusRequestVersionTooHigh = "urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooHigh"
Expand Down
4 changes: 2 additions & 2 deletions service_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ func (sp *ServiceProvider) Metadata() *EntityDescriptor {
RoleDescriptor: RoleDescriptor{
ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol",
KeyDescriptors: keyDescriptors,
ValidUntil: &validUntil,
ValidUntil: validUntil,
},
SingleLogoutServices: sloEndpoints,
NameIDFormats: []NameIDFormat{sp.AuthnNameIDFormat},
Expand Down Expand Up @@ -1664,7 +1664,7 @@ func elementToBytes(el *etree.Element) ([]byte, error) {
for _, attr := range currentElement.Attr {
// "xmlns" is either the space or the key of the attribute, depending on whether it is a default namespace declaration or not
if attr.Space == "xmlns" || attr.Key == "xmlns" {
// If the namespace is already preset in the list, it means that a child element has overriden it, so skip it
// If the namespace is already preset in the list, it means that a child element has overridden it, so skip it
if _, prefixExists := namespaces[attr.FullKey()]; !prefixExists {
namespaces[attr.FullKey()] = attr.Value
}
Expand Down
24 changes: 23 additions & 1 deletion testdata/TestCanParseMetadata_metadata.xml
Original file line number Diff line number Diff line change
@@ -1 +1,23 @@
<?xml version='1.0' encoding='UTF-8'?><md:EntityDescriptor ID='_af805d1c-c2e3-444e-9cf5-efc664eeace6' entityID='https://dev.aa.kndr.org/users/auth/saml/metadata' validUntil='2001-02-03T04:05:06.789' cacheDuration='PT1H' xmlns:md='urn:oasis:names:tc:SAML:2.0:metadata' xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'><md:SPSSODescriptor AuthnRequestsSigned='false' WantAssertionsSigned='false' protocolSupportEnumeration='urn:oasis:names:tc:SAML:2.0:protocol'><md:AssertionConsumerService Binding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' Location='https://dev.aa.kndr.org/users/auth/saml/callback' index='0' isDefault='true'/><md:AttributeConsumingService index='1' isDefault='true'><md:ServiceName xml:lang='en'>Required attributes</md:ServiceName><md:RequestedAttribute FriendlyName='Email address' Name='email' NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic'/><md:RequestedAttribute FriendlyName='Full name' Name='name' NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic'/><md:RequestedAttribute FriendlyName='Given name' Name='first_name' NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic'/><md:RequestedAttribute FriendlyName='Family name' Name='last_name' NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic'/></md:AttributeConsumingService></md:SPSSODescriptor></md:EntityDescriptor>
<?xml version='1.0' encoding='UTF-8'?>
<md:EntityDescriptor ID='_af805d1c-c2e3-444e-9cf5-efc664eeace6'
entityID='https://dev.aa.kndr.org/users/auth/saml/metadata' validUntil='2001-02-03T04:05:06.789'
cacheDuration='PT1H' xmlns:md='urn:oasis:names:tc:SAML:2.0:metadata'
xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'>
<md:SPSSODescriptor AuthnRequestsSigned='false' WantAssertionsSigned='false'
protocolSupportEnumeration='urn:oasis:names:tc:SAML:2.0:protocol' validUntil='2001-02-03T04:05:06.789'
cacheDuration='PT1H'>
<md:AssertionConsumerService Binding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
Location='https://dev.aa.kndr.org/users/auth/saml/callback' index='0' isDefault='true' />
<md:AttributeConsumingService index='1' isDefault='true'>
<md:ServiceName xml:lang='en'>Required attributes</md:ServiceName>
<md:RequestedAttribute FriendlyName='Email address' Name='email'
NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic' />
<md:RequestedAttribute FriendlyName='Full name' Name='name'
NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic' />
<md:RequestedAttribute FriendlyName='Given name' Name='first_name'
NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic' />
<md:RequestedAttribute FriendlyName='Family name' Name='last_name'
NameFormat='urn:oasis:names:tc:SAML:2.0:attrname-format:basic' />
</md:AttributeConsumingService>
</md:SPSSODescriptor>
</md:EntityDescriptor>
Loading
Loading