You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by be...@apache.org on 2009/02/25 22:15:46 UTC

svn commit: r747927 - in /incubator/shindig/trunk/java/gadgets/src: main/java/org/apache/shindig/gadgets/oauth/ test/java/org/apache/shindig/gadgets/oauth/

Author: beaton
Date: Wed Feb 25 21:15:44 2009
New Revision: 747927

URL: http://svn.apache.org/viewvc?rev=747927&view=rev
Log:
Allow for javascript to do all outbound OAuth configuration.  This will
definitely help with PhotoBucket integration, and it would have made the
Yahoo integration easier as well.

Still experimental, I want to get feedback from a few gadget developers
before I make a spec proposal for opensocial .NEXT.


Modified:
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/GadgetOAuthTokenStore.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthRequest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/OAuthRequestTest.java

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/GadgetOAuthTokenStore.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/GadgetOAuthTokenStore.java?rev=747927&r1=747926&r2=747927&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/GadgetOAuthTokenStore.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/GadgetOAuthTokenStore.java Wed Feb 25 21:15:44 2009
@@ -18,6 +18,7 @@
 package org.apache.shindig.gadgets.oauth;
 
 import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.common.uri.Uri;
 import org.apache.shindig.config.ContainerConfig;
 import org.apache.shindig.gadgets.GadgetContext;
 import org.apache.shindig.gadgets.GadgetException;
@@ -30,6 +31,7 @@
 import org.apache.shindig.gadgets.spec.GadgetSpec;
 import org.apache.shindig.gadgets.spec.OAuthService;
 import org.apache.shindig.gadgets.spec.OAuthSpec;
+import org.apache.shindig.gadgets.spec.SpecParserException;
 import org.apache.shindig.gadgets.spec.OAuthService.Location;
 import org.apache.shindig.gadgets.spec.OAuthService.Method;
 
@@ -85,10 +87,14 @@
 
     AccessorInfoBuilder accessorBuilder = new AccessorInfoBuilder();
 
-    // Does the gadget spec tell us any details about the service provider, like where to put the
-    // OAuth parameters and what methods to use for their URLs?
+    // Pick up any additional information needed about the format of the request, either from
+    // options to makeRequest, or options from the spec, or from sensible defaults.  This is how
+    // we figure out where to put the OAuth parameters and what methods to use for the OAuth
+    // URLs.
     OAuthServiceProvider provider = null;
-    if (arguments.mayUseToken()) {
+    if (arguments.programmaticConfig()) {
+      provider = loadProgrammaticConfig(arguments, accessorBuilder, responseParams);
+    } else if (arguments.mayUseToken()) {
       provider = lookupSpecInfo(securityToken, arguments, accessorBuilder, responseParams);
     } else {
       // This is plain old signed fetch.
@@ -148,6 +154,51 @@
         service.getAuthorizationUrl().toJavaUri().toASCIIString(),
         service.getAccessUrl().url.toJavaUri().toASCIIString());
   }
+  
+  private OAuthServiceProvider loadProgrammaticConfig(OAuthArguments arguments,
+      AccessorInfoBuilder accessorBuilder, OAuthResponseParams responseParams)
+      throws OAuthRequestException {
+    try {
+      String paramLocationStr = arguments.getRequestOption(OAuthArguments.PARAM_LOCATION_PARAM, "");
+      Location l = Location.parse(paramLocationStr);
+      accessorBuilder.setParameterLocation(getStoreLocation(l, responseParams));
+
+      String requestMethod = arguments.getRequestOption(OAuthArguments.REQUEST_METHOD_PARAM, "GET");
+      Method m = Method.parse(requestMethod);
+      accessorBuilder.setMethod(getStoreMethod(m, responseParams));
+      
+      String requestTokenUrl = arguments.getRequestOption(OAuthArguments.REQUEST_TOKEN_URL_PARAM);
+      verifyUrl(requestTokenUrl, responseParams);
+      String accessTokenUrl = arguments.getRequestOption(OAuthArguments.ACCESS_TOKEN_URL_PARAM);
+      verifyUrl(accessTokenUrl, responseParams);
+
+      String authorizationUrl = arguments.getRequestOption(OAuthArguments.AUTHORIZATION_URL_PARAM);
+      verifyUrl(authorizationUrl, responseParams);
+      return new OAuthServiceProvider(requestTokenUrl, authorizationUrl, accessTokenUrl);
+    } catch (SpecParserException e) {
+      // these exceptions have decent programmer readable messages
+      throw responseParams.oauthRequestException(OAuthError.BAD_OAUTH_CONFIGURATION,
+          e.getMessage());
+    }
+  }
+  
+  private void verifyUrl(String url, OAuthResponseParams responseParams)
+      throws OAuthRequestException {
+    if (url == null) {
+      return;
+    }
+    Uri uri;
+    try {
+      uri = Uri.parse(url);
+    } catch (Throwable t) {
+      throw responseParams.oauthRequestException(OAuthError.BAD_OAUTH_CONFIGURATION,
+          "Invalid url: " + url);
+    }
+    if (!uri.isAbsolute()) {
+      throw responseParams.oauthRequestException(OAuthError.BAD_OAUTH_CONFIGURATION,
+          "Invalid url: " + url);
+    }
+  }
 
   /**
    * Figure out the OAuth token that should be used with this request.  We check for this in three

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java?rev=747927&r1=747926&r2=747927&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java Wed Feb 25 21:15:44 2009
@@ -42,6 +42,16 @@
   private static final String BYPASS_SPEC_CACHE_PARAM = "bypassSpecCache";
   private static final String SIGN_OWNER_PARAM = "signOwner";
   private static final String SIGN_VIEWER_PARAM = "signViewer";
+  
+  // Experimental support for configuring OAuth without special parameters in the spec XML.
+  public static final String PROGRAMMATIC_CONFIG_PARAM = "OAUTH_PROGRAMMATIC_CONFIG";
+  public static final String REQUEST_METHOD_PARAM = "OAUTH_REQUEST_METHOD";
+  public static final String PARAM_LOCATION_PARAM = "OAUTH_PARAM_LOCATION";
+  public static final String REQUEST_TOKEN_URL_PARAM = "OAUTH_REQUEST_TOKEN_URL";
+  public static final String ACCESS_TOKEN_URL_PARAM = "OAUTH_ACCESS_TOKEN_URL";
+  public static final String AUTHORIZATION_URL_PARAM = "OAUTH_AUTHORIZATION_URL";
+
+  
 
   /**
    * Should the OAuth access token be used?
@@ -301,4 +311,13 @@
   public String getRequestOption(String name) {
     return requestOptions.get(name);
   }
+  
+  public String getRequestOption(String name, String def) {
+    String val = requestOptions.get(name);
+    return (val != null ? val : def);
+  }
+  
+  public boolean programmaticConfig() {
+    return Boolean.parseBoolean(requestOptions.get(PROGRAMMATIC_CONFIG_PARAM));
+  }
 }

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthRequest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthRequest.java?rev=747927&r1=747926&r2=747927&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthRequest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthRequest.java Wed Feb 25 21:15:44 2009
@@ -320,6 +320,10 @@
 
   private void fetchRequestToken() throws OAuthRequestException, OAuthProtocolException {
     OAuthAccessor accessor = accessorInfo.getAccessor();
+    if (accessor.consumer.serviceProvider.requestTokenURL == null) {
+      throw responseParams.oauthRequestException(OAuthError.BAD_OAUTH_CONFIGURATION,
+          "No request token URL specified");
+    }
     HttpRequest request = new HttpRequest(
         Uri.parse(accessor.consumer.serviceProvider.requestTokenURL));
     request.setMethod(accessorInfo.getHttpMethod().toString());
@@ -574,10 +578,13 @@
   /**
    * Builds the URL the client needs to visit to approve access.
    */
-  private void buildAznUrl() {
-    // At some point we can be clever and use a callback URL to improve
-    // the user experience, but that's too complex for now.
+  private void buildAznUrl() throws OAuthRequestException {
+    // We add the token, gadget is responsible for the callback URL.
     OAuthAccessor accessor = accessorInfo.getAccessor();
+    if (accessor.consumer.serviceProvider.userAuthorizationURL == null) {
+      throw responseParams.oauthRequestException(OAuthError.BAD_OAUTH_CONFIGURATION,
+          "No authorization URL specified");
+    }
     StringBuilder azn = new StringBuilder(
         accessor.consumer.serviceProvider.userAuthorizationURL);
     if (azn.indexOf("?") == -1) {
@@ -619,6 +626,10 @@
       accessorInfo.getAccessor().accessToken = null;
     }
     OAuthAccessor accessor = accessorInfo.getAccessor();
+    if (accessor.consumer.serviceProvider.accessTokenURL == null) {
+      throw responseParams.oauthRequestException(OAuthError.BAD_OAUTH_CONFIGURATION,
+          "No access token URL specified.");
+    }
     Uri accessTokenUri = Uri.parse(accessor.consumer.serviceProvider.accessTokenURL);
     HttpRequest request = new HttpRequest(accessTokenUri);
     request.setMethod(accessorInfo.getHttpMethod().toString());

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/OAuthRequestTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/OAuthRequestTest.java?rev=747927&r1=747926&r2=747927&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/OAuthRequestTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/oauth/OAuthRequestTest.java Wed Feb 25 21:15:44 2009
@@ -31,6 +31,7 @@
 import org.apache.shindig.common.util.FakeTimeSource;
 import org.apache.shindig.gadgets.FakeGadgetSpecFactory;
 import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.GadgetSpecFactory;
 import org.apache.shindig.gadgets.http.HttpResponse;
 import org.apache.shindig.gadgets.oauth.AccessorInfo.OAuthParamLocation;
 import org.apache.shindig.gadgets.oauth.BasicOAuthStoreConsumerKeyAndSecret.KeyType;
@@ -110,7 +111,12 @@
   /**
    * Builds a nicely populated fake token store.
    */
-  public static GadgetOAuthTokenStore getOAuthStore(BasicOAuthStore base) throws GadgetException {
+  public static GadgetOAuthTokenStore getOAuthStore(BasicOAuthStore base) {
+    return getOAuthStore(base, new FakeGadgetSpecFactory());
+  }
+  
+  private static GadgetOAuthTokenStore getOAuthStore(BasicOAuthStore base,
+      GadgetSpecFactory specFactory) {
     if (base == null) {
       base = new BasicOAuthStore();
     }
@@ -121,9 +127,7 @@
     addBadOAuthUrlConsumer(base);
     addApprovalParamsConsumer(base);
     addDefaultKey(base);
-    GadgetOAuthTokenStore store = new GadgetOAuthTokenStore(base, new FakeGadgetSpecFactory());
-    base.initFromConfigString("{}");
-    return store;
+    return new GadgetOAuthTokenStore(base, specFactory);
   }
 
   private static void addValidConsumer(BasicOAuthStore base) {
@@ -316,6 +320,217 @@
   }
 
   @Test
+  public void testOAuthFlow_noSpec() throws Exception {
+    fetcherConfig = new OAuthFetcherConfig(
+        new BasicBlobCrypter("abcdefghijklmnop".getBytes()),
+        getOAuthStore(base, null),
+        clock);
+    
+    MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL);
+    setNoSpecOptions(client);
+
+    HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL);
+    assertEquals("", response.getResponseAsString());
+    client.approveToken("user_data=hello-oauth");
+
+    response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL);
+    assertEquals("User data is hello-oauth", response.getResponseAsString());
+    checkEmptyLog();
+  }
+  
+  private void setNoSpecOptions(MakeRequestClient client) {
+    client.getBaseArgs().setRequestOption(OAuthArguments.PROGRAMMATIC_CONFIG_PARAM, "true");
+    client.getBaseArgs().setRequestOption(OAuthArguments.PARAM_LOCATION_PARAM, "uri-query");
+    client.getBaseArgs().setRequestOption(OAuthArguments.REQUEST_METHOD_PARAM, "GET");
+    client.getBaseArgs().setRequestOption(OAuthArguments.REQUEST_TOKEN_URL_PARAM,
+        FakeOAuthServiceProvider.REQUEST_TOKEN_URL);
+    client.getBaseArgs().setRequestOption(OAuthArguments.ACCESS_TOKEN_URL_PARAM,
+        FakeOAuthServiceProvider.ACCESS_TOKEN_URL);
+    client.getBaseArgs().setRequestOption(OAuthArguments.AUTHORIZATION_URL_PARAM,
+        FakeOAuthServiceProvider.APPROVAL_URL);
+  }
+  
+  @Test
+  public void testOAuthFlow_noSpecNoRequestTokenUrl() throws Exception {
+    fetcherConfig = new OAuthFetcherConfig(
+        new BasicBlobCrypter("abcdefghijklmnop".getBytes()),
+        getOAuthStore(base, null),
+        clock);
+    
+    MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL);
+    setNoSpecOptions(client);
+    client.getBaseArgs().removeRequestOption(OAuthArguments.REQUEST_TOKEN_URL_PARAM);
+
+    HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL);
+    assertEquals("", response.getResponseAsString());
+    assertEquals(403, response.getHttpStatusCode());
+    assertEquals(OAuthError.BAD_OAUTH_CONFIGURATION.toString(),
+        response.getMetadata().get("oauthError"));
+    String errorText = response.getMetadata().get("oauthErrorText");
+    assertNotNull(errorText);
+    checkStringContains("should report no request token url", errorText,
+        "No request token URL specified");
+  }
+  
+  @Test
+  public void testOAuthFlow_noSpecNoAccessTokenUrl() throws Exception {
+    fetcherConfig = new OAuthFetcherConfig(
+        new BasicBlobCrypter("abcdefghijklmnop".getBytes()),
+        getOAuthStore(base, null),
+        clock);
+    
+    MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL);
+    setNoSpecOptions(client);
+    client.getBaseArgs().removeRequestOption(OAuthArguments.ACCESS_TOKEN_URL_PARAM);
+
+    // Get the request token
+    HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL);
+    
+    // try to swap for access token
+    response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL);
+
+    assertEquals("", response.getResponseAsString());
+    assertEquals(403, response.getHttpStatusCode());
+    assertEquals(OAuthError.BAD_OAUTH_CONFIGURATION.toString(),
+        response.getMetadata().get("oauthError"));
+    String errorText = response.getMetadata().get("oauthErrorText");
+    assertNotNull(errorText);
+    checkStringContains("should report no access token url", errorText,
+        "No access token URL specified");
+  }
+  
+  @Test
+  public void testOAuthFlow_noSpecNoApprovalUrl() throws Exception {
+    fetcherConfig = new OAuthFetcherConfig(
+        new BasicBlobCrypter("abcdefghijklmnop".getBytes()),
+        getOAuthStore(base, null),
+        clock);
+    
+    MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL);
+    setNoSpecOptions(client);
+    client.getBaseArgs().removeRequestOption(OAuthArguments.AUTHORIZATION_URL_PARAM);
+
+    HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL);
+
+    assertEquals("", response.getResponseAsString());
+    assertEquals(403, response.getHttpStatusCode());
+    assertEquals(OAuthError.BAD_OAUTH_CONFIGURATION.toString(),
+        response.getMetadata().get("oauthError"));
+    String errorText = response.getMetadata().get("oauthErrorText");
+    assertNotNull(errorText);
+    checkStringContains("should report no authorization url", errorText,
+        "No authorization URL specified");
+  }
+  
+  @Test
+  public void testOAuthFlow_noSpecAuthHeader() throws Exception {
+    serviceProvider.setParamLocation(OAuthParamLocation.AUTH_HEADER);
+    fetcherConfig = new OAuthFetcherConfig(
+        new BasicBlobCrypter("abcdefghijklmnop".getBytes()),
+        getOAuthStore(base, null),
+        clock);
+    
+    MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL);
+    setNoSpecOptions(client);
+    client.getBaseArgs().setRequestOption(OAuthArguments.PARAM_LOCATION_PARAM, "auth-header");
+
+    HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL);
+    assertEquals("", response.getResponseAsString());
+    client.approveToken("user_data=hello-oauth");
+
+    response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL);
+    assertEquals("User data is hello-oauth", response.getResponseAsString());
+    checkEmptyLog();
+  }
+  
+  @Test
+  public void testOAuthFlow_noSpecPostBody() throws Exception {
+    serviceProvider.setParamLocation(OAuthParamLocation.POST_BODY);
+    fetcherConfig = new OAuthFetcherConfig(
+        new BasicBlobCrypter("abcdefghijklmnop".getBytes()),
+        getOAuthStore(base, null),
+        clock);
+    
+    MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL);
+    setNoSpecOptions(client);
+    client.getBaseArgs().setRequestOption(OAuthArguments.REQUEST_METHOD_PARAM, "POST");
+    client.getBaseArgs().setRequestOption(OAuthArguments.PARAM_LOCATION_PARAM, "post-body");
+
+    HttpResponse response = client.sendFormPost(FakeOAuthServiceProvider.RESOURCE_URL, "");
+    assertEquals("", response.getResponseAsString());
+    client.approveToken("user_data=hello-oauth");
+
+    response = client.sendFormPost(FakeOAuthServiceProvider.RESOURCE_URL, "");
+    assertEquals("User data is hello-oauth", response.getResponseAsString());
+    checkEmptyLog();
+  }
+
+  @Test
+  public void testOAuthFlow_noSpecPostBodyAndHeader() throws Exception {
+    serviceProvider.setParamLocation(OAuthParamLocation.POST_BODY);
+    serviceProvider.addParamLocation(OAuthParamLocation.AUTH_HEADER);
+    fetcherConfig = new OAuthFetcherConfig(
+        new BasicBlobCrypter("abcdefghijklmnop".getBytes()),
+        getOAuthStore(base, null),
+        clock);
+    
+    MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL);
+    setNoSpecOptions(client);
+    client.getBaseArgs().setRequestOption(OAuthArguments.REQUEST_METHOD_PARAM, "POST");
+    client.getBaseArgs().setRequestOption(OAuthArguments.PARAM_LOCATION_PARAM, "post-body");
+
+    HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL);
+    assertEquals("", response.getResponseAsString());
+    client.approveToken("user_data=hello-oauth");
+
+    response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL);
+    assertEquals("User data is hello-oauth", response.getResponseAsString());
+    checkEmptyLog();
+  }
+
+  @Test
+  public void testOAuthFlow_noSpecInvalidUrl() throws Exception {
+    fetcherConfig = new OAuthFetcherConfig(
+        new BasicBlobCrypter("abcdefghijklmnop".getBytes()),
+        getOAuthStore(base, null),
+        clock);
+    
+    MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL);
+    setNoSpecOptions(client);
+    client.getBaseArgs().setRequestOption(OAuthArguments.REQUEST_TOKEN_URL_PARAM, "foo");
+
+    HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL);
+    assertEquals("", response.getResponseAsString());
+    assertEquals(403, response.getHttpStatusCode());
+    assertEquals(OAuthError.BAD_OAUTH_CONFIGURATION.toString(),
+        response.getMetadata().get("oauthError"));
+    String errorText = response.getMetadata().get("oauthErrorText");
+    assertNotNull(errorText);
+    checkStringContains("should report invalid url", errorText, "Invalid url: foo");
+  }
+  
+  @Test
+  public void testOAuthFlow_noSpecBlankUrl() throws Exception {
+    fetcherConfig = new OAuthFetcherConfig(
+        new BasicBlobCrypter("abcdefghijklmnop".getBytes()),
+        getOAuthStore(base, null),
+        clock);
+    
+    MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL);
+    setNoSpecOptions(client);
+    client.getBaseArgs().setRequestOption(OAuthArguments.REQUEST_TOKEN_URL_PARAM, "");
+
+    HttpResponse response = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL);
+    assertEquals("", response.getResponseAsString());
+    assertEquals(403, response.getHttpStatusCode());
+    assertEquals(OAuthError.BAD_OAUTH_CONFIGURATION.toString(),
+        response.getMetadata().get("oauthError"));
+    String errorText = response.getMetadata().get("oauthErrorText");
+    assertNotNull(errorText);
+    checkStringContains("should report invalid url", errorText, "Invalid url: ");
+  }
+  
+  @Test
   public void testAccessTokenNotUsedForSocialPage() throws Exception {
     MakeRequestClient client = makeNonSocialClient("owner", "owner", GADGET_URL);
 
@@ -770,6 +985,20 @@
     assertTrue(contains(queryParams, OAuth.OAUTH_CONSUMER_KEY, "signedfetch"));
     assertTrue(contains(queryParams, "xoauth_signature_publickey", "foo"));
   }
+  
+  @Test
+  public void testSignedFetch_authHeader() throws Exception {
+    serviceProvider.setParamLocation(OAuthParamLocation.AUTH_HEADER);
+    MakeRequestClient client = makeSignedFetchClient("o", "v", "http://www.example.com/app");
+    client.getBaseArgs().setRequestOption(OAuthArguments.PROGRAMMATIC_CONFIG_PARAM, "true");
+    client.getBaseArgs().setRequestOption(OAuthArguments.PARAM_LOCATION_PARAM, "auth-header");
+    
+    HttpResponse resp = client.sendGet(FakeOAuthServiceProvider.RESOURCE_URL);
+    String auth = resp.getHeader(FakeOAuthServiceProvider.AUTHZ_ECHO_HEADER);
+    assertNotNull("Should have echoed authz header", auth);
+    checkStringContains("should have opensocial params in header", auth,
+        "opensocial_owner_id=\"o\"");
+  }
 
   @Test
   public void testPostBinaryData() throws Exception {