Skip to content

Commit 7444b81

Browse files
atezetdavidv1992
andauthored
Add RequestedAuthnContext (#413)
* Add support for RequestedAuthnContext Co-authored-by: David Venhoek <[email protected]>
1 parent 00a79cc commit 7444b81

File tree

4 files changed

+73
-36
lines changed

4 files changed

+73
-36
lines changed

samlsp/new.go

+29-27
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,21 @@ import (
1414

1515
// Options represents the parameters for creating a new middleware
1616
type Options struct {
17-
EntityID string
18-
URL url.URL
19-
Key *rsa.PrivateKey
20-
Certificate *x509.Certificate
21-
Intermediates []*x509.Certificate
22-
HTTPClient *http.Client
23-
AllowIDPInitiated bool
24-
DefaultRedirectURI string
25-
IDPMetadata *saml.EntityDescriptor
26-
SignRequest bool
27-
UseArtifactResponse bool
28-
ForceAuthn bool // TODO(ross): this should be *bool
29-
CookieSameSite http.SameSite
30-
RelayStateFunc func(w http.ResponseWriter, r *http.Request) string
17+
EntityID string
18+
URL url.URL
19+
Key *rsa.PrivateKey
20+
Certificate *x509.Certificate
21+
Intermediates []*x509.Certificate
22+
HTTPClient *http.Client
23+
AllowIDPInitiated bool
24+
DefaultRedirectURI string
25+
IDPMetadata *saml.EntityDescriptor
26+
SignRequest bool
27+
UseArtifactResponse bool
28+
ForceAuthn bool // TODO(ross): this should be *bool
29+
RequestedAuthnContext *saml.RequestedAuthnContext
30+
CookieSameSite http.SameSite
31+
RelayStateFunc func(w http.ResponseWriter, r *http.Request) string
3132
}
3233

3334
// DefaultSessionCodec returns the default SessionCodec for the provided options,
@@ -102,19 +103,20 @@ func DefaultServiceProvider(opts Options) saml.ServiceProvider {
102103
}
103104

104105
return saml.ServiceProvider{
105-
EntityID: opts.EntityID,
106-
Key: opts.Key,
107-
Certificate: opts.Certificate,
108-
HTTPClient: opts.HTTPClient,
109-
Intermediates: opts.Intermediates,
110-
MetadataURL: *metadataURL,
111-
AcsURL: *acsURL,
112-
SloURL: *sloURL,
113-
IDPMetadata: opts.IDPMetadata,
114-
ForceAuthn: forceAuthn,
115-
SignatureMethod: signatureMethod,
116-
AllowIDPInitiated: opts.AllowIDPInitiated,
117-
DefaultRedirectURI: opts.DefaultRedirectURI,
106+
EntityID: opts.EntityID,
107+
Key: opts.Key,
108+
Certificate: opts.Certificate,
109+
HTTPClient: opts.HTTPClient,
110+
Intermediates: opts.Intermediates,
111+
MetadataURL: *metadataURL,
112+
AcsURL: *acsURL,
113+
SloURL: *sloURL,
114+
IDPMetadata: opts.IDPMetadata,
115+
ForceAuthn: forceAuthn,
116+
RequestedAuthnContext: opts.RequestedAuthnContext,
117+
SignatureMethod: signatureMethod,
118+
AllowIDPInitiated: opts.AllowIDPInitiated,
119+
DefaultRedirectURI: opts.DefaultRedirectURI,
118120
}
119121
}
120122

schema.go

+25-8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,24 @@ import (
1111
"github.com/russellhaering/goxmldsig/etreeutils"
1212
)
1313

14+
// RequestedAuthnContext represents the SAML object of the same name, an indication of the
15+
// requirements on the authentication process.
16+
type RequestedAuthnContext struct {
17+
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol RequestedAuthnContext"`
18+
Comparison string `xml:",attr"`
19+
AuthnContextClassRef string `xml:"urn:oasis:names:tc:SAML:2.0:assertion AuthnContextClassRef"`
20+
}
21+
22+
// Element returns an etree.Element representing the object in XML form.
23+
func (r *RequestedAuthnContext) Element() *etree.Element {
24+
el := etree.NewElement("samlp:RequestedAuthnContext")
25+
el.CreateAttr("Comparison", r.Comparison)
26+
elContext := etree.NewElement("saml:AuthnContextClassRef")
27+
elContext.SetText(r.AuthnContextClassRef)
28+
el.AddChild(elContext)
29+
return el
30+
}
31+
1432
// AuthnRequest represents the SAML object of the same name, a request from a service provider
1533
// to authenticate a user.
1634
//
@@ -26,10 +44,10 @@ type AuthnRequest struct {
2644
Issuer *Issuer `xml:"urn:oasis:names:tc:SAML:2.0:assertion Issuer"`
2745
Signature *etree.Element
2846

29-
Subject *Subject
30-
NameIDPolicy *NameIDPolicy `xml:"urn:oasis:names:tc:SAML:2.0:protocol NameIDPolicy"`
31-
Conditions *Conditions
32-
//RequestedAuthnContext *RequestedAuthnContext // TODO
47+
Subject *Subject
48+
NameIDPolicy *NameIDPolicy `xml:"urn:oasis:names:tc:SAML:2.0:protocol NameIDPolicy"`
49+
Conditions *Conditions
50+
RequestedAuthnContext *RequestedAuthnContext
3351
//Scoping *Scoping // TODO
3452

3553
ForceAuthn *bool `xml:",attr"`
@@ -159,7 +177,6 @@ func (r *LogoutRequest) Deflate() ([]byte, error) {
159177
return b.Bytes(), nil
160178
}
161179

162-
// Element returns an etree.Element representing the object
163180
// Element returns an etree.Element representing the object in XML form.
164181
func (r *AuthnRequest) Element() *etree.Element {
165182
el := etree.NewElement("samlp:AuthnRequest")
@@ -189,9 +206,9 @@ func (r *AuthnRequest) Element() *etree.Element {
189206
if r.Conditions != nil {
190207
el.AddChild(r.Conditions.Element())
191208
}
192-
//if r.RequestedAuthnContext != nil {
193-
// el.AddChild(r.RequestedAuthnContext.Element())
194-
//}
209+
if r.RequestedAuthnContext != nil {
210+
el.AddChild(r.RequestedAuthnContext.Element())
211+
}
195212
//if r.Scoping != nil {
196213
// el.AddChild(r.Scoping.Element())
197214
//}

schema_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,19 @@ func TestAuthnStatementMarshalWithoutSessionNotOnOrAfter(t *testing.T) {
9595
assert.Check(t, is.DeepEqual(expected, actual))
9696
}
9797

98+
func TestRequestedAuthnContext(t *testing.T) {
99+
expected := RequestedAuthnContext{
100+
Comparison: "comparison",
101+
}
102+
103+
doc := etree.NewDocument()
104+
doc.SetRoot(expected.Element())
105+
x, err := doc.WriteToBytes()
106+
assert.Check(t, err)
107+
assert.Check(t, is.Equal(`<samlp:RequestedAuthnContext Comparison="comparison"><saml:AuthnContextClassRef/></samlp:RequestedAuthnContext>`,
108+
string(x)))
109+
}
110+
98111
func TestArtifactResolveElement(t *testing.T) {
99112
issueInstant := time.Date(2020, 7, 21, 12, 30, 45, 0, time.UTC)
100113
expected := ArtifactResolve{

service_provider.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ type ServiceProvider struct {
102102
// has a SSO session at the IdP.
103103
ForceAuthn *bool
104104

105+
// RequestedAuthnContext allow you to specify the requested authentication
106+
// context in authentication requests
107+
RequestedAuthnContext *RequestedAuthnContext
108+
105109
// AllowIdpInitiated
106110
AllowIDPInitiated bool
107111

@@ -407,7 +411,8 @@ func (sp *ServiceProvider) MakeAuthenticationRequest(idpURL string, binding stri
407411
// urn:oasis:names:tc:SAML:2.0:nameid-format:transient
408412
Format: &nameIDFormat,
409413
},
410-
ForceAuthn: sp.ForceAuthn,
414+
ForceAuthn: sp.ForceAuthn,
415+
RequestedAuthnContext: sp.RequestedAuthnContext,
411416
}
412417
// We don't need to sign the XML document if the IDP uses HTTP-Redirect binding
413418
if len(sp.SignatureMethod) > 0 && binding == HTTPPostBinding {

0 commit comments

Comments
 (0)