skip to Main Content

I’m creating an application integrating with Shopify’s API, which uses OAuth2 for authentication and authorization. Using the tutorial for Spring Security OAuth2, and the tutorial for Shopify, I’ve been able to get integration working with a single shop. The YAML configuration looks like this:

shopify:
  shop: myshop
  scopes: read_customers,read_orders
security:
  oauth2:
    client:
      clientId: myclientid
      clientSecret: mysecret
      tokenName: access_token
      authenticationScheme: query
      clientAuthenticationScheme: form
      accessTokenUri: https://${shopify.shop}.myshopify.com/admin/oauth/access_token
      userAuthorizationUri: https://${shopify.shop}.myshopify.com/admin/oauth/authorize?scope=${shopify.scopes}&grant_options[]=
      pre-established-redirect-uri: https://myapp/login
      registered-redirect-uri: https://myapp/login
      use-current-uri: false
    resource:
      userInfoUri: https://${shopify.shop}.myshopify.com/admin/shop.json

However, this static configuration won’t work for an app published in Shopify’s App Store because the redirect, access token, user info, and user authorization URIs depend on the shop name. There are examples of using more than one provider, but they still have to be static.

To allow these URI’s to be dynamic, I’ve come up with a few possible options:

  1. Use a parameter in the /login path to identify the shop, then create a filter that adds the shop name to a ThreadLocal that runs before everything else, then dynamically create the AuthorizationCodeResourceDetails that is needed by the OAuth2 filter via a Spring proxied factory bean.

  2. Use a sort of “metafilter” that dynamically recreates the OAuth2ClientAuthenticationProcessingFilter per request along with all of the resources that it needs.

  3. Override OAuth2ClientAuthenticationProcessingFilter so that it can handle recreating the RestTemplate it needs to obtain the access token.

All of these options seem pretty difficult. What’s a good way to handle dynamically-generated URI’s for access tokens and user information in Spring Security OAuth2?

Also, since I’m new to OAuth2 in general, do I need to enable a Resource Server in my Spring configuration to protect my app with the access token?

2

Answers


  1. a bit late but I did return a dynamic url for an oauth resource by overriding the getter for the Oauth2ProtectedResource

        @Bean(name = "googleOauthResource")
    public BaseOAuth2ProtectedResourceDetails getGoogleOauthResource() {
        final AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails() {
            @Override
            public String getPreEstablishedRedirectUri() {
                final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
                if (requestAttributes instanceof ServletRequestAttributes) {
                    final HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
                    return request.getRequestURL() + "?" + request.getQueryString() + "&addStuff";
                }
    
                return super.getPreEstablishedRedirectUri();
            }
        };
        details.setId("google-oauth-client");
        details.setClientId("xxxxxxxxxxx");
        details.setClientSecret("xxxxxxxx");
        details.setAccessTokenUri("https://www.googleapis.com/oauth2/v4/token");
        details.setUserAuthorizationUri("https://accounts.google.com/o/oauth2/v2/auth");
        details.setTokenName("authorization_code");
        details.setScope(Arrays.asList("https://mail.google.com/,https://www.googleapis.com/auth/gmail.modify"));
        details.setPreEstablishedRedirectUri("http://localhost:8080/xxx-api-web/v2/gmail"); //TODO
        details.setUseCurrentUri(false);
        details.setAuthenticationScheme(AuthenticationScheme.query);
        details.setClientAuthenticationScheme(AuthenticationScheme.form);
        details.setGrantType("authorization_code");
        return details;
    }
    
    Login or Signup to reply.
  2. I’m having the same problem you are and I’m leaning toward your first theory of using ThreadLocal storage. Here is how I’ll probably go about my solution:

    Set values from ServletRequest in LocalThread storage by overriding methods in OAuth2ClientAuthenticationProcessingFilter:

    • attemptAuthentication
    • successfulAuthentication
    • unsuccessfulAuthentication
    • requiresAuthentication

    Then translate the URI’s within the OAuth2RestTemplate by overriding the followign methods:

    • createRequest
    • doExecute
    • appendQueryParameter

    I’ll probably have to make my own @Bean for the RestTemplate that has an injected @Service that will lookup the dynamic Shopify domain.

    I’ll post my solution if it works.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search