Wednesday, October 5, 2016

SSO using SAML

Single Sign On using Security Assertion Markup Language

Few options

Identity Provider (IDP) is used for authentication purposes only. It is replacing login screen :)
Service Provider (SP, application) will do authorization by reading roles from LDAP/database...
or
IDP will be used for authentication and authorization.
Problem with this approach is that SP does not know when role changes.

Session information can be stored using cookies on SP and IDP domain, so IDP can verify if user user is already logged on another SP.

Flow:

  1. User comes to SP (User is not logged. SP and IDP does not have any information about user)
  2. There is no session on SP for this user and user is redirected to IDP
  3. IDP authenticate user and send SAML response to SP
  4. SP validates SAML response and reads roles from datasource and the session is established
  5. User comes to SP2 (another application)
  6. SP2 does not have a session and asks IDP about this user.
  7. IDP knows this user has session so user is not provided with login screen. IDP reads data from datasource and returns SAML response to SP2
  8. SP2 validates SAML response and reads roles from LDAP
  9. User has seamlessly logged to SP2
Handle roles and password changes on SP.

IDP notes

IdP only reads, never having any sort of admin access. This helps security ­wise, knowing that authentication system can't write or retrieve sensitive information.

Common problems

Problem

DEBUG (SAMLProcessingFilter.java:99) - Incoming SAML message is invalid
org.opensaml.ws.security.SecurityPolicyException: Validation of protocol message signature failed

Solution

Verify that SP and IDP have proper metadata.

Problem

14:54:00.798 [http-nio-8082-exec-6] DEBUG (SAMLProcessingFilter.java:99) - Incoming SAML message is invalid
org.opensaml.ws.security.SecurityPolicyException: Validation of protocol message signature failed
at org.opensaml.common.binding.security.SAMLProtocolMessageXMLSignatureSecurityPolicyRule.doEvaluate(SAMLProtocolMessageXMLSignatureSecurityPolicyRule.java:138)
at org.opensaml.common.binding.security.SAMLProtocolMessageXMLSignatureSecurityPolicyRule.evaluate(SAMLProtocolMessageXMLSignatureSecurityPolicyRule.java:107)
at org.opensaml.ws.security.provider.BasicSecurityPolicy.evaluate(BasicSecurityPolicy.java:51)
at org.opensaml.ws.message.decoder.BaseMessageDecoder.processSecurityPolicy(BaseMessageDecoder.java:132)
at org.opensaml.ws.message.decoder.BaseMessageDecoder.decode(BaseMessageDecoder.java:83)
at org.opensaml.saml2.binding.decoding.BaseSAML2MessageDecoder.decode(BaseSAML2MessageDecoder.java:70)
at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:105)

Solution

Add IDP public key for signing messages to java key store. It can be found in incoming saml message from IDP.

http://stackoverflow.com/questions/23059203/http-status-401-authentication-failed-incoming-saml-message-is-invalid-with

Problem

InResponseToField of the Response doesn't correspond to sent message

Log
2016-10-26 12:33:20,159 DEBUG PROTOCOL_MESSAGE,http-nio-8080-exec-4:74 -
<?xml version="1.0" encoding="UTF-8"?>
<saml2p:AuthnRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" AssertionConsumerServiceURL="http://mercurybi-local:8080/mercurybi/saml/SSO" Destination="https://authstack/saml2.0/sso" ForceAuthn="false" ID="a293907a4b8ed0d21iggb0ci9dc0bbb" IsPassive="false" IssueInstant="2016-10-26T10:33:20.100Z" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0">
   <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">SAMARA_SP</saml2:Issuer>
</saml2p:AuthnRequest>

2016-10-26 12:33:22,427 DEBUG PROTOCOL_MESSAGE,http-nio-8080-exec-5:113 -
<?xml version="1.0" encoding="UTF-8"?><samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified" Destination="http://mercurybi-local:8080/mercurybi/saml/SSO" ID="_4981b4a45c61a289809108b15d7c401bb2c0bcdae5" InResponseTo="a293907a4b8ed0d21iggb0ci9dc0bbb" IssueInstant="2016-10-26T10:33:20Z" Version="2.0">
   <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://authstack</saml:Issuer>
   <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
  <ds:SignedInfo>

  2016-10-26 12:33:22,698 DEBUG HttpSessionStorage,http-nio-8080-exec-5:117 - Message a293907a4b8ed0d21iggb0ci9dc0bbb not found in session A9C98F53D692EDFA486D96FB3D9F67C6
2016-10-26 12:33:22,702 DEBUG SAMLAuthenticationProvider,http-nio-8080-exec-5:98 - Error validating SAML message
org.opensaml.common.SAMLException: InResponseToField of the Response doesn't correspond to sent message a293907a4b8ed0d21iggb0ci9dc0bbb

Solution

<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderImpl">
  <property name="storageFactory">
    <bean class="org.springframework.security.saml.storage.EmptyStorageFactory"/>
  </property>
</bean>

Problem and Solution

Application is hosted on 2 different machines using different domains.
IDP manages to return proper domain name based on following rules

  1. read where to return in SP metadata
  2. if AssertionConsumerServiceURL field is present in SAML request use that one


<?xml version="1.0" encoding="UTF-8"?>

<saml2p:AuthnRequest
    AssertionConsumerServiceURL="http://app.samara.hr:8080/app/saml/SSO"
    Destination="https://myIDP/saml2.0/sso" ForceAuthn="false"
    ID="a4bi2g9cj906hj4429i0b0413h5aji4" IsPassive="false"
    IssueInstant="2016-11-03T13:57:40.212Z"
    ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
    Version="2.0" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol">
    <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">SAMARA-SP</saml2:Issuer>
</saml2p:AuthnRequest>

Logout and SingleLogout feature

What will be URL used for redirects (if that option is used) update SP metadata:

<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://mydomain.samara.hr/myApp/saml/SingleLogout"/>

Send SAML request via POST

To be sure POST is used:

@Bean
public WebSSOProfileOptions defaultWebSSOProfileOptions() {
final WebSSOProfileOptions webSSOProfileOptions = new WebSSOProfileOptions();
webSSOProfileOptions.setIncludeScoping(false);
webSSOProfileOptions.setBinding(SAMLConstants.SAML2_POST_BINDING_URI);
return webSSOProfileOptions;
}

Ref:

SWITCH - Demo
okta - SAML
SAML Security Cheat Sheet
Spring Security SAML
Spring SAML - Troubleshooting common problems
Can one SP metadata file be used for 5 separate SPs running the same application

No comments:

Post a Comment