Skip to content

Instantly share code, notes, and snippets.

@theseal
Last active May 24, 2023 08:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save theseal/ed1d08a2c2c67dae6fc27675a1e913fa to your computer and use it in GitHub Desktop.
Save theseal/ed1d08a2c2c67dae6fc27675a1e913fa to your computer and use it in GitHub Desktop.
SWAMID Hackaton Shibboleth multifaktorinloggning
SWAMID Hackaton Shibboleth multifaktorinloggning
<AttributeFilterPolicy id="saml-proxy-pass-through">
<PolicyRequirementRule xsi:type="Issuer" value="https://idp.dev.eduid.se/idp.xml" />
<AttributeRule attributeID="norEduPersonNIN" permitAny="true" />
</AttributeFilterPolicy>
<AttributeDefinition xsi:type="SubjectDerivedAttribute"
forCanonicalization="true"
principalAttributeName="norEduPersonNIN"
id="proxied-nin"
/>
<MetadataProvider xsi:type="FilesystemMetadataProvider" xmlns="urn:mace:shibboleth:2.0:metadata"
id="MyMetadata"
metadataFile="%{idp.home}/metadata/eduid-dev.xml" />
<!--
curl 'https://metadata.swamid.se/?rawXML=264&download' > /opt/shibboleth-idp/metadata/eduid-dev.xml
-->
# Properties that control authentication generally and the behavior of
# specific methods.
# Regular expression matching login flows to enable, e.g. IPAddress|Password
idp.authn.flows = MFA
# Default settings for most authentication methods.
#idp.authn.defaultLifetime = PT1H
#idp.authn.defaultTimeout = PT30M
#idp.authn.proxyRestrictionsEnforced = true
# Whether to populate relying party user interface information for display
# during authentication, consent, terms-of-use.
#idp.authn.rpui = true
# Whether to prioritize "active" results when an SP requests more than
# one possible matching login method (V2 behavior was to favor them)
#idp.authn.favorSSO = false
# Whether to fail requests when a user identity after authentication
# doesn't match the identity in a pre-existing session.
#idp.authn.identitySwitchIsError = false
# If using IdP discovery feature, provides a discovery location to use.
#idp.authn.discoveryURL = https://ds.example.org/shibboleth-ds/index.html
# Login flow audit logging (defaults false for log compatibility)
#idp.authn.audit.enabled = false
# Revocation (administrative logout)
#idp.authn.revocation = false
#idp.authn.revocation.lifetime = %{idp.authn.defaultAuthnLifetime:PT12H}
# Name of BiCondition to apply for check
#idp.authn.revocation.Condition = shibboleth.RevocationCacheCondition
# Set to true to treat lookup failures as being revoked.
#idp.authn.revocation.strict = false
# Set to true to check for address-based revocation.
#idp.authn.revocation.addressBased = false
# Default implementation based on a StorageService bean.
#idp.authn.revocation.cache = shibboleth.AuthnRevocationCache
#idp.authn.revocation.StorageService = shibboleth.StorageService
# Properties below override specific method behavior, as an alternative
# to defining Spring beans in XML. Refer to the documentation for a complete
# list. Many of the properties below are mentioned only because they are
# atypical defaults assumed for a given method.
# Flow selection among multiple equivalent options can be managed with
# the order properties, lower will be tried first.
#### Password ####
#idp.authn.Password.order = 1000
#idp.authn.Password.passiveAuthenticationSupported = true
#idp.authn.Password.forcedAuthenticationSupported = true
# Override this and removeAfterValidation to require all validators to succeed
#idp.authn.Password.requireAll = false
# Override to keep the password around
#idp.authn.Password.removeAfterValidation = true
# Override to store password in Java Subject
#idp.authn.Password.retainAsPrivateCredential = false
# Simple username transforms before validation
#idp.authn.Password.trim = true
#idp.authn.Password.lowercase = false
#idp.authn.Password.uppercase = false
#idp.authn.Password.matchExpression =
# Override default form field names
#idp.authn.Password.usernameFieldName = j_username
#idp.authn.Password.passwordFieldName = j_password
#idp.authn.Password.ssoBypassFieldName = donotcache
# Unset if using customized Principals per validator
#idp.authn.Password.addDefaultPrincipals = true
# The Principal collection below is the typical default if not otherwise noted.
#idp.authn.Password.supportedPrincipals = \
# saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport, \
# saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:Password, \
# saml1/urn:oasis:names:tc:SAML:1.0:am:password
# Validators are controlled in password-authn-config.xml
#### Password Backends ####
# See ldap.properties for LDAP authn properties
# Kerberos settings
#idp.authn.Krb5.refreshConfig = false
#idp.authn.Krb5.preserveTicket = false
# Set next two for KDC verification
#idp.authn.Krb5.servicePrincipal =
#idp.authn.Krb5.keytab =
# JAAS settings
#idp.authn.JAAS.loginConfigNames = ShibUserPassAuth
#idp.authn.JAAS.loginConfig = %{idp.home}/conf/authn/jaas.config
#### External ####
#idp.authn.External.order = 1000
#idp.authn.External.nonBrowserSupported = false
#idp.authn.External.matchExpression =
# Unset if you plan to return full Java Subject from external source
#idp.authn.External.addDefaultPrincipals = true
# Servlet context-relative path to wherever your implementation lives
idp.authn.External.externalAuthnPath = contextRelative:external.jsp
#### RemoteUser ####
#idp.authn.RemoteUser.order = 1000
#idp.authn.RemoteUser.nonBrowserSupported = false
#idp.authn.RemoteUser.matchExpression =
# Unset in most cases only if using the authnMethodHeader or
# subjectAttribute settings
#idp.authn.RemoteUser.addDefaultPrincipals = true
# Most other settings need to be supplied via web.xml to the servlet
#### RemoteUserInternal ####
#idp.authn.RemoteUserInternal.order = 1000
#idp.authn.RemoteUserInternal.nonBrowserSupported = true
# Unset in most cases only if using the authnMethodHeader feature
#idp.authn.RemoteUserInternal.addDefaultPrincipals = true
#idp.authn.RemoteUserInternal.checkRemoteUser = true
# Comma-delimited lists of attributes or headers to pull from
#idp.authn.RemoteUserInternal.checkAttributes =
#idp.authn.RemoteUserInternal.checkHeaders =
# Simple transforms to apply
#idp.authn.RemoteUserInternal.trim = true
#idp.authn.RemoteUserInternal.lowercase = false
#idp.authn.RemoteUserInternal.uppercase = false
#idp.authn.RemoteUserInternal.matchExpression =
#idp.authn.RemoteUserInternal.allowedUsernames =
#idp.authn.RemoteUserInternal.deniedUsernames =
#### SPNEGO ####
#idp.authn.SPNEGO.order = 1000
#idp.authn.SPNEGO.nonBrowserSupported = false
#idp.authn.SPNEGO.enforceRun = false
#idp.authn.SPNEGO.refreshKrbConfig = false
#idp.authn.SPNEGO.matchExpression =
idp.authn.SPNEGO.supportedPrincipals = \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos, \
saml1/urn:ietf:rfc:1510
#### X509 ####
#idp.authn.X509.order = 1000
#idp.authn.X509.nonBrowserSupported = false
# Servlet context-relative path to wherever your implementation lives
#idp.authn.X509.externalAuthnPath = contextRelative:x509-prompt.jsp
idp.authn.X509.supportedPrincipals = \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:X509, \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient, \
saml1/urn:ietf:rfc:2246
#### X509Internal ####
#idp.authn.X509Internal.order = 1000
#idp.authn.X509Internal.nonBrowserSupported = false
#idp.authn.X509Internal.saveCertificateToCredentialSet = true
idp.authn.X509Internal.supportedPrincipals = \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:X509, \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient, \
saml1/urn:ietf:rfc:2246
#### IPAddress ####
#idp.authn.IPAddress.order = 1000
#idp.authn.IPAddress.passiveAuthenticationSupported = true
#idp.authn.IPAddress.lifetime = PT60S
#idp.authn.IPAddress.inactivityTimeout = PT60S
idp.authn.IPAddress.supportedPrincipals = \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol
#### Function ####
#idp.authn.Function.order = 1000
#idp.authn.Function.passiveAuthenticationSupported = true
# Unset if you plan to return full Java Subject from function
#idp.authn.Function.addDefaultPrincipals = true
#### Duo ####
#idp.authn.Duo.order = 1000
#idp.authn.Duo.nonBrowserSupported = false
#idp.authn.Duo.forcedAuthenticationSupported = true
# Unset if you have advanced Duo integrations with individualized Principals
#idp.authn.Duo.addDefaultPrincipals = true
# The list below should be changed to reflect whatever locally- or
# community-defined values are appropriate to represent Duo. It is
# strongly advised that the value not be specific to Duo or any
# particular technology to avoid lock-in.
idp.authn.Duo.supportedPrincipals = \
saml2/http://example.org/ac/classes/mfa, \
saml1/http://example.org/ac/classes/mfa
# Default Duo integration settings are defined separately
# in duo.properties due to the sensitivity of the secret key.
#### SAML ####
#idp.authn.SAML.order = 1000
#idp.authn.SAML.nonBrowserSupported = false
#idp.authn.SAML.passiveAuthenticationSupported = true
#idp.authn.SAML.forcedAuthenticationSupported = true
#idp.authn.SAML.proxyScopingEnforced = true
# Discovery options:
# Define shibboleth.authn.SAML.discoveryFunction bean
# Set proxyEntityID property
# Fall through to discovery via discoveryRequired property
#idp.authn.SAML.proxyEntityID = https://idp.dev.eduid.se/idp.xml
idp.authn.SAML.proxyEntityID = https://idp-sweden-connect-valfr-2017-sandbox.test.frejaeid.com
#idp.authn.SAML.discoveryRequired = true
# Generally left false with bidirectional mappings in
# conf/authn/authn-comparison.xml across the proxy boundary.
# Adjust as needed to reflect IdP's capabilities/support.
#idp.authn.SAML.addDefaultPrincipals = false
idp.authn.SAML.supportedPrincipals = \
saml2/http://id.elegnamnden.se/loa/1.0/loa3, \
saml2/https://refeds.org/profile/mfa, \
saml1/https://refeds.org/profile/mfa
#### MFA ####
#idp.authn.MFA.order = 1000
#idp.authn.MFA.passiveAuthenticationSupported = true
#idp.authn.MFA.forcedAuthenticationSupported = true
#idp.authn.MFA.validateLoginTransitions = true
# The list below almost certainly requires changes, and should generally be the
# union of any of the separate factors you combine in your particular MFA flow
# rules. The example corresponds to the example in mfa-authn-config.xml that
# combines IPAddress with Password.
idp.authn.MFA.supportedPrincipals = \
saml2/http://id.elegnamnden.se/loa/1.0/loa3, \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol, \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport, \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:Password, \
saml1/urn:oasis:names:tc:SAML:1.0:am:password, \
saml2/https://refeds.org/profile/mfa, \
saml1/https://refeds.org/profile/mfa
# Most actual setup via mfa-authn-config.xml
--- conf/authn/authn.properties 2023-05-17 11:37:54.319811928 +0000
+++ dist/conf/authn/authn.properties 2023-03-30 13:29:46.000000000 +0000
@@ -2,7 +2,7 @@
# specific methods.
# Regular expression matching login flows to enable, e.g. IPAddress|Password
-idp.authn.flows = MFA
+#idp.authn.flows = Password
# Default settings for most authentication methods.
#idp.authn.defaultLifetime = PT1H
@@ -206,17 +206,16 @@
# Define shibboleth.authn.SAML.discoveryFunction bean
# Set proxyEntityID property
# Fall through to discovery via discoveryRequired property
-#idp.authn.SAML.proxyEntityID = https://idp.dev.eduid.se/idp.xml
-idp.authn.SAML.proxyEntityID = https://idp-sweden-connect-valfr-2017-sandbox.test.frejaeid.com
+#idp.authn.SAML.proxyEntityID = https://idp.example.org/idp/shibboleth
#idp.authn.SAML.discoveryRequired = true
# Generally left false with bidirectional mappings in
# conf/authn/authn-comparison.xml across the proxy boundary.
# Adjust as needed to reflect IdP's capabilities/support.
#idp.authn.SAML.addDefaultPrincipals = false
+idp.authn.SAML.supportedPrincipals = \
+ saml2/https://refeds.org/profile/mfa, \
+ saml1/https://refeds.org/profile/mfa
-#idp.authn.SAML.supportedPrincipals = \
-# saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport, \
-# saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:Password, \
-# saml1/urn:oasis:names:tc:SAML:1.0:am:password
#### MFA ####
@@ -229,11 +228,8 @@
# rules. The example corresponds to the example in mfa-authn-config.xml that
# combines IPAddress with Password.
idp.authn.MFA.supportedPrincipals = \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol, \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport, \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:Password, \
+ saml1/urn:oasis:names:tc:SAML:1.0:am:password, \
+ saml2/https://refeds.org/profile/mfa, \
+ saml1/https://refeds.org/profile/mfa
- saml1/urn:oasis:names:tc:SAML:1.0:am:password
# Most actual setup via mfa-authn-config.xml
# LDAP authentication (and possibly attribute resolver) configuration
# Note, this doesn't apply to the use of JAAS authentication via LDAP
## Authenticator strategy, either anonSearchAuthenticator, bindSearchAuthenticator, directAuthenticator, adAuthenticator
#idp.authn.LDAP.authenticator = anonSearchAuthenticator
## Connection properties ##
idp.authn.LDAP.ldapURL = ldap://172.16.0.2:389
idp.authn.LDAP.useStartTLS = false
# Time in milliseconds that connects will block
#idp.authn.LDAP.connectTimeout = PT3S
# Time in milliseconds to wait for responses
#idp.authn.LDAP.responseTimeout = PT3S
# Connection strategy to use when multiple URLs are supplied, either ACTIVE_PASSIVE, ROUND_ROBIN, RANDOM
#idp.authn.LDAP.connectionStrategy = ACTIVE_PASSIVE
## SSL configuration, either jvmTrust, certificateTrust, or keyStoreTrust
#idp.authn.LDAP.sslConfig = certificateTrust
## If using certificateTrust above, set to the trusted certificate's path
idp.authn.LDAP.trustCertificates = %{idp.home}/credentials/ldap-server.crt
## If using keyStoreTrust above, set to the truststore path
idp.authn.LDAP.trustStore = %{idp.home}/credentials/ldap-server.truststore
## Return attributes during authentication
idp.authn.LDAP.returnAttributes = passwordExpirationTime,loginGraceRemaining
## DN resolution properties ##
# Search DN resolution, used by anonSearchAuthenticator, bindSearchAuthenticator
# for AD: CN=Users,DC=example,DC=org
idp.authn.LDAP.baseDN = ou=people,dc=qa,dc=swamid,dc=se
#idp.authn.LDAP.subtreeSearch = false
idp.authn.LDAP.userFilter = (uid={user})
# bind search configuration
# for AD: idp.authn.LDAP.bindDN=adminuser@domain.com
idp.authn.LDAP.bindDN = cn=professor1,ou=People,dc=qa,dc=swamid,dc=se
# Format DN resolution, used by directAuthenticator, adAuthenticator
# for AD use idp.authn.LDAP.dnFormat=%s@domain.com
idp.authn.LDAP.dnFormat = uid=%s,ou=people,dc=qa,dc=swamid,dc=se
# pool passivator, either none, bind or anonymousBind
#idp.authn.LDAP.bindPoolPassivator = none
# LDAP attribute configuration, see attribute-resolver.xml
# Note, this likely won't apply to the use of legacy V2 resolver configurations
idp.attribute.resolver.LDAP.ldapURL = %{idp.authn.LDAP.ldapURL}
idp.attribute.resolver.LDAP.connectTimeout = %{idp.authn.LDAP.connectTimeout:PT3S}
idp.attribute.resolver.LDAP.responseTimeout = %{idp.authn.LDAP.responseTimeout:PT3S}
idp.attribute.resolver.LDAP.connectionStrategy = %{idp.authn.LDAP.connectionStrategy:ACTIVE_PASSIVE}
idp.attribute.resolver.LDAP.baseDN = %{idp.authn.LDAP.baseDN:undefined}
idp.attribute.resolver.LDAP.bindDN = %{idp.authn.LDAP.bindDN:undefined}
idp.attribute.resolver.LDAP.useStartTLS = %{idp.authn.LDAP.useStartTLS:true}
idp.attribute.resolver.LDAP.trustCertificates = %{idp.authn.LDAP.trustCertificates:undefined}
idp.attribute.resolver.LDAP.searchFilter = (|(uid=$resolutionContext.principal)(norEduPersonNIN=$resolutionContext.principal))
# LDAP pool configuration, used for both authn and DN resolution
#idp.pool.LDAP.minSize = 3
#idp.pool.LDAP.maxSize = 10
#idp.pool.LDAP.validateOnCheckout = false
#idp.pool.LDAP.validatePeriodically = true
#idp.pool.LDAP.validatePeriod = PT5M
#idp.pool.LDAP.validateDN =
#idp.pool.LDAP.validateFilter = (objectClass=*)
#idp.pool.LDAP.prunePeriod = PT5M
#idp.pool.LDAP.idleTime = PT10M
#idp.pool.LDAP.blockWaitTime = PT3S
--- conf/ldap.properties 2023-05-11 12:09:52.116733811 +0000
+++ dist/conf/ldap.properties 2023-05-11 06:38:12.085000000 +0000
@@ -52,7 +52,7 @@
idp.attribute.resolver.LDAP.bindDN = %{idp.authn.LDAP.bindDN:undefined}
idp.attribute.resolver.LDAP.useStartTLS = %{idp.authn.LDAP.useStartTLS:true}
idp.attribute.resolver.LDAP.trustCertificates = %{idp.authn.LDAP.trustCertificates:undefined}
+idp.attribute.resolver.LDAP.searchFilter = (|(uid=$resolutionContext.principal)(norEduPersonNIN=$resolutionContext.principal))
-idp.attribute.resolver.LDAP.searchFilter = (uid=$resolutionContext.principal)
# LDAP pool configuration, used for both authn and DN resolution
#idp.pool.LDAP.minSize = 3
#
# https://shibboleth.atlassian.net/wiki/spaces/KB/pages/1459979597/Using+SAML+Proxying+to+another+IdP#UsingSAMLProxyingtoanotherIdP-UpdateyourIdP'smetadata
#
--- idp-metadata.xml 2023-05-17 13:16:45
+++ idp-metadata-with-sp.xml 2023-05-17 13:27:17
@@ -12,6 +12,91 @@
<mdrpi:RegistrationPolicy xml:lang="en">http://swamid.se/policy/mdrps</mdrpi:RegistrationPolicy>
</mdrpi:RegistrationInfo>
</md:Extensions>
+ <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+ <md:Extensions>
+ <mdui:UIInfo>
[..]
+ </mdui:UIInfo>
+ </md:Extensions>
+ <md:KeyDescriptor use="signing">
[...]
+ </md:KeyDescriptor>
+ <md:KeyDescriptor use="encryption">
[...]
+ </md:KeyDescriptor>
+ <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://shibmfa.test.swamid.se/idp/profile/Authn/SAML2/POST/SSO" index="0"/>
+ </md:SPSSODescriptor>
--- /tmp/mfa-authn-config.xml.idpnew 2023-05-17 11:50:11.406497913 +0000
+++ /tmp/mfa-authn-config.xml 2023-05-17 11:51:26.749225639 +0000
@@ -60,7 +60,7 @@
<constructor-arg>
<value>
<![CDATA[
- nextFlow = "authn/Password";
+ nextFlow = "authn/SAML";
// Check if second factor is necessary for request to be satisfied.
authCtx = input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");
<!--
Activate MFA:
/opt/shibboleth-idp/bin/module.sh -e idp.authn.MFA
-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
default-init-method="initialize"
default-destroy-method="destroy">
<!--
This is a map of transition rules that guide the behavior of the MFA flow
and controls how factors are sequenced, skipped, etc. The key of each entry
is the name of the step/flow out of which control is passing. The starting
rule has an empty key.
Each entry is a bean inherited from "shibboleth.authn.MFA.Transition". Per
the Javadoc for net.shibboleth.idp.authn.MultiFactorAuthenticationTransition:
p:nextFlow (String)
- A flow to run if the previous step signaled a "proceed" event, for simple
transitions.
p:nextFlowStrategy (Function<ProfileRequestContext,String>)
- A function to run if the previous step signaled a "proceed" event, for dynamic
transitions. Returning null ends the MFA process.
p:nextFlowStrategyMap (Map<String,Object> where Object is String or Function<ProfileRequestContext,String>)
- Fully dynamic way of expressing control paths. Map is keyed by a previously
signaled event and the value is a flow to run or a function to
return the flow to run. Returning null ends the MFA process.
When no rule is provided, there's an implicit "null" that ends the MFA flow
with whatever event was last signaled. If the "proceed" event from a step is
the final event, then the MFA process attempts to complete itself successfully.
-->
<util:map id="shibboleth.authn.MFA.TransitionMap">
<!-- First rule runs the IPAddress login flow. -->
<entry key="">
<bean parent="shibboleth.authn.MFA.Transition" p:nextFlow="authn/Password" />
</entry>
<!--
Second rule runs a function if IPAddress succeeds, to determine whether an additional
factor is required.
-->
<entry key="authn/Password">
<bean parent="shibboleth.authn.MFA.Transition" p:nextFlowStrategy-ref="checkSecondFactor" />
</entry>
<!-- An implicit final rule will return whatever the final flow returns. -->
</util:map>
<!-- Example script to see if second factor is required. -->
<bean id="checkSecondFactor" parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript">
<constructor-arg>
<value>
<![CDATA[
nextFlow = "authn/SAML";
// Check if second factor is necessary for request to be satisfied.
authCtx = input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");
mfaCtx = authCtx.getSubcontext("net.shibboleth.idp.authn.context.MultiFactorAuthenticationContext");
if (mfaCtx.isAcceptable()) {
nextFlow = null;
}
nextFlow; // pass control to second factor or end with the first
]]>
</value>
</constructor-arg>
</bean>
</beans>
# Properties that control the behavior of post-login subject c14n flows.
# A few more advanced settings require XML configuration, see flow-specific docs.
# Simple username -> principal name c14n
#idp.c14n.simple.lowercase = false
#idp.c14n.simple.uppercase = false
#idp.c14n.simple.trim = true
# Attribute resolution -> principal name c14n
#idp.c14n.attribute.lowercase = false
#idp.c14n.attribute.uppercase = false
#idp.c14n.attribute.trim = true
# Lists of attributes to resolve...
idp.c14n.attribute.attributesToResolve = proxied-nin
# and then select a principal name from
idp.c14n.attribute.attributeSourceIds = proxied-nin
# Allows direct use of attributes via SAML proxy authn, bypasses resolver
#idp.c14n.attribute.resolveFromSubject = false
#idp.c14n.attribute.resolutionCondition = shibboleth.Conditions.TRUE
# X.509 certificate -> principal name c14n
#idp.c14n.x500.lowercase = false
#idp.c14n.x500.uppercase = false
#idp.c14n.x500.trim = true
# Precedence is to check for a subjectAltName and then an OID RDN
# Comma-delimited list of subjectAltName type numbers
# (See https://tools.ietf.org/html/rfc5280#section-4.2.1.6)
#idp.c14n.x500.subjectAltNameTypes =
# Comma-delimited list of OIDS
#idp.c14n.x500.objectIDs =
# Proxied SAML NameID -> principal name c14n
#idp.c14n.saml.proxy.lowercase = false
#idp.c14n.saml.proxy.uppercase = false
# NameID consumption from SAML requests
#idp.c14n.saml.lowercase = false
#idp.c14n.saml.uppercase = false
--- conf/c14n/subject-c14n.properties 2023-05-11 10:42:55.824627047 +0000
+++ dist/conf/c14n/subject-c14n.properties 2023-03-30 13:29:46.000000000 +0000
@@ -13,9 +13,9 @@
#idp.c14n.attribute.uppercase = false
#idp.c14n.attribute.trim = true
# Lists of attributes to resolve...
+idp.c14n.attribute.attributesToResolve = proxied-nin
-#idp.c14n.attribute.attributesToResolve =
# and then select a principal name from
+idp.c14n.attribute.attributeSourceIds = proxied-nin
-#idp.c14n.attribute.attributeSourceIds =
# Allows direct use of attributes via SAML proxy authn, bypasses resolver
#idp.c14n.attribute.resolveFromSubject = false
#idp.c14n.attribute.resolutionCondition = shibboleth.Conditions.TRUE
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
default-init-method="initialize"
default-destroy-method="destroy">
<!-- ========================= Java Subject -> Principal Mapping ========================= -->
<!--
These are lists of Subject Canonicalization flows that turn complex Subject data into a string-based
principal name that the rest of the IdP can operate on. They're used primarily after authentication
and also during less common operations like SAML attribute queries, to map the SAML Subject into a
principal name.
Flows are identified with an ID that corresponds to a Spring Web Flow subflow name.
Most of the simple settings that configure these flows are in subject-c14n.properties.
-->
<!--
====================================================================
Flows used after authentication to produce canonical principal name.
====================================================================
-->
<util:list id="shibboleth.PostLoginSubjectCanonicalizationFlows">
<!--
This is an advanced post-login step that performs attribute resolution and then produces a username
from an attribute value. To enable universally, just uncomment, but if you want it to run under more
specific conditions, set an activationCondition property to a condition to apply.
-->
<bean id="c14n/attribute" parent="shibboleth.PostLoginSubjectCanonicalizationFlow" />
<!--
This is an advanced option for use with SAML 2 proxy authentication to a second IdP that
derives the principal name semi-directly from the incoming NameID value. It is functionally
akin to the c14n/SAML2Transform flow for SAML Request scenarios, but separately defined so
a suitably restrictive format list and/or condition can be applied to it.
-->
<!-- <ref bean="c14n/SAML2ProxyTransform" /> -->
<!--
This is an alternative that handles Subjects containing an X500Principal object and
allows extraction from the DN.
-->
<ref bean="c14n/x500" />
<!--
This is the standard post-login step that returns a username derived from the login process. If you
have more complex needs such as mapping a certificate DN into a principal name, an alternative may
be required such as that above, but you can use this for simple transforms.
-->
<ref bean="c14n/simple" />
</util:list>
<!-- What SAML NameID formats do you want to support direct transformations for while proxying? -->
<util:list id="shibboleth.ProxyNameTransformFormats">
<value>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</value>
<value>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</value>
<value>urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName</value>
<value>urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName</value>
<value>urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos</value>
</util:list>
<!--
Under what conditions should direct NameID mapping during proxying be allowed? By default, never.
Any condition can be used here; the example is suitable for enumerating a number of IdPs to allow.
-->
<bean id="shibboleth.ProxyNameTransformPredicate" parent="shibboleth.Conditions.ProxyAuthentication">
<constructor-arg name="collection">
<list>
<!-- <value>https://idp-proxy.example.org</value> -->
</list>
</constructor-arg>
</bean>
<!--
Regular expression transforms to apply to incoming proxied subject names. The default empty list just
echoes the name through unmodified.
-->
<util:list id="shibboleth.ProxyNameTransforms">
<!--
<bean parent="shibboleth.Pair" p:first="^(.+)@example\.org$" p:second="$1" />
-->
</util:list>
<!--
=======================================================================
Flows used during SAML requests to reverse-map NameIdentifiers/NameIDs.
Below the list are some settings that might be useful to adjust.
=======================================================================
-->
<util:list id="shibboleth.SAMLSubjectCanonicalizationFlows">
<!-- The next four are for handling transient IDs (in-storage and stateless variants). -->
<ref bean="c14n/SAML2Transient" />
<ref bean="c14n/SAML2CryptoTransient" />
<ref bean="c14n/SAML1Transient" />
<ref bean="c14n/SAML1CryptoTransient" />
<!-- Handle a SAML 2 persistent ID, provided a stored strategy is in use. -->
<!-- <ref bean="c14n/SAML2Persistent" /> -->
<!--
Finally we have beans for decoding arbitrary SAML formats directly. By default, these are turned off,
having *no* circumstances for which they apply (see shibboleth.TransformNamePredicate below).
-->
<ref bean="c14n/SAML2Transform" />
<ref bean="c14n/SAML1Transform" />
</util:list>
<!-- What SAML NameID formats do you want to support direct transformations for? -->
<util:list id="shibboleth.NameTransformFormats">
<value>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</value>
<value>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</value>
<value>urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName</value>
<value>urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName</value>
<value>urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos</value>
</util:list>
<!--
Under what conditions should direct NameID mapping be allowed? By default, never.
Any condition can be used here; the example is suitable for enumerating a number of SPs to allow.
-->
<bean id="shibboleth.NameTransformPredicate" parent="shibboleth.Conditions.RelyingPartyId">
<constructor-arg name="candidates">
<list>
<!-- <value>https://sp.example.org</value> -->
</list>
</constructor-arg>
</bean>
<!--
Regular expression transforms to apply to incoming subject names. The default empty list just
echoes the name through unmodified.
-->
<util:list id="shibboleth.NameTransforms">
<!--
<bean parent="shibboleth.Pair" p:first="^(.+)@example\.org$" p:second="$1" />
-->
</util:list>
<util:map id="umich.shibboleth.authn.MFA.customMap">
<entry key="AttributeResolver" value-ref="shibboleth.AttributeResolverService" />
<entry key="request" value-ref="shibboleth.HttpServletRequest" />
</util:map>
<bean id="shibboleth.c14n.attribute.PrincipalNameLookupStrategy"
parent="shibboleth.ContextFunctions.Scripted"
factory-method="inlineScript" p:customObject-ref="umich.shibboleth.authn.MFA.customMap">
<constructor-arg>
<value>
<![CDATA[
logger = Java.type("org.slf4j.LoggerFactory").getLogger("net.shibboleth.idp.attribute.resolver.eppnbuilder");
var principalName = null;
var subject = profileContext.getSubcontext("net.shibboleth.idp.authn.context.SubjectCanonicalizationContext").getSubject();
var princs = subject.getPrincipals(Java.type("net.shibboleth.idp.authn.principal.UsernamePrincipal").class);
var princs2 = subject.getPrincipals(Java.type("net.shibboleth.idp.authn.principal.IdPAttributePrincipal").class);
if (princs.size() == 1) {
principalName = princs.iterator().next().getName();
if (princs2.size() == 1) {
resCtx = input.getSubcontext("net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext", true);
resCtx.setPrincipal(principalName);
resCtx.getRequestedIdPAttributeNames().add("norEduPersonNIN");
resCtx.resolveAttributes(custom["AttributeResolver"]);
orig_nin = resCtx.getResolvedIdPAttributes().get("norEduPersonNIN").getValues().get(0).getValue();
eduid_nin = resCtx.getResolvedIdPAttributes().get("proxied-nin").getValues().get(0).getValue();
logger.info("SWAMID-origin-nin: " + orig_nin);
logger.info("SWAMID-eduid-nin: " + eduid_nin);
if (orig_nin != eduid_nin) {
throw new Error("'Identification attribute recived from eduID didn't match the local catalog'");
}
}
}
principalName;
]]>
</value>
</constructor-arg>
</bean>
</beans>
--- dist/conf/c14n/subject-c14n.xml 2023-03-30 13:29:46.000000000 +0000
+++ conf/c14n/subject-c14n.xml 2023-05-16 11:53:04.522101535 +0000
@@ -36,7 +36,7 @@
from an attribute value. To enable universally, just uncomment, but if you want it to run under more
specific conditions, set an activationCondition property to a condition to apply.
-->
- <!-- <bean id="c14n/attribute" parent="shibboleth.PostLoginSubjectCanonicalizationFlow" /> -->
+ <bean id="c14n/attribute" parent="shibboleth.PostLoginSubjectCanonicalizationFlow" />
<!--
This is an advanced option for use with SAML 2 proxy authentication to a second IdP that
@@ -147,5 +147,48 @@
<bean parent="shibboleth.Pair" p:first="^(.+)@example\.org$" p:second="$1" />
-->
</util:list>
-
+
+ <util:map id="umich.shibboleth.authn.MFA.customMap">
+ <entry key="AttributeResolver" value-ref="shibboleth.AttributeResolverService" />
+ <entry key="request" value-ref="shibboleth.HttpServletRequest" />
+ </util:map>
+
+ <bean id="shibboleth.c14n.attribute.PrincipalNameLookupStrategy"
+ parent="shibboleth.ContextFunctions.Scripted"
+ factory-method="inlineScript" p:customObject-ref="umich.shibboleth.authn.MFA.customMap">
+ <constructor-arg>
+ <value>
+ <![CDATA[
+ logger = Java.type("org.slf4j.LoggerFactory").getLogger("net.shibboleth.idp.attribute.resolver.eppnbuilder");
+
+ var principalName = null;
+ var subject = profileContext.getSubcontext("net.shibboleth.idp.authn.context.SubjectCanonicalizationContext").getSubject();
+ var princs = subject.getPrincipals(Java.type("net.shibboleth.idp.authn.principal.UsernamePrincipal").class);
+ var princs2 = subject.getPrincipals(Java.type("net.shibboleth.idp.authn.principal.IdPAttributePrincipal").class);
+
+
+ if (princs.size() == 1) {
+ principalName = princs.iterator().next().getName();
+
+ if (princs2.size() == 1) {
+ resCtx = input.getSubcontext("net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext", true);
+
+ resCtx.setPrincipal(principalName);
+ resCtx.getRequestedIdPAttributeNames().add("norEduPersonNIN");
+ resCtx.resolveAttributes(custom["AttributeResolver"]);
+
+ orig_nin = resCtx.getResolvedIdPAttributes().get("norEduPersonNIN").getValues().get(0).getValue();
+ eduid_nin = resCtx.getResolvedIdPAttributes().get("proxied-nin").getValues().get(0).getValue();
+ logger.info("SWAMID-origin-nin: " + orig_nin);
+ logger.info("SWAMID-eduid-nin: " + eduid_nin);
+ if (orig_nin != eduid_nin) {
+ throw new Error("'Identification attribute recived from eduID didn't match the local catalog'");
+ }
+ }
+ }
+ principalName;
+ ]]>
+ </value>
+ </constructor-arg>
+ </bean>
</beans>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment