Error resolving template [twitterConnect], template might not exist or might not be accessible by any of the configured Template Resolvers - spring-social

Ι'm trying to implement ConnectController from Spring Social Twitter. Spring Social Facebook is working fine but when it comes to twitter I get this error error.org.thymeleaf.exceptions.TemplateInputException: Error resolving template [twitterConnect], template might not exist or might not be accessible by any of the configured Template Resolvers
This is my social Configuration class
#Configuration
public class socialConfig {
#Autowired
DataSource dataSource;
#Bean
public ConnectionFactoryLocator addConnectionFactories() {
ConnectionFactoryRegistry registry = new ConnectionFactoryRegistry();
registry.addConnectionFactory(new FacebookConnectionFactory("appid","appsecret"));
registry.addConnectionFactory(new TwitterConnectionFactory("consumerKey","consumerSecret"));
return registry;
}
#Bean
public UsersConnectionRepository getUsersConnectionRepository() {
return new JdbcUsersConnectionRepository(dataSource, addConnectionFactories(), Encryptors.noOpText());
}
#Bean
#Scope(value="request", proxyMode=ScopedProxyMode.INTERFACES)
public ConnectionRepository connectionRepository(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new IllegalStateException("Unable to get a ConnectionRepository: no user signed in");
}
return getUsersConnectionRepository().createConnectionRepository(authentication.getName());
}
#Bean
public ConnectController connectController() {
ConnectController controller = new ConnectController(addConnectionFactories(),
connectionRepository());
controller.setViewPath("");
return controller ;
}

Related

How can I access custom claims added to token using jdbc token store in authorization server in resource server?

I have added custom claims in token using TokenEnhancer, I need some of the custom claims to be available in Principal and/or authentication object.
I am using JdbcTokenStore and not JwtTokenStore.
I have gone through a couple of forum and articles but most talk about JwtTokenStore and not JdbcTokenStore.
public class AuthTokenEnhancer implements TokenEnhancer {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("claim1", "claimVal");
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
Your question has the answer within it.
JWT is used to represent claims between two parties securely. Without JWT, there is no question of claims.
That means how can you add a claim into a normal token and expect it to be read by the resource server. This token enhancer will provide the additional information to the client and won't be stored into any database and hence there is no way for the resource server to know it.
The simplest solution for your case is to use JWT. But if it is a sensitive information, maybe you should store it within the database and expose via an API which will check for the authority of the user accessing it.
Basically you could:
At the AuthorizationServer: Provide your custom TokenEnhancer
implementation and configure it via AuthorizationServerConfigurer
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenEnhancer(tokenEnhancer());
}
#Bean
public TokenEnhancer tokenEnhancer() {
return new TestTokenEnhancer();
}
}
At the ResourceServer: Extend DefaultUserAuthenticationConverter and override extractAuthentication in which you can read the custom claim from the Map and add it to the Authentication (or your own extension of it).
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Autowired
RemoteTokenServices tokenServices;
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenServices(testTokenServices());
}
private ResourceServerTokenServices testTokenServices() {
tokenServices.setAccessTokenConverter(testAccessTokenConverter());
return tokenServices;
}
private AccessTokenConverter testAccessTokenConverter() {
DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
accessTokenConverter.setUserTokenConverter(testUserTokenConverter());
return accessTokenConverter;
}
/**
* Retrieve additional claims that were set in the TokenEnhancer of the Authorization server during Oauth token creation.
*/
private UserAuthenticationConverter testUserTokenConverter() {
return new DefaultUserAuthenticationConverter() {
#Override
public Authentication extractAuthentication(Map<String, ?> map) {
Authentication authentication = super.extractAuthentication(map);
if (authentication != null) {
authentication = new TestAuthenticationToken(authentication.getPrincipal(),
authentication.getCredentials(), authentication.getAuthorities(),
(String) map.get("testKey"));
}
return authentication;
}
};
}
}
This thread contains related solutions.
Don't forget to delete the token from the database (table oauth_access_token) during development iterations! Otherwise you could get an "old" (not expired) token not reflecting your current custom claims.

Spring boot Oauth2 : Token relay from a client using Feign, Ribbon, Zull and Eureka to a ressource

I have an oauth2 client that get a token from an authorization server successfully. (not always has been the case but now it is... :))
The client, the zuul gateway and the resource server are all registered in Eureka.
My client use a Proxy to access to a remote ressource service named microservice-files.
#RestController
#FeignClient(name = "zuul-server")
#RibbonClient(name = "microservice-files")
public interface ProxyMicroserviceFiles {
#GetMapping(value = "microservice-files/root")
FileBean getUserRoot();
}
So I'd like to relay the token to Zull and then to the resource server.
I can relay the token this way to contact Zuul and apparently the load balancing is managed too (I've just test I didn't know and it's great) also zuul can relay the token, but it's not very convenient I'd prefer the previous approach.
#EnableConfigurationProperties
#SpringBootApplication
#EnableFeignClients("com.clientui")
public class ClientUiApplication {
#Bean
public OAuth2RestOperations restOperations(
OAuth2ProtectedResourceDetails resource,
OAuth2ClientContext context) {
return new OAuth2RestTemplate(resource, context);
}
public static void main(String[] args) {
SpringApplication.run(ClientUiApplication.class, args);
}
}
here is the test controler
#Controller
public class ClientController {
#Autowired
private RestOperations restOperations;
#RequestMapping("/root")
public ResponseEntity userRootTest() {
String rootUrl = "http://localhost:9004/microservice-files/root";
return restOperations.getForEntity(rootUrl,FileBean.class);
}
}
If I correctly understand your problem then you can use a RequestInterceptor to add a token in each request by the feign. In order to do it you can use the next configuration:
#Bean
public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oauth2ClientContext,
OAuth2ProtectedResourceDetails resource) {
return new OAuth2FeignRequestInterceptor(oauth2ClientContext, resource);
}
#Bean
protected OAuth2ProtectedResourceDetails resource() {
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
resource.setAccessTokenUri("http://127.0.0.1:9000/auth/login");
resource.setUserAuthorizationUri("http://127.0.0.1:9000/auth/authorize");
resource.setClientId("my-client");
resource.setClientSecret("my-secret");
return resource;
}
This is what I did to make it work.
#Bean(name = "oauth2RestTemplate")
#LoadBalanced
public OAuth2RestTemplate restTemplate(SpringClientFactory clientFactory) {
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails());
RibbonLoadBalancerClient ribbonLoadBalancerClient = new RibbonLoadBalancerClient(clientFactory);
LoadBalancerInterceptor loadBalancerInterceptor = new LoadBalancerInterceptor(ribbonLoadBalancerClient);
ClientCredentialsAccessTokenProvider accessTokenProvider = new ClientCredentialsAccessTokenProvider();
accessTokenProvider.setInterceptors(Arrays.asList(loadBalancerInterceptor));
restTemplate.setAccessTokenProvider(accessTokenProvider);
return restTemplate;
}
public ClientCredentialsResourceDetails resourceDetails() {
ClientCredentialsResourceDetails clientCredentialsResourceDetails = new ClientCredentialsResourceDetails();
clientCredentialsResourceDetails.setId("1");
clientCredentialsResourceDetails.setClientId("my-ms");
clientCredentialsResourceDetails.setClientSecret("123");
clientCredentialsResourceDetails.setAccessTokenUri("http://oauth-server/oauth/token");
clientCredentialsResourceDetails.setScope(Arrays.asList("read"));
clientCredentialsResourceDetails.setGrantType("client_credentials");
return clientCredentialsResourceDetails;
}

Twitter Sign in using Spring Boot

I am trying to sign in to my web application (developed using Spring Boot) using social logins. The logins for Google & facebook are okay. But the for some reason there is a token issue in the twitter login. I have created the project in the twitter developer site obtained all the credentials. Please refer to my code below.
My Property file values are mentioned below.
twitter.client.client-id=XXXXXXX
twitter.client.client-secret=XXXXXXXX
twitter.client.access-token-uri=https://api.twitter.com/oauth/access_token
twitter.client.user-authorization-uri=https://api.twitter.com/oauth/authorize
twitter.client.token-name=oauth_token
twitter.client.authentication-scheme=form
twitter.resource.user-info-uri=https://api.twitter.com/1.1/account/verify_credentials.json
The filter method
private Filter ssoTwitterFilter(String processingUrl, PrincipalExtractor principalExtractor) {
OAuth2ClientAuthenticationProcessingFilter twitterFilter = new OAuth2ClientAuthenticationProcessingFilter(
processingUrl);
LOGGER.debug("processingUrl :{} ", processingUrl);
twitterFilter.setAuthenticationSuccessHandler(authenticationSuccessHandlerAndRegistrationFilter());
OAuth2RestTemplate twitterTemplate = new OAuth2RestTemplate(twitter(), oauth2ClientContext);
twitterFilter.setRestTemplate(twitterTemplate);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(twitterResource().getUserInfoUri(),
twitter().getClientId());
tokenServices.setRestTemplate(twitterTemplate);
tokenServices.setPrincipalExtractor(principalExtractor);
return twitterFilter;
}
These are the bean configurations.
#Bean
#ConfigurationProperties("twitter.client")
public AuthorizationCodeResourceDetails twitter() {
return new AuthorizationCodeResourceDetails();
}
#Bean
#ConfigurationProperties("twitter.resource")
public ResourceServerProperties twitterResource() {
return new ResourceServerProperties();
}
This is the error that I get
enter image description here
Please can anyone shed some light on this. Because all the samples I found were related getting profile information from twitter where as i need a sample for sign in using spring Boot. Thanks in advance
You can configure Twitter login like this:
#Configuration
#EnableSocial
public class SocialConfig implements SocialConfigurer {
#Autowired
private UserAuthorizationService userAuthorizationService;
#Override
public void addConnectionFactories(ConnectionFactoryConfigurer cfConfig, Environment env) {
cfConfig.addConnectionFactory(new TwitterConnectionFactory(
env.getProperty("twitter.consumer-key"),
env.getProperty("twitter.consumer-secret")
));
}
#Override
public UserIdSource getUserIdSource() {
return new UserIdSource() {
#Override
public String getUserId() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new IllegalStateException("Unable to get a ConnectionRepository: no user signed in");
}
return authentication.getName();
}
};
}
#Override
public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
InMemoryUsersConnectionRepository usersConnectionRepository = new InMemoryUsersConnectionRepository(
connectionFactoryLocator
);
return usersConnectionRepository;
}
#Autowired
private TwitterConnectionSignup twitterConnectionSignup;
#Autowired
private ConnectionFactoryLocator connectionFactoryLocator;
#Autowired
private UsersConnectionRepository usersConnectionRepository;
#Bean
public ProviderSignInController providerSignInController() {
((InMemoryUsersConnectionRepository) usersConnectionRepository)
.setConnectionSignUp(twitterConnectionSignup);
return new ProviderSignInController(
connectionFactoryLocator,
usersConnectionRepository,
new TwitterSignInAdapter(userAuthorizationService));
}
}
Configure TwitterConnectionSignup:
#Service
public class TwitterConnectionSignup implements ConnectionSignUp {
#Autowired
private UserRepo userRepo;
#Override
public String execute(Connection<?> connection) {
//add your logic to save user to your db
return connection.getDisplayName();
}
}
Now configure TwitterSignInAdapter:
public class TwitterSignInAdapter implements SignInAdapter {
private UserAuthorizationService userAuthorizationService;
public TwitterSignInAdapter(UserAuthorizationService userAuthorizationService) {
this.userAuthorizationService = userAuthorizationService;
}
#Override
public String signIn(String localUserId, Connection<?> connection, NativeWebRequest webRequest) {
log.debug(" Email {}", localUserId);
UserAuthDto userAuthDto = (UserAuthDto) userAuthorizationService.loadUserByUsername(localUserId);
UsernamePasswordAuthenticationToken updatedAuth = new UsernamePasswordAuthenticationToken(userAuthDto, userAuthDto.getSocialId(),
userAuthDto.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(updatedAuth);
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
// add authentication to the session
servletRequest.getSession().setAttribute(
HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
SecurityContextHolder.getContext());
return "/";
}
}

spring boot callbackUrl ConnectSupport

I want to overload callbackUrl in ConnectSupport
I use Spring boot Connect : org.springframework.social:spring-social-core:jar:1.1.0.RELEASE:compile
#Bean
public ConnectController connectController(
ConnectionFactoryLocator connectionFactoryLocator,
ConnectionRepository connectionRepository) {
ConnectController controller = new ConnectController(connectionFactoryLocator,
connectionRepository);
controller.set callbackUrl ??
return controller;
}
Spring Social Api is available here. You need to call the setApplicationUrl().
#Bean
public ConnectController connectController(ConnectionFactoryLocator connectionFactoryLocator,
ConnectionRepository connectionRepository) {
ConnectController controller = new ConnectController(connectionFactoryLocator,
connectionRepository);
String url = "www.foo.com";
controller.setApplicationUrl(url) ;
return controller;
}
At this point (NullPointerException problem) one way to solve the problem is to extend ConnectController (and ProviderSignInController which suffers the same issue) and fix the offending portion of the code yourself. Just adding the classes into project works. Is it elegant? Well...
/*******************************************************************/
//RedirectedConnectController.java
public class RedirectedConnectController extends ConnectController {
#Value("${application.url}")
private String appUrl;
public RedirectedConnectController(ConnectionFactoryLocator connectionFactoryLocator,
ConnectionRepository connectionRepository) {
super(connectionFactoryLocator, connectionRepository);
}
/*
* This is the method, which should be called BEFORE setApplicationUrl
* but obviously is not.
*/
#Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
setApplicationUrl(appUrl);
}
}
/*******************************************************************/
//RedirectedSignInController.java
#Controller
public class RedirectedSignInController extends ProviderSignInController {
#Value("${application.url}")
private String appUrl;
#Inject
public RedirectedSignInController(ConnectionFactoryLocator connectionFactoryLocator,
UsersConnectionRepository usersConnectionRepository,
SignInAdapter signInAdapter) {
super(connectionFactoryLocator, usersConnectionRepository, signInAdapter);
}
#Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
setApplicationUrl(appUrl);
}
}

Creating cron job to fetching twitter posts from user's wall using spring-social

I'm trying to create a cron job, which should fetch logged user's tweets.
I'm trying to do it like this:
public class MessagesSaver {
private static final Logger logger = LoggerFactory
.getLogger(MessagesSaver.class);
private static String TWITTER_NETWORK = "twitter";
private static String FACEBOOK_NETWORK = "facebook";
private static int MAX_TWEET_COUNT_PER_PAGE = 50;
#Autowired
private MessageRepository messageRepository;
#Autowired
private Twitter twitter;
#Scheduled(cron = "0 30 * * * *")
public void getMessagesFromSocialNetworks() {
if (twitter != null) {
List<Tweet> tweets = twitter.timelineOperations().getUserTimeline(
MAX_TWEET_COUNT_PER_PAGE);
parseAndSaveTwitterPosts(tweets);
}
}
}
and SocialConfig :
#Configuration
#EnableSocial
public class SocialConfig implements SocialConfigurer {
#Inject
private DataSource dataSource;
//
// SocialConfigurer implementation methods
//
#Override
public void addConnectionFactories(ConnectionFactoryConfigurer cfConfig,
Environment env) {
cfConfig.addConnectionFactory(new TwitterConnectionFactory(env
.getProperty("twitter.consumerKey"), env
.getProperty("twitter.consumerSecret")));
}
#Override
public UserIdSource getUserIdSource() {
return new UserIdSource() {
#Override
public String getUserId() {
return "admin";
}
};
}
#Override
public UsersConnectionRepository getUsersConnectionRepository(
ConnectionFactoryLocator connectionFactoryLocator) {
return new JdbcUsersConnectionRepository(dataSource,
connectionFactoryLocator, Encryptors.noOpText());
}
#Bean
#Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
public Twitter twitter(ConnectionRepository repository) {
Connection<Twitter> connection = repository
.findPrimaryConnection(Twitter.class);
return connection != null ? connection.getApi() : null;
}
//
// Web Controller and Filter Beans
//
#Bean
public ConnectController connectController(
ConnectionFactoryLocator connectionFactoryLocator,
ConnectionRepository connectionRepository) {
ConnectController connectController = new ConnectController(
connectionFactoryLocator, connectionRepository);
return connectController;
}
#Bean
public ProviderSignInController providerSignInController(
ConnectionFactoryLocator connectionFactoryLocator,
UsersConnectionRepository usersConnectionRepository) {
return new ProviderSignInController(connectionFactoryLocator,
usersConnectionRepository, new SimpleSignInAdapter(
new HttpSessionRequestCache()));
}
#Bean
public DisconnectController disconnectController(
UsersConnectionRepository usersConnectionRepository, Environment env) {
return new DisconnectController(usersConnectionRepository,
env.getProperty("facebook.clientSecret"));
}
#Bean
public ReconnectFilter apiExceptionHandler(
UsersConnectionRepository usersConnectionRepository,
UserIdSource userIdSource) {
return new ReconnectFilter(usersConnectionRepository, userIdSource);
}
}
But of course it doesn't work, because twitter connection live in the request scope(
How i can configure this job?
The logged in user is associated with a web session, so there's no problem obtaining a request-scoped Twitter object at the web level of your app.
However, scheduled/cron jobs operate independent of the web layer of your application. They have no concept of "logged in user", as they are simply background beans, similar to services or DAOs in that they have no concept of who the current user is. From the perspective of MessagesSaver, who is the logged in user? There could easily be many logged in users. Which one is it expected to work on behalf of?
You could inject a UsersConnectionRepository into MessagesSaver, use it to obtain a ConnectionRepository for a specific user, then use that to obtain the Twitter connection (and from that, the Twitter API binding). The tricky bit is (again) who is the logged in user? There could be many...which one is MessagesSaver supposed to use?
Stepping away from what you've written here, what is it that your trying to accomplish? Maybe there's another way to do what you need.

Resources