Spring saml redirects the request to http - spring-saml

I am using the spring saml extension with Apache 2.2 + Tomcat 7.0 + OKTA(IdP). The securityContext.xml looks as follows:
MetadataGeneratorFilter:
<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.MetadataGenerator">
<property name="entityBaseURL" value="https://myapp.com/api"/>
</bean>
</constructor-arg>
</bean>
ContextProvider:
<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderLB">
<property name="scheme" value="https"/>
<property name="serverName" value="myapp.com"/>
<property name="serverPort" value="443"/>
<property name="includeServerPortInRequestURL" value="false"/>
<property name="contextPath" value="/api"/>
<property name="storageFactory">
<bean class="org.springframework.security.saml.storage.EmptyStorageFactory"/>
</property>
</bean>
I use the following URL which triggers the SP initiated login.
https://myapp.com/api/welcome.html
After authentication is complete, the browser gets redirected to HTTP instead of HTTPS.
http://myapp.com/api/welcome.html
I don't know why the request gets redirected to HTTP. Please help.
Thanks
Nara
HTTP Requests Dump:
https://docs.google.com/document/d/1mYh-EhDjxMixzZ8krhOg_2fjpTaTu7fuST_nIXAMeVY/edit?usp=sharing
In OKTA, created a SAML 2.0 app with following metadata:
Single Sign On URL => https://myapp.com/api/saml/SSO
Recipient URL => https://myapp.com/api/saml/SSO
Destination URL => https://myapp.com/api/saml/SSO
Audience Restriction => https://myapp.com/api/saml/metadata
Default Relay State => https://myapp.com/dashboard.html
Name ID Format => Unspecified
Response => Signed
Assertion Signature => Signed
Signature Algorithm => RSA_SHA256
Digest Algorithm => SHA256
Assertion Encryption => Unencrypted
SAML Single Logout => Disabled
authnContextClassRef => PasswordProtectedTransport
Request Compression => Uncompressed
Honor Force Authentication => Yes
SAML Issuer ID => http://www.okta.com/${org.externalKey}
Spring Config:
https://docs.google.com/document/d/16iDLcBuwvQ23-mKMFybPfxdIyvqCBi5sbYePgUjl0p4/edit?usp=sharing

I still don't see the real reason, your configuration looks ok, but please try replacing the current successRedirectHandler with the following (replace URL with your own of course) and see if the issue goes away:
<bean id="successRedirectHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="https://yourapp.com/welcome.jsp"/>
</bean>

Make sure that your IDP is configured with proper metadata XML with https assertion end points.
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="**https**://../saml/SSO" index="0" isDefault="true" />
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="**https**://.../saml/SSO" index="1" />

Related

Spring SAML: SAML message intended destination endpoint did not match recipient endpoint

I am getting 'Caused by: org.opensaml.xml.security.SecurityException: SAML message intended destination endpoint did not match recipient endpoint' exception while SSO between my app SP and client IdP.
Server log show the difference in schemas, see below:
Checking SAML message intended destination endpoint against receiver endpoint
2019-03-05 15:02:44.599 DEBUG [204 default task-41][BaseSAMLMessageDecoder] Intended message destination endpoint: https://my.app.com/app-gateway/saml/SSO
2019-03-05 15:02:44.599 DEBUG [204 default task-41][BaseSAMLMessageDecoder] Actual message receiver endpoint: http://my.app.com/app-gateway/saml/SSO
2019-03-05 15:02:44.600 ERROR [204 default task-41][BaseSAMLMessageDecoder] SAML message intended destination endpoint 'https://my.app.com/app-gateway/saml/SSO' did not match the recipient endpoint 'http://my.app.com/app-gateway/saml/SSO'
My application is running on STG on 2 instances with the LB in front, therefore I use SAMLContextProviderLB context provider instead of SAMLContextProviderImpl:
<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderLB">
<property name="scheme" value="https"/>
<property name="serverName" value="my.app.com"/>
<property name="serverPort" value="443"/>
<property name="includeServerPortInRequestURL" value="false"/>
<property name="contextPath" value="/app-gateway"/>
</bean>
<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.MetadataGenerator">
<property name="entityBaseURL" value="https://my.app.com/app-gateway1"/>
<property name="entityId" value="${cas.sso.entityId}"/>
<property name="includeDiscoveryExtension" value="false"/>
<property name="extendedMetadata" ref="extendedMetadata"/>
<property name="keyManager" ref="keyManager"/>
</bean>
</constructor-arg>
</bean>
In the source code of getActualReceiverEndpointURI the receiver endpoint URL is being taken from request httpRequest obj. Thus, I am trying to understand at which step that wrong URL http://my.app.com/app-gateway/saml/SSO was set to it. Can anyone explain me it?
protected String getActualReceiverEndpointURI(SAMLMessageContext messageContext) throws MessageDecodingException {
InTransport inTransport = messageContext.getInboundMessageTransport();
if (! (inTransport instanceof HttpServletRequestAdapter)) {
log.error("Message context InTransport instance was an unsupported type: {}",
inTransport.getClass().getName());
throw new MessageDecodingException("Message context InTransport instance was an unsupported type");
}
HttpServletRequest httpRequest = ((HttpServletRequestAdapter)inTransport).getWrappedRequest();
StringBuffer urlBuilder = httpRequest.getRequestURL();
return urlBuilder.toString();
}
You might want to check the following page :
https://developer.jboss.org/thread/240113
I had a similar issue, even with X-Forwarded-Proto properly set on the LB, the request was still interpreted in http only.
The backend must be aware of the header.
add proxy-address-forwarding="true" on the http listener and two filter-ref
<http-listener name="default" socket-binding="http" proxy-address-forwarding="true"/>
<filter-ref name="server-header"/>
<filter-ref name="x-powered-by-header"/>
Hope this help,
For Apache Tomcat server, which is running behind AWS Application load balancer, need to enable the RemoteIPValue so that based on the x-forwarded-proto header, Tomcat will overwrite scheme(https) & port(443) accordingly.
In server.xml
<Valve className="org.apache.catalina.valves.RemoteIpValve" protocolHeader="X-Forwarded-Proto" internalProxies="10\.\d+\.\d+\.\d+|192\.168\.\d+\.\d+|169\.254\.\d+\.\d+|127\.\d+\.\d+\.\d+|172\.(1[6-9]|2[0-9]|3[0-1])\.\d+\.\d+" />

Spring Social Login: How to add more than one app of the same social provider (f.e. Facebook)?

My Spring MVC web application is serving simultanously three different web shops with three different domains (let's call them domain1.com, domain2.com and domain3.com).
For each of these shops I need to offer an own Social Login (with custom login screen title, custom data privacy statement, ...). Therefore I need to create three different social provider (f.e. facebook) apps.
In Spring Social - from my understanding - I'm allowed to add only one FacebookConnectionFactory and hereby only one provider app.
How can I despite that add these three different facebook apps to Spring Social? Is Spring Social capable to manage this? Are there workarounds existing?
May be I answered too late for the OP but this could be usefull for other developers.
I implemented a workaround for this issue defining my own "Registries" as follow:
<bean id="myConnectionFactoryRegistries" class="package.ShopsFactoryRegistries">
<property name="registries">
<map key-type="java.lang.String">
<entry key="shop1Key" value-ref="connectionFactoryRegistryShop1" />
<entry key="shop2Key" value-ref="connectionFactoryRegistryShop2" />
</map>
</property>
</bean>
<bean id="connectionFactoryRegistryShop1"
class="org.springframework.social.connect.support.ConnectionFactoryRegistry">
<property name="connectionFactories">
<list>
<bean
class="org.springframework.social.facebook.connect.FacebookConnectionFactory">
<constructor-arg value="${facebook.apikey.shop1}" />
<constructor-arg value="${facebook.apisecret.shop1}" />
</bean>
</list>
</property>
</bean>
<bean id="connectionFactoryRegistryShop2"
class="org.springframework.social.connect.support.ConnectionFactoryRegistry">
<property name="connectionFactories">
<list>
<bean
class="org.springframework.social.facebook.connect.FacebookConnectionFactory">
<constructor-arg value="${facebook.apikey.shop2}" />
<constructor-arg value="${facebook.apisecret.shop2}" />
</bean>
</list>
</property>
</bean>
And implement a factory in order to retrieve the proper ConnectionFactoryRegistry related to the "current" shop (identified by the shopKey).
public class ShopsFactoryRegistries
{
private Map<String, ConnectionFactoryRegistry> registries;
public ConnectionFactoryRegistry getRegistryForShopKey(String shopKey)
{
return registries.get(shopKey);
}
//getter and setter for registries
}
With this code you can retrieve the facebookConnectionFactory for the shop1:
String shopKey = "shop1Key";
FacebookConnectionFactory facebookConnectionFactory = (FacebookConnectionFactory) myConnectionFactoryRegistries.getRegistryForShopKey(shopKey).getConnectionFactory(FACEBOOK);

How to validate user using info in headers in spring security

Currently I'm creating the web without login page.
I have another website that will send a header with info:
user:John
userCode:1234567
So my current website will check the content of the headers and validate the user in authentication manager like this:
First I create the AuthenticationEntryPoint so the unauthentication user will go there.In the AuthenticationEntryPoint I create a token and redirect the user to main page,so before its goes to the main page,spring will authenticate the user and give a token for a valid user to use the page. The code is like this:
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
if(authException.getClass().getSimpleName().equals("InsufficientAuthenticationException")) {
if (request.getHeader("user") != null) {
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(request.getHeader("user"), request.getHeader("userCode"));
SecurityContextHolder.getContext().setAuthentication(auth);
response.sendRedirect(request.getContextPath());
}
}
}
In the AuthenticationManager the process will go as usual and give token if the user is valid. Is there anything I need to change or another approach that can be used in spring?
Thanks!
Your case make me think of the Siteminder implementation example, in the reference documentation.
With Siteminder, a header (SM_USER) is passed with the HTTP request.
This is an example for pre-authentication in Spring Security.
Did you try this configuration ?
They begin by defining a "custom-filter" which is an instance of RequestHeaderAuthenticationFilter.
Extract of the documentation :
<security:http>
<!-- Additional http configuration omitted -->
<security:custom-filter position="PRE_AUTH_FILTER" ref="siteminderFilter" />
</security:http>
<bean id="siteminderFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<property name="principalRequestHeader" value="SM_USER"/>
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
<bean id="userDetailsServiceWrapper"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
</property>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="preauthAuthProvider" />
</security:authentication-manager>

outbound-gateway with Basic Authentication in spring-integration

With spring-integration I would like to call an outbound-gateway with an Basic Authentication.
I have something like this :
<int-http:inbound-gateway id="versionRequestGateway"
supported-methods="POST" request-channel="requestVersionChannel"
reply-channel="requestTransformerVersionChannel"
path="/consultersite" reply-timeout="10000" request-payload-type="java.lang.String">
</int-http:inbound-gateway>
<int-http:outbound-gateway order="1" request-channel="requestVersionChannel"
url-expression="#urlExpressionGateway.getUrlFor(payload) + '/consultersite'"
reply-channel="responseVersionChannel"
http-method="POST"
expected-response-type="java.lang.String" >
</int-http:outbound-gateway>
The URL of outbound-gateway is dynamic.
I decide to use rest-template attribute on the outbound-gateway, with this :
<bean id="httpClientParams" class="org.apache.commons.httpclient.params.HttpClientParams">
<property name="authenticationPreemptive" value="true"/>
<property name="connectionManagerClass" value="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager"/>
</bean>
<bean id="httpClient" class="org.apache.commons.httpclient.HttpClient">
<constructor-arg ref="httpClientParams"/>
</bean>
<bean id="httpClientFactory" class="org.springframework.http.client.CommonsClientHttpRequestFactory">
<constructor-arg ref="httpClient"/>
</bean>
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<constructor-arg ref="httpClientFactory"/>
</bean>
It's work when I inject an UsernamePasswordCredentials in an ApplicationListener after spring application context is loaded.
HttpClient client = ctx.getBean("httpClient", HttpClient.class);
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("username", "password");
client.getState().setCredentials(AuthScope.ANY, credentials);
But according the url of outbound-gateway, username and password are different.
How can I do to use the good username/password according the url outbound-gateway ?
It was necessary to implement my own BasicSecureSimpleClientHttpRequestFactory extends SimpleClientHttpRequestFactory to map information Credentials according to the URL of connection. Hope an implementation Spring will be available one day ...
Thanks.
Do not type any Java code you can use a combination of Spring WS HttpComponentsMessageSender and Spring WEB HttpComponentsClientHttpRequestFactory:
<bean id="httpComponentsMessageSender" class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
<property name="credentials">
<bean class="org.apache.commons.httpclient.UsernamePasswordCredentials">
<constructor-arg value="userName"/>
<constructor-arg value="password"/>
</bean>
</property>
</bean>
<bean id="clientHttpRequestFactory" class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<property name="httpClient" value="#{httpComponentsMessageSender.httpClient}"/>
</bean>
<int-http:outbound-gateway url-expression="#urlExpressionGateway.getUrlFor(payload) + '/consultersite'"
request-factory="clientHttpRequestFactory"/>
I can believe, that my answer might not be full for your case.
However I hope it can help a bit.
Maybe there is need to implement your own HttpComponentsClientHttpRequestFactory#createRequest to authenticate at runtime and do this:
method.addRequestHeader(new Header(WWW_AUTH_RESP, authstring, true));
Take a look into source code of HttpMethodDirector#authenticateHost
you should use org.apache.http.auth.UsernamePasswordCredentials implementation instead of org.apache.commons.httpclient.UsernamePasswordCredentials
<beans:bean id="httpComponentsMessageSender" class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
<beans:property name="credentials">
<beans:bean class="org.apache.http.auth.UsernamePasswordCredentials">
<beans:constructor-arg value="user"/>
<beans:constructor-arg value="password"/>
</beans:bean>
</beans:property>
</beans:bean>

How to use separate realms for authentication and authorization with Shiro and CAS?

I'm working on a web application where multiple applications authenticates through a CAS SSO Server. Howerver, each application should maintain their respective roles and these roles are stored in a database specific to the application. So, I need to have 2 realms, one for CAS (for authc) and another for DB (for authz).
This is my current shiro config. I'm getting the redirection to the CAS working properly, but the logged in user (Subject) doesn't seems to have the roles/permission loaded in it (e.g. SecurityUtil.isPermitted() not working as expected)
<bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm">
<property name="name" value="jdbcRealm" />
<property name="dataSource" ref="dataSource" />
<property name="authenticationQuery"
value="SELECT password FROM system_user_accounts WHERE username=? and status=10" />
<property name="userRolesQuery"
value="SELECT role_code FROM system_roles r, system_user_accounts u, system_user_roles ur WHERE u.user_id=ur.user_id AND r.role_id=ur.role_id AND u.username=?" />
<property name="permissionsQuery"
value="SELECT code FROM system_roles r, system_permissions p, system_role_permission rp WHERE r.role_id=rp.role_id AND p.permission_id=rp.permission_id AND r.role_code=?" />
<property name="permissionsLookupEnabled" value="true"></property>
<property name="cachingEnabled" value="true" />
<property name="credentialsMatcher" ref="passwordMatcher" />
</bean>
<!-- For CAS -->
<bean id="casRealm" class="org.apache.shiro.cas.CasRealm">
<property name="defaultRoles" value="ROLE_USER" />
<property name="casServerUrlPrefix" value="http://localhost:7080/auth" />
<property name="casService" value="http://localhost:8080/hawk-hck-web/shiro-cas" />
<property name="validationProtocol" value="SAML" />
<property name="cachingEnabled" value="true"></property>
</bean>
<bean id="casSubjectFactory" class="org.apache.shiro.cas.CasSubjectFactory" />
<!-- Security Manager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realms">
<list>
<ref bean="casRealm" />
<ref bean="jdbcRealm" />
</list>
</property>
<property name="cacheManager" ref="cacheManager"/>
<property name="subjectFactory" ref="casSubjectFactory" />
</bean>
<bean id="casFilter" class="org.apache.shiro.cas.CasFilter">
<property name="failureUrl" value="/error"></property>
</bean>
<!-- Shiro filter -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="http://localhost:7080/auth/login?service=http://localhost:8080/hawk-hck-web/shiro-cas" />
<property name="successUrl" value="/home/index" />
<property name="unauthorizedUrl" value="/error" />
<property name="filters">
<util:map>
<entry key="casFilter" value-ref="casFilter" />
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
<!-- !!! Order matters !!! -->
/shiro-cas = casFilter
/login = anon
/logout = logout
/error = anon
/static/** = anon
/** = authc
</value>
</property>
</bean>
The way I register the realms with the securityManager should be in correct. I can't really find a good example of the setup.
I have 2 questions here:
What is correct setup/configuration to achieve above mentioned scenario?
What is the best practice to manage users and roles across different/seperate applications?
The problem you are running into has to do with the fact that both CasRealm and JdbcRealm extends both AuthorizingRealm (Authorizer) and AuthenticatingRealm. First step I would take is with the JdbcRealm. The JdbcRealm implementation inherits the AuthenticatingRealm#supports(AuthenticationToken token) method implementation. If you extend JdbcRealm and override the "supports" method to return "false" for all token types the JdbcRealm will no longer be used for authentication purposes.
#Override
public boolean supports (AuthenticationToken token) {
return false;
}
The CasRealm is a different story, there is no way (that I know of) to easily tell Shiro to not use a realm that implements Authorizer when checking permissions. I personally find it frustrating that the default implementation for most protocols assumes that both authorization and authentication are needed. I would prefer each to be split into two implementations (eg AuthenticatingCasRealm, AuthorizingCasRealm).
The logic behind checking permissions when multiple realms are in use is documented here. The specific text that references this behavior is:
Step 4: Each configured Realm is checked to see if it implements the
same Authorizer interface. If so, the Realm's own respective hasRole*,
checkRole*, isPermitted*, or checkPermission* method is called.
Based on this, you theoretically could override each of the named methods and all of their overloaded implementations to always return "false".
My solution to this problem is based on my prior comment about splitting each realm into two components, one for authentication and one for authorization. You end up with more duplicate code this way but it is explicit in what behaviors you are expecting from your implementation.
Here's how to go about it:
Create a new class "AuthenticatingCasRealm" that extends org.apache.shiro.realm.AuthenticatingRealm and implements org.apache.shiro.util.Initializable.
Copy and paste the contents of the existing CasRealm source into your new "AuthenticatingCasRealm" class. (I am aware that taking a copy-and-paste route of existing code is often frowned upon however in the described circumstsance I know of no other way of solving the problem.)
Strip out all methods that were implemented for org.apache.shiro.realm.AuthorizingRealm.
Update your Shrio configuration to reference your new AuthenticatingCasRealm implementation.
Based on these changes you should now have two custom implementations in your Shrio config; one of JdbcRealm overriding the "supports" method and one of CasRealm removing the authorization API methods.
There is one additional method based on explicitly declaring an Authorizer via Shiro's configuration that may be better suited to your situation.
Here is an explicit declaration of an Authorizer and Authenticator via a custom ShiroFilter extension. Both were implemented and registered to the provided JNDI names at startup.
public class CustomShiroFilter extends ShiroFilter {
#Override
public void init () throws Exception {
super.init();
DefaultWebSecurityManager dwsm = (DefaultWebSecurityManager) getSecurityManager();
dwsm.setAuthorizer((Authorizer)JndiUtil.get("realms/authorizerRealm"));
dwsm.setAuthenticator((Authenticator)JndiUtil.get("realms/authenticatorRealm"));
}
}
You need only one realm that extends AuthorizingRealm. It will provide
authc: method doGetAuthenticationInfo (CAS server)
authz: method doGetAuthorizationInfo (JDBC)
Hope this helps
We had a similar case where we use a LDAP Realm for authentication and used the standard shiro.ini file for the authorization for a simple use case.
To complement the answer of 'justin.hughey', I give the blueprint (could be spring as well) configuration in order to make your use case working:
<!-- Bean for Authentication -->
<bean id="rccadRealm" class="org.mydomain.myproject.security.shiro.ldap.realm.LdapRealm"
init-method="init">
<property name="searchBase" value="${realm.searchBase}" />
<property name="singleUserFilter" value="${realm.singleUserFilter}" />
<property name="timeout" value="30000" />
<property name="url" value="${contextFactory.url}" />
<property name="systemUsername" value="${contextFactory.systemUsername}" />
<property name="systemPassword" value="${contextFactory.systemPassword}" />
</bean>
<!-- Bean for Authorization -->
<bean id="iniRealm" class="org.mydomain.myproject.security.realm.AuthzOnlyIniRealm">
<argument value="file:$[config.base]/etc/shiro.ini"/>
<property name="authorizationCachingEnabled" value="true" />
</bean>
<bean id="myModularAuthenticator"
class="org.mydomain.myproject.security.service.MyModularRealmAuthenticator">
<property name="realms">
<list>
<ref component-id="ldapRealm" />
</list>
</property>
</bean>
<bean id="mySecurityManager" class="org.apache.shiro.mgt.DefaultSecurityManager">
<property name="authenticator" ref="myModularAuthenticator" />
<property name="authorizer" ref="iniRealm" />
<property name="cacheManager" ref="cacheManager" />
</bean>
The key things is that we needed:
a modularRealmAuthenticator and let the default strategy (as there's only one realm) for the 'authenticator'
a special AuthzOnlyIniRealm which overrides the method supports returning false to prevent using it for authentication.
Our LdapRealm implementation is just an extension of the Shiro ActiveDirectoryRealm.

Resources