Announcement Announcement Module
Collapse
No announcement yet.
Scheduled OAuth: No thread-bound request found Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Scheduled OAuth: No thread-bound request found

    Just a heads-up, this project is based on the project I've described from my previous thread which has been resolved already. See
    Spring OAuth2 M6 and Google Analytics: No OAuth 2 Security Context. I now have a new issue.

    Basically, I'm trying to do a scheduled pull of data from Google Analytics (still in the context of OAuth). However, I'm getting an exception regarding scope and proxy:

    Code:
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.org.springframework.security.oauth2.client.context.DefaultOAuth2ClientContext#0': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
    	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:342)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    	at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:33)
    	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:182)
    	at $Proxy123.getAccessToken(Unknown Source)
    	at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:120)
    I've tried declaring an <aop:scoped-proxy/> within my scheduled bean service and even tried the annotation based proxying @Scope(value="session", proxyMode=ScopedProxyMode.INTERFACES). But the errors are still the same:

    Code:
    Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
    	at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
    In my web.xml, I tried adding the following lines:

    Code:
    <listener>
    		<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    	</listener>
    But with no good results. I'm suspecting the scope in the OAuth client is not exposed properly outside the context of MVC.

    I think my problem has some affinity with the following JIRA: Client AccessTokenRequest cannot be created when application context has ConversionService (which is still unresolved).

    For my configuration, please see my previous thread Spring OAuth2 M6 and Google Analytics: No OAuth 2 Security Context. What's new is basically the scheduler config:

    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" 
           	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           	xmlns:context="http://www.springframework.org/schema/context"
    		xmlns:util="http://www.springframework.org/schema/util"
    		xmlns:task="http://www.springframework.org/schema/task"
           	xsi:schemaLocation="
    			http://www.springframework.org/schema/beans 
    			http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    			http://www.springframework.org/schema/context
    			http://www.springframework.org/schema/context/spring-context-3.1.xsd
    			http://www.springframework.org/schema/task
    			http://www.springframework.org/schema/task/spring-task-3.1.xsd">
    
    	<context:property-placeholder properties-ref="deployProperties" />
    
    	<task:scheduler id="taskScheduler" pool-size="1"/>
    
    	<beans profile="web-dev">	
    		<task:scheduled-tasks scheduler="taskScheduler">
    			<task:scheduled ref="scheduledGoogleAnalyticsService" method="stats" cron="0 */1 * * * ?"/>
    		</task:scheduled-tasks>
    	</beans>
    				
    </beans>
    And I have the following scheduled service:

    Code:
    @Service
    @Transactional
    public class ScheduledGoogleAnalyticsService implements AnalyticsSchedulerService {
    
    	@Autowired
    	private AnalyticsService analyticsService;
    	
    	@Override
    	public void stats() {
    		analyticsService.stats();
    	}
    }

  • #2
    If you use a scheduler then there is no current request active in the thread, but that also means that there is no way to obtain an access token by getting the user to follow a flow at Google. If you want to do the scheduling you need to find a way to collect a token and inject that into the OAuth2RestTemplate instead of allowing it to try and discover the token itself, guided by a user in a browser. Maybe you need 2 rest templates, one in a UI with a link that is followed by a user (using <oauth:rest-template/>, and one in a scheduled component that can manage the handover of the token (using a plain bean definition)? Depending on what you need this for you might have issues keeping it thread safe - if more than one user logs into your app then there will be more than one token, so which one would the scheduler use?

    Comment


    • #3
      Dave, thanks for the clarification.

      It seems what I want is too complicated too execute. Gladly my application only needs to maintain a single profile to authenticate with Google Analytics (the app itself).

      One possible solution to my situation is to have a single HTTP entry that returns a certain analytics metrics/dimensions from GA, which will be polled by curl and cron via a shell script. (My requirement is to poll data and email that information).

      Anyway, thanks for the help. You have saved me time from wandering too far :-)

      Comment


      • #4
        I actually tried a different solution than the one I proposed. It's similar with yours.

        I have an OAuth2RestTemplate that does the usual OAuth dance. Then I pull the retrieved access token and store that in memory for the time being.

        Then I have a RestTemplate that does a scheduled Google Analytics metrics/dimensions data pull. Basically, I'm doing a GET request similar to the following as suggested by Google OAuth 2.0 docs:

        Code:
        GET https://www.googleapis.com/oauth2/v1/userinfo?access_token=1/fFBGRNJru1FQd44AzqT3Z
        Code:
        ResponseEntity<String> result = restTemplate.exchange(uri, HttpMethod.GET, requestEntity, String.class, accessToken);
        Is there a way to retrieve this access token directly from the <oauth:client> tag? i.e

        Code:
        <oauth:client id="oauth2AuthenticationClientFilter"/>
        Based on the Spring Security OAuth 2 docs,

        The OAuth2ClientTokenServices interface defines the operations that are necessary to manage OAuth 2.0 tokens for specific users. There is an in-memory implementation provided, but it's likely you'll need to implement your own service for storing the access tokens and associated authentication instances in a persistent database....

        The client element is used to configure the OAuth 2.0 client mechanism. The following attributes can be applied to the client element:

        token-services-ref: The reference to the bean that stores tokens on behalf of a user. Default value is an instance of InMemoryOAuth2ClientTokenServices.

        See https://github.com/SpringSource/spri...docs/oauth2.md
        However, I think the docs are outdated. There is no token-services-ref but on the XSD there's the newer token-cache-ref. And there's no InMemoryOAuth2ClientTokenServices but rather InMemoryTokenStore

        Anyway, I assumed that I don't have to declare those since "there is an in-memory implementation provided" already. But doing an autowire

        Code:
        @Autowired
        	private TokenStore tokenStore;
        throws an exception that the bean cannot be autowired. So I had to plug one:

        Code:
        <oauth:client id="oauth2AuthenticationClientFilter" token-cache-ref="inMemoryTokenStore"/>
        	<beans:bean id="inMemoryTokenStore" class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore"/>

        But on my service, when I try to retrieve the access token:

        Code:
        String accessToken = (String) tokenStore.findTokensByClientId("myGOOGLEclientID").toArray()[0];
        I get a null pointer exception.

        So my question is how to retrieve the access token efficiently? Does it get stored in the first place?

        Comment


        • #5
          The token is stored in the OAuth2RestTemplate. You can grab it from there for your use case, can't you? (The client token services died, but someone else was asking for it to be resurrected and I don't remember a JIRA issue. If you want it back put it in JIRA.)

          Comment


          • #6
            I was able to pull it from OAuth2RestTemplate. I was thinking there was a special service but as you've noted it died.

            The problem with pulling it from OAuth2ResTemplate within a scheduler is the issue I've raised earlier "no thread bound issue". So what I've done is append an access_type=offline in the auth URL. I had to append that parameter manually in the browsers URL since in the oauth:resource there's no way to specify custom parameter.

            The access_type is used for retrieving a refresh token based on Google's doc. Then within the scheduler I have the normal RestTemplate. To get a new access token I have to do a POST to the token URL. And then do a GET. So basically the OAuth2RestTemplate in this scenario was just used to get the auth screen.

            Comment


            • #7
              That's an interesting use case (and a strange twist on the spec). You can add additional parameters to the auth request by pulling the AccessTokenRequest out o f the OAuth2RestTemplate before you send the request (it's just a map). Let us know if that helps.

              To get the token in an other thread you need to extract it from the OAuth2RestTemplate in the session that obtained it, and store it somewhere that you can reference in the scheduled task. Anything that can hold the data in memory would do.

              Comment

              Working...
              X