I'm trying to get the ability to write to someone's Twitter account through Java and Spring Social.
Whenever I request write access through my application on twitter, I get the following exception:
org.springframework.social.NotAuthorizedException: Invalid or expired token
org.springframework.social.twitter.api.impl.TwitterErrorHandler.handleClientErrors(TwitterErrorHandler.java:104)
org.springframework.social.twitter.api.impl.TwitterErrorHandler.handleError(TwitterErrorHandler.java:58)
org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:537)
However, when I turn off access, I don't get this exception, but I (obviously) lose the ability to write. From all the research I have done, I have yet to be able to find anything about spring social except that I need an access token. I cannot find Spring Social documentation that tells me where to get that.
Anyway, this is in my controller:
#Autowired
ConnectionRepository connectionRepository
private Twitter getTwitter() {
connectionRepository.findPrimaryConnection(Twitter.class).api
}
#RequestMapping(value = "/connect/twitter/connect/twitter")
String loggedIn(Model model) {
if (twitter?.authorized) {
model.addAttribute("screenName", twitter.userOperations().screenName)
twitter.timelineOperations().updateStatus("Welcome to Miami #helloWorld")
HOME
}
else {
"redirect:/twitter"
}
}
My ConnectionRepository implementation is a basic one for MongoDB. I don't think it is the issue, but if it is it is nearly identical to:
https://github.com/CarloMicieli/spring-social-mongo/blob/master/src/main/java/org/springframework/social/connect/mongo/MongoConnectionRepository.java
Here is my dispatch xml:
<bean class="org.springframework.social.connect.web.ConnectController">
<property name="applicationUrl" value="http://localhost:8081/MinnesotaCows/" />
</bean>
<bean class="org.springframework.social.connect.web.ProviderSignInController">
<property name="applicationUrl" value="http://localhost:8081/MinnesotaCows/" />
<property name="signUpUrl" value="/register" />
</bean>
<bean id="twitterConnectionFactory"
class="org.springframework.social.twitter.connect.TwitterConnectionFactory">
<constructor-arg value="bUC8VEWgkfkeTXuTBuxCg" />
<constructor-arg value="5I1CNYKBCkNbsbgL2JfTNdSnSA9JVY4KHI4myxV7k4" />
</bean>
<bean id="connectionFactoryLocator"
class="org.springframework.social.connect.support.ConnectionFactoryRegistry">
<property name="connectionFactories">
<list>
<ref bean="twitterConnectionFactory" />
</list>
</property>
</bean>
<bean id="textEncryptor" class="org.springframework.security.crypto.encrypt.Encryptors"
factory-method="noOpText" />
Note: I'm running at localhost. Could the problem be that? Since there is no callback? Also, I'm not using the signUpUrl in the ProviderSignInController for anything. I'm not quite sure what that is for either.
Anyone have any ideas on what I might be doing wrong - or how I can exactly get the access token through the API?
Thanks for your time!
I haven't tested CarloMicieli's mongo based connection repositories, but looking the code I can see that primary connection is the first connection created. This is the same behavior that the built in jdbc based repository in Spring Social has. But I think it's wrong, primary connection should be always the last issued connection/login to the provider (the connection with rank 1 can be expired even though there are newer connections).
You could try to empty the connections collection in your mongo db and test if your code works after that. If the token works then, it is the issue that I described.
I have created my own mongo based social repositories that rank the connections by timestamp and order them so that the last issued connection is the primary one. That way you can issue multiple logins and the last login will be the primary connection.
See reference from github: https://github.com/trautonen/spring-social-mongodb/tree/master/src/main/java/org/eluder/spring/social/mongodb
You also defined ConnectController which map the social provider connection paths. You can initialize login flow by doing POST request to /connect/twitter and if completed without problems should result a working connection in your connections collection in mongodb.
See full reference here: http://docs.spring.io/spring-social/docs/1.1.0.RELEASE/reference/htmlsingle/#creating-connections-with-connectcontroller
Related
I am updating the client of an existing SOAP webservice since the service provider has made some changes to thier security headers in the request.
The requirement is to digitally sign the Timestamp which should be present in the request header and the body should not be digitally signed. I am using XML config to create my SOAP request header and digitally sign the Timestamp.
I am basically using org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor as an interceptor. The problem is that Timestamp gets created in the request header however the SignatureValue and DigestValue tags are empty
I have referred https://docs.spring.io/spring-ws/site/reference/html/security.html#security-wss4j-digital-signatures
Versions:
Spring-ws-core --> 2.0.0.RELEASE
spring-ws-security --> 2.0.0.RELEASE
<bean id="wsClientSecurityInterceptor" class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Timestamp Signature"/>
<property name="securementSignatureKeyIdentifier" value="DirectReference" />
<property name="securementUsername" value="username" />
<property name="securementPassword" value="keystorepassword" />
<property name="securementSignatureCrypto" ref="clientCrypto"/>
<property name="securementSignatureUser" value="username" />
<property name="securementSignatureParts" value="{}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp"/>
</bean>
<bean id="clientCrypto" class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="keystorepassword" />
<property name="keyStoreLocation" value="file:${key.store.location}"/>
<property name="keyStoreType" value="jks" />
<property name="keyStoreProvider" value="IBMJCE" />
</bean>
Though the timestamp gets added to the wsse:Security element in the header, the DigestValue and SignatureValue elements belonging to the xmlns:ds="http://www.w3.org/2000/09/xmldsig#" namespace are always empty
This does not happen if I only sign the Body
I also tried using another Interceptor XwsSecurityInterceptor but that does not work without the Wss4jSecurityInterceptor and gives me the same result when used with Wss4jSecurityInterceptor
<bean id="xwsSecurityInterceptor" class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
<property name="policyConfiguration" value="classpath:securityPolicy.xml"/>
<property name="callbackHandlers">
<list>
<ref bean="keyStoreHandler"/>
</list>
</property>
</bean>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="keyStore" ref="keyStore"/>
<property name="privateKeyPassword" value="keystorepassword"/>
</bean>
<bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="file:${key.store.location}"/>
<property name="password" value="keystorepassword"/>
</bean>
After referring a number of IBM support posts, I have finally come to the solution and have received a QA signoff too on the implementation. I had to replace all my spring mvc config for creating SSL context with configurations on the WAS server itself.You can configure all this using either scripts or using the WAS console manually.
Steps:
Add an entry to ur applications web.xml.Please refer this post https://www.ibm.com/support/knowledgecenter/en/SSAW57_8.5.5/com.ibm.websphere.nd.multiplatform.doc/ae/twbs_jaxwsclientdd.html
The link below will give you a step by step guide as to how to configure the request payload to have a signed timestamp on WAS server.
http://pglezen.github.io/was-config/html/signts.html#sec.signts.keystores.consumer
It basically focusses on :
Policy Set Creation:This will Specify the element or elements to be signed or encrypted in this message part.In our case its the timestamp in the header
Client Policy Set Bindings. If you are the provider you have to refer the provider Policy set bindings section.This involves creation of truststore and keystore required to configure your SSL context. Also has configurations for any proxy settings that you might require.
Finally take a JNDI reference of the service whereever you need to call a particular operation in the service.Refer https://www.ibm.com/support/knowledgecenter/SSAW57_9.0.5/com.ibm.websphere.nd.multiplatform.doc/ae/twbs_jaxwsclientdd.html
Once this done, Simply navigate to Services--> Service Clients and you should have your service refs visible there. You should now attach the Policy set and the bindings created above to the Service client references created in WAS due to point 1 above.You might have to write a script to attach the polict set and binding since after deployment you dont want to do this step manually
Please note: this solution came into picture sicne the version of WAS 8.5.5.16,spring -3.0.5.RELEASE and spring-ws-2.0.0.RELEASE did not support signing of timestamp in the request payload of the SOAP service request. Hope this helps someone !!
I was creating a spring oath2 application. It works. I have a doubt.
Based on this URL http://projects.spring.io/spring-security-oauth/docs/oauth2.html there are only 2 real options:
JdbcTokenStore, JwtTokenStore.
Is it possible to use JDBCTokenStore but not refer to it in the resourceServers?
I mean can we not have it referred directly only in the AuthorizationServer and the resource servers could use an endpoint from AuthorizationServer instead of configuring another direct JDBCTokenStore reference.
Motive: Want to avoid sharing a database between AuthorizationServer and multiple ResourceServers. Is there any other way to achieve this motive.
R
In your Resource Servers you can use RemoteTokenServices. This class queries the /check_token endpoint present in Authorization Server to verify tokens.
You can have a database only for authentication server and another databases for your resource servers.
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.RemoteTokenServices">
<property name="checkTokenEndpointUrl" value="${auth.service.url:https://localhost:8443/auth-service}/oauth/check_token"/>
<property name="clientId" value="${auth.client.name:TEST_API}"/>
<property name="clientSecret" value="${auth.client.secret:password}"/>
<property name="accessTokenConverter" ref="accessTokenConverter"/>
<property name="restTemplate" ref="oauth2RestTemplate"/>
</bean>
I implemented the Spring SAML sample application using ssocircle and it worked fine. Now I have been trying to implement it for the client's ADFS. Following is the configuration I think that is required, please correct me if I am wrong:
Change the first parameter below, to the federationMetadata.xml url provided by client
<bean class="org.opensaml.saml2.metadata.provider.HTTPMetadataProvider">
<constructor-arg>
<value type="java.lang.String">http://idp.ssocircle.com/idp-meta.xml</value>
</constructor-arg>
<constructor-arg>
<value type="int">5000</value>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
Replace the entity id of SP metadata below:
<bean class="org.springframework.security.saml.metadata.MetadataGenerator">
<property name="entityId" value="replaceWithUniqueIdentifier"/>
<property name="extendedMetadata">
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="signMetadata" value="false"/>
<property name="idpDiscoveryEnabled" value="true"/>
</bean>
</property>
</bean>
I haven't been able to figure out the following:
All I have received is a url to adfs/../federationMetadata.xml, who is supposed to create the SP metadata?
Am I supposed to create SP metadata and provide to the client, to add it in adfs? Because, that's what I did using sample application. I added the generated metadata to ssocircle
Is my understanding, that point 1 would be adfs url, and point 2 will be SP entity id, correct?
I would be grateful if you could clarify the above to me, also if possible, point me to straightforward tutorial that helps in integrating SAML with Spring security enabled application as I haven't been able to find the same.
Many thanks
To make SAML between SP and IdP (ADFS) work, you have to mutually exchange metadata.
The ADFS metadata are available on the URL https://adfs-host/FederationMetadata/2007-06/FederationMetadata.xml and you can register them in your SP either with HTTPMetadataProvider, or download them and read them from classpath, or file system with ResourceBackedMetadataProvider.
For SP metadata, you have to configure MetadataGenerator (as you have it in your question) and then expose it via FilterChainProxy. Here is a Java configuration (it's equivalent for XML):
#Bean
public FilterChainProxy samlFilter() throws Exception {
List<SecurityFilterChain> chains = new ArrayList<SecurityFilterChain>();
chains.add(new DefaultSecurityFilterChain(
new AntPathRequestMatcher("/saml/metadata/**"), metadataDisplayFilter()));
return new FilterChainProxy(chains);
}
Than, you can access SP metadata on the URL https://sp-host/saml/metadata and register them on ADFS as a Relying Party Trust. Again, you can do this either via URL, or import data from the (downloaded) file.
Basically, you should be fine if you follow Spring Security SAML Reference Documentation which uses XML configuration. In case, you'll need to switch to Java configuration, you can find handy either referenced vdenotaris/spring-boot-security-saml-sample, or my working prototype sw-samuraj/blog-spring-security.
I have Spring Security configured to authenticate against LDAP server.
<security:authentication-manager >
<security:ldap-authentication-provider user-dn-pattern="uid={0}" />
</security:authentication-manager>
After authentication I want to load roles from local database for the same user. How can I load local database roles using "ldap-authentication-provider"?
If I add the second authentication provider as below:
<security:authentication-manager >
<security:ldap-authentication-provider user-dn-pattern="uid={0}" />
<security:authentication-provider ref="daoAuthenticationProvider" />
</security:authentication-manager>
daoAuthenticationProvider added, but Spring does not use the second provider when first auth provider authenticates the user. Only if the first auth provider fails to authenticate it goes next in the list.
So basically look like we have to customize
<security:ldap-authentication-provider user-dn-pattern="uid={0}" />
to load ROLEs from local database.
Any suggestions? How should this be implemented?
An authentication provider must deliver a fully populated authentication token on successfull authentication, so it's not possible to use one provider to check the user's credentials, and another one to assign authorities (roles) to it.
You can however customize an ldap auth provider to fetch user roles from database instead of the default behaviour (searching for the user's groups in ldap). The LdapAuthenticationProvider has two strategies injected: one that performs the authentication itself (LdapAuthenticator), and another one that fetches the user's authorities (LdapAuthoritiesPopulator). You can achieve your requirements if you supply an LdapAuthoritiesPopulator implementation that loads roles from database. In case you already have a UserDetailsService working against the database, you can easily integrate that by wrapping it in a UserDetailsServiceLdapAuthoritiesPopulator and injecting it in the LdapAuthenticationProvider.
Since this configuration is rather uncommon, the security xml namespace doesn't provide tags/attributes to set it up, but the raw bean config isn't too complicated. Here is the outline:
1) I suppose you have an ldap-server somewhere in your config. It's important to assign and id to it, which will allow us to reference it later.
<security:ldap-server url="..." id="ldapServer" .../>
2) From the authentication-manager section, you will only refer to the customized provider:
<security:authentication-manager>
<security:authentication-provider ref="customLdapAuthProvider"/>
</security:authentication-manager>
3) Now, the essential part:
<bean id="customLdapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<constructor-arg name="authenticator">
<bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
<constructor-arg name="contextSource" ref="ldapServer"/>
<property name="userDnPatterns">
<list>
<value>uid={0}</value>
</list>
</property>
</bean>
</constructor-arg>
<constructor-arg name="authoritiesPopulator">
<bean class="org.springframework.security.ldap.authentication.UserDetailsServiceLdapAuthoritiesPopulator">
<constructor-arg name="userService" ref="userService"/>
</bean>
</constructor-arg>
</bean>
The authenticator is basically the same as the one that would be created by the namespace config. (Note the contextSource attribute referencing the ldap server.)
The authoritiesPopulator is a simple wrapper around your userService implementation which is supposed to be defined somewhere in your config.
I'm using Hibernate with Spring, relevant config:
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="openSessionInViewInterceptor" class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">
<property name="sessionFactory"><ref bean="sessionFactory" /></property>
</bean>
<tx:annotation-driven />
<aop:aspectj-autoproxy />
Think about it...
Some code wants to obtain a Connection from a DataSource. Probably in order to start a transaction and run some SQL query
AbstractRoutingDataSource executes determineCurrentLookupKey() in order to find suitable DataSource from a set of available ones
Lookup key is used to obtain current DataSource. AbstractRoutingDataSource returns JDBC connections from that data source.
Connection is returned from AbstractRoutingDataSource as if it was a normal source.
Now you are asking why determineCurrentLookupKey() is not running within a transaction? First Spring would have to go to point 1. to fetch some database connection required to start a transaction. Look at the next point. See the problem? Smells like infinite recursion to me.
Simply put - determineCurrentLookupKey() can't run within a transaction because transaction needs a connection and the purpose of that method is to determine which DataSource to use to obtain a connection. See also: Chicken or the egg.
Similarly, the engineers couldn't use a computer to design the first computer.