You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by na...@apache.org on 2015/04/15 21:41:32 UTC

[27/39] jclouds git commit: Decomplicate OAuth a little.

Decomplicate OAuth a little.


Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/35156560
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/35156560
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/35156560

Branch: refs/heads/master
Commit: 35156560dc52a975edb20ac2135bf04757823696
Parents: cd8aeed
Author: Adrian Cole <ac...@twitter.com>
Authored: Thu Oct 30 21:05:23 2014 -0700
Committer: Adrian Cole <ad...@apache.org>
Committed: Fri Oct 31 08:50:50 2014 -0700

----------------------------------------------------------------------
 .../java/org/jclouds/oauth/v2/OAuthApi.java     |  7 +-
 .../oauth/v2/binders/OAuthTokenBinder.java      | 76 +++++++++++++++++
 .../oauth/v2/domain/TokenRequestFormat.java     | 45 ----------
 .../v2/filters/BearerTokenAuthenticator.java    | 20 ++---
 .../oauth/v2/filters/OAuthAuthenticator.java    | 26 +++---
 .../oauth/v2/functions/BuildTokenRequest.java   | 34 ++++----
 .../jclouds/oauth/v2/functions/FetchToken.java  | 17 ++--
 .../v2/functions/OAuthCredentialsSupplier.java  | 69 ++++++++-------
 .../v2/functions/SignOrProduceMacForToken.java  | 51 ++++++-----
 .../oauth/v2/handlers/OAuthErrorHandler.java    | 16 ++--
 .../oauth/v2/handlers/OAuthTokenBinder.java     | 45 ----------
 .../v2/internal/SubtypeAdapterFactory.java      | 42 ---------
 .../oauth/v2/json/JWTTokenRequestFormat.java    | 89 --------------------
 .../oauth/v2/binders/OAuthTokenBinderTest.java  | 69 +++++++++++++++
 .../v2/json/JWTTokenRequestFormatTest.java      | 70 ---------------
 15 files changed, 253 insertions(+), 423 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/35156560/apis/oauth/src/main/java/org/jclouds/oauth/v2/OAuthApi.java
----------------------------------------------------------------------
diff --git a/apis/oauth/src/main/java/org/jclouds/oauth/v2/OAuthApi.java b/apis/oauth/src/main/java/org/jclouds/oauth/v2/OAuthApi.java
index 4a12d8d..d3082fc 100644
--- a/apis/oauth/src/main/java/org/jclouds/oauth/v2/OAuthApi.java
+++ b/apis/oauth/src/main/java/org/jclouds/oauth/v2/OAuthApi.java
@@ -16,17 +16,18 @@
  */
 package org.jclouds.oauth.v2;
 
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
 import java.io.Closeable;
 
 import javax.inject.Named;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.POST;
-import javax.ws.rs.core.MediaType;
 
+import org.jclouds.oauth.v2.binders.OAuthTokenBinder;
 import org.jclouds.oauth.v2.config.Authentication;
 import org.jclouds.oauth.v2.domain.Token;
 import org.jclouds.oauth.v2.domain.TokenRequest;
-import org.jclouds.oauth.v2.handlers.OAuthTokenBinder;
 import org.jclouds.rest.AuthorizationException;
 import org.jclouds.rest.annotations.BinderParam;
 import org.jclouds.rest.annotations.Endpoint;
@@ -55,6 +56,6 @@ public interface OAuthApi extends Closeable {
     */
    @Named("authenticate")
    @POST
-   @Consumes(MediaType.APPLICATION_JSON)
+   @Consumes(APPLICATION_JSON)
    Token authenticate(@BinderParam(OAuthTokenBinder.class) TokenRequest tokenRequest) throws AuthorizationException;
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/35156560/apis/oauth/src/main/java/org/jclouds/oauth/v2/binders/OAuthTokenBinder.java
----------------------------------------------------------------------
diff --git a/apis/oauth/src/main/java/org/jclouds/oauth/v2/binders/OAuthTokenBinder.java b/apis/oauth/src/main/java/org/jclouds/oauth/v2/binders/OAuthTokenBinder.java
new file mode 100644
index 0000000..e62d9c7
--- /dev/null
+++ b/apis/oauth/src/main/java/org/jclouds/oauth/v2/binders/OAuthTokenBinder.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.oauth.v2.binders;
+
+import static com.google.common.base.Charsets.UTF_8;
+import static com.google.common.base.Joiner.on;
+import static com.google.common.io.BaseEncoding.base64Url;
+import static org.jclouds.io.Payloads.newUrlEncodedFormPayload;
+
+import javax.inject.Inject;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.io.Payload;
+import org.jclouds.json.Json;
+import org.jclouds.oauth.v2.domain.TokenRequest;
+import org.jclouds.rest.Binder;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMultimap;
+
+/**
+ * Formats a token request into JWT format namely:
+ * <ol>
+ * <li>Transforms the token request to json.</li>
+ * <li>Creates the base64 header.claimset portions of the payload.</li>
+ * <li>Uses the provided signer function to create a signature.</li>
+ * <li>Creates the full url encoded payload as described in: <a href="https://developers.google.com/accounts/docs/OAuth2ServiceAccount">OAuth2ServiceAccount</a></li>
+ * </ol>
+ */
+public final class OAuthTokenBinder implements Binder {
+   private static final String ASSERTION_FORM_PARAM = "assertion";
+   private static final String GRANT_TYPE_FORM_PARAM = "grant_type";
+   private static final String GRANT_TYPE_JWT_BEARER = "urn:ietf:params:oauth:grant-type:jwt-bearer";
+
+   private final Function<byte[], byte[]> signer;
+   private final Json json;
+
+   @Inject OAuthTokenBinder(Function<byte[], byte[]> signer, Json json) {
+      this.signer = signer;
+      this.json = json;
+   }
+
+   @Override public <R extends HttpRequest> R bindToRequest(R request, Object input) {
+      TokenRequest tokenRequest = (TokenRequest) input;
+      String encodedHeader = json.toJson(tokenRequest.header());
+      String encodedClaimSet = json.toJson(tokenRequest.claimSet());
+
+      encodedHeader = base64Url().omitPadding().encode(encodedHeader.getBytes(UTF_8));
+      encodedClaimSet = base64Url().omitPadding().encode(encodedClaimSet.getBytes(UTF_8));
+
+      byte[] signature = signer.apply(on(".").join(encodedHeader, encodedClaimSet).getBytes(UTF_8));
+      String encodedSignature = signature != null ?  base64Url().omitPadding().encode(signature) : "";
+
+      // the final assertion in base 64 encoded {header}.{claimSet}.{signature} format
+      String assertion = on(".").join(encodedHeader, encodedClaimSet, encodedSignature);
+      Payload payload = newUrlEncodedFormPayload(ImmutableMultimap.<String, String> builder()
+            .put(GRANT_TYPE_FORM_PARAM, GRANT_TYPE_JWT_BEARER)
+            .put(ASSERTION_FORM_PARAM, assertion).build());
+
+      return (R) request.toBuilder().payload(payload).build();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/35156560/apis/oauth/src/main/java/org/jclouds/oauth/v2/domain/TokenRequestFormat.java
----------------------------------------------------------------------
diff --git a/apis/oauth/src/main/java/org/jclouds/oauth/v2/domain/TokenRequestFormat.java b/apis/oauth/src/main/java/org/jclouds/oauth/v2/domain/TokenRequestFormat.java
deleted file mode 100644
index 7099592..0000000
--- a/apis/oauth/src/main/java/org/jclouds/oauth/v2/domain/TokenRequestFormat.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jclouds.oauth.v2.domain;
-
-import com.google.inject.ImplementedBy;
-import org.jclouds.http.HttpRequest;
-import org.jclouds.oauth.v2.json.JWTTokenRequestFormat;
-
-import java.util.Set;
-
-/**
- * Transforms a TokenRequest into a specific format (e.g. JWT token)
- */
-@ImplementedBy(JWTTokenRequestFormat.class)
-public interface TokenRequestFormat {
-
-   /**
-    * Transforms the provided HttpRequest into a particular token request with a specific format.
-    */
-   <R extends HttpRequest> R formatRequest(R httpRequest, TokenRequest tokenRequest);
-
-   /**
-    * The name of the type of the token request, e.g., "JWT"
-    */
-   String type();
-
-   /**
-    * The claims that must be present in the token request for it to be valid.
-    */
-   Set<String> requiredClaims();
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/35156560/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/BearerTokenAuthenticator.java
----------------------------------------------------------------------
diff --git a/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/BearerTokenAuthenticator.java b/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/BearerTokenAuthenticator.java
index 779ba44..5ff8c50 100644
--- a/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/BearerTokenAuthenticator.java
+++ b/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/BearerTokenAuthenticator.java
@@ -16,26 +16,24 @@
  */
 package org.jclouds.oauth.v2.filters;
 
-import com.google.common.base.Supplier;
+import static java.lang.String.format;
+
+import javax.inject.Inject;
+
 import org.jclouds.http.HttpException;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.oauth.v2.domain.OAuthCredentials;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import com.google.common.base.Supplier;
 
-@Singleton
-public class BearerTokenAuthenticator implements OAuthAuthenticationFilter {
+public final class BearerTokenAuthenticator implements OAuthAuthenticationFilter {
    private final Supplier<OAuthCredentials> creds;
 
-   @Inject
-   BearerTokenAuthenticator(final Supplier<OAuthCredentials> creds) {
+   @Inject BearerTokenAuthenticator(Supplier<OAuthCredentials> creds) {
       this.creds = creds;
    }
 
-   @Override
-   public HttpRequest filter(HttpRequest request) throws HttpException {
-      return request.toBuilder().addHeader("Authorization", String.format("%s %s",
-            "Bearer ", creds.get().credential)).build();
+   @Override public HttpRequest filter(HttpRequest request) throws HttpException {
+      return request.toBuilder().addHeader("Authorization", format("%s %s", "Bearer ", creds.get().credential)).build();
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/35156560/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/OAuthAuthenticator.java
----------------------------------------------------------------------
diff --git a/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/OAuthAuthenticator.java b/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/OAuthAuthenticator.java
index 40fc0cf..28a2c15 100644
--- a/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/OAuthAuthenticator.java
+++ b/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/OAuthAuthenticator.java
@@ -16,47 +16,41 @@
  */
 package org.jclouds.oauth.v2.filters;
 
-import com.google.common.base.Function;
-import com.google.common.cache.LoadingCache;
+import static com.google.common.base.Preconditions.checkState;
+
+import javax.inject.Inject;
+
 import org.jclouds.http.HttpException;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.oauth.v2.domain.Token;
 import org.jclouds.oauth.v2.domain.TokenRequest;
 import org.jclouds.rest.internal.GeneratedHttpRequest;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-import static com.google.common.base.Preconditions.checkState;
+import com.google.common.base.Function;
+import com.google.common.cache.LoadingCache;
 
 /**
  * To be used by client applications to embed an OAuth authentication in their REST requests.
  * <p/>
  * TODO when we're able to use the OAuthAuthentication an this should be used automatically
  */
-@Singleton
-public class OAuthAuthenticator implements OAuthAuthenticationFilter {
+public final class OAuthAuthenticator implements OAuthAuthenticationFilter {
 
    private Function<GeneratedHttpRequest, TokenRequest> tokenRequestBuilder;
    private Function<TokenRequest, Token> tokenFetcher;
 
-   @Inject
-   OAuthAuthenticator(Function<GeneratedHttpRequest, TokenRequest> tokenRequestBuilder, LoadingCache<TokenRequest,
-           Token> tokenFetcher) {
+   @Inject OAuthAuthenticator(Function<GeneratedHttpRequest, TokenRequest> tokenRequestBuilder,
+         LoadingCache<TokenRequest, Token> tokenFetcher) {
       this.tokenRequestBuilder = tokenRequestBuilder;
       this.tokenFetcher = tokenFetcher;
    }
 
-   @Override
-   public HttpRequest filter(HttpRequest request) throws HttpException {
+   @Override public HttpRequest filter(HttpRequest request) throws HttpException {
       checkState(request instanceof GeneratedHttpRequest, "request must be an instance of GeneratedHttpRequest");
       GeneratedHttpRequest generatedHttpRequest = GeneratedHttpRequest.class.cast(request);
       TokenRequest tokenRequest = tokenRequestBuilder.apply(generatedHttpRequest);
       Token token = tokenFetcher.apply(tokenRequest);
       return request.toBuilder().addHeader("Authorization", String.format("%s %s",
               token.tokenType(), token.accessToken())).build();
-
    }
-
-
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/35156560/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/BuildTokenRequest.java
----------------------------------------------------------------------
diff --git a/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/BuildTokenRequest.java b/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/BuildTokenRequest.java
index 664dcab..bcce004 100644
--- a/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/BuildTokenRequest.java
+++ b/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/BuildTokenRequest.java
@@ -24,22 +24,21 @@ import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGOR
 
 import java.util.Collections;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 
-import javax.inject.Singleton;
-
 import org.jclouds.Constants;
 import org.jclouds.oauth.v2.config.OAuthScopes;
 import org.jclouds.oauth.v2.domain.ClaimSet;
 import org.jclouds.oauth.v2.domain.Header;
 import org.jclouds.oauth.v2.domain.OAuthCredentials;
 import org.jclouds.oauth.v2.domain.TokenRequest;
-import org.jclouds.oauth.v2.domain.TokenRequestFormat;
 import org.jclouds.rest.internal.GeneratedHttpRequest;
 
 import com.google.common.base.Function;
 import com.google.common.base.Joiner;
 import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
 import com.google.common.reflect.Invokable;
 import com.google.inject.Inject;
 import com.google.inject.name.Named;
@@ -51,50 +50,47 @@ import com.google.inject.name.Named;
  * <p/>
  * TODO scopes etc should come from the REST method and not from a global property
  */
-@Singleton
-public class BuildTokenRequest implements Function<GeneratedHttpRequest, TokenRequest> {
+public final class BuildTokenRequest implements Function<GeneratedHttpRequest, TokenRequest> {
+   // exp and ist (expiration and emission times) are assumed mandatory already
+   private static final List<String> REQUIRED_CLAIMS = ImmutableList.of("iss", "scope", "aud");
 
    private final String assertionTargetDescription;
    private final String signatureAlgorithm;
-   private final TokenRequestFormat tokenRequestFormat;
    private final Supplier<OAuthCredentials> credentialsSupplier;
    private final long tokenDuration;
 
    @Inject(optional = true)
    @Named(ADDITIONAL_CLAIMS)
-   protected Map<String, String> additionalClaims = Collections.emptyMap();
+   private Map<String, String> additionalClaims = Collections.emptyMap();
 
    @Inject(optional = true)
    @Named(SCOPES)
-   protected String globalScopes = null;
+   private String globalScopes = null;
 
    // injectable so expect tests can override with a predictable value
    @Inject(optional = true)
-   protected Supplier<Long> timeSourceMillisSinceEpoch = new Supplier<Long>() {
+   private Supplier<Long> timeSourceMillisSinceEpoch = new Supplier<Long>() {
       @Override
       public Long get() {
          return System.currentTimeMillis();
       }
    };
-   
-   @Inject
-   public BuildTokenRequest(@Named(AUDIENCE) String assertionTargetDescription,
+
+   @Inject BuildTokenRequest(@Named(AUDIENCE) String assertionTargetDescription,
                             @Named(SIGNATURE_OR_MAC_ALGORITHM) String signatureAlgorithm,
-                            TokenRequestFormat tokenRequestFormat, Supplier<OAuthCredentials> credentialsSupplier,
+                            Supplier<OAuthCredentials> credentialsSupplier,
                             @Named(Constants.PROPERTY_SESSION_INTERVAL) long tokenDuration) {
       this.assertionTargetDescription = assertionTargetDescription;
       this.signatureAlgorithm = signatureAlgorithm;
-      this.tokenRequestFormat = tokenRequestFormat;
       this.credentialsSupplier = credentialsSupplier;
       this.tokenDuration = tokenDuration;
    }
 
-   @Override
-   public TokenRequest apply(GeneratedHttpRequest request) {
+   @Override public TokenRequest apply(GeneratedHttpRequest request) {
       long now = timeSourceMillisSinceEpoch.get() / 1000;
 
       // fetch the token
-      Header header = Header.create(signatureAlgorithm, tokenRequestFormat.type());
+      Header header = Header.create(signatureAlgorithm, "JWT");
 
       Map<String, String> claims = new LinkedHashMap<String, String>();
       claims.put("iss", credentialsSupplier.get().identity);
@@ -102,7 +98,7 @@ public class BuildTokenRequest implements Function<GeneratedHttpRequest, TokenRe
       claims.put("aud", assertionTargetDescription);
       claims.putAll(additionalClaims);
 
-      checkState(claims.keySet().containsAll(tokenRequestFormat.requiredClaims()),
+      checkState(claims.keySet().containsAll(REQUIRED_CLAIMS),
             "not all required claims were present");
 
       ClaimSet claimSet = ClaimSet.create(now, now + tokenDuration, Collections.unmodifiableMap(claims));
@@ -110,7 +106,7 @@ public class BuildTokenRequest implements Function<GeneratedHttpRequest, TokenRe
       return TokenRequest.create(header, claimSet);
    }
 
-   protected String getOAuthScopes(GeneratedHttpRequest request) {
+   private String getOAuthScopes(GeneratedHttpRequest request) {
       Invokable<?, ?> invokable = request.getInvocation().getInvokable();
       
       OAuthScopes classScopes = invokable.getOwnerType().getRawType().getAnnotation(OAuthScopes.class);

http://git-wip-us.apache.org/repos/asf/jclouds/blob/35156560/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/FetchToken.java
----------------------------------------------------------------------
diff --git a/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/FetchToken.java b/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/FetchToken.java
index 593c885..1a02cab 100644
--- a/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/FetchToken.java
+++ b/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/FetchToken.java
@@ -16,26 +16,23 @@
  */
 package org.jclouds.oauth.v2.functions;
 
-import com.google.common.base.Function;
+import javax.inject.Inject;
+
 import org.jclouds.oauth.v2.OAuthApi;
 import org.jclouds.oauth.v2.domain.Token;
 import org.jclouds.oauth.v2.domain.TokenRequest;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import com.google.common.base.Function;
 
-@Singleton
-public class FetchToken implements Function<TokenRequest, Token> {
+public final class FetchToken implements Function<TokenRequest, Token> {
 
-   private OAuthApi oAuthApi;
+   private final OAuthApi oAuthApi;
 
-   @Inject
-   public FetchToken(OAuthApi oAuthApi) {
+   @Inject FetchToken(OAuthApi oAuthApi) {
       this.oAuthApi = oAuthApi;
    }
 
-   @Override
-   public Token apply(TokenRequest input) {
+   @Override public Token apply(TokenRequest input) {
       return this.oAuthApi.authenticate(input);
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/35156560/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/OAuthCredentialsSupplier.java
----------------------------------------------------------------------
diff --git a/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/OAuthCredentialsSupplier.java b/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/OAuthCredentialsSupplier.java
index 45620c0..cd7e3d6 100644
--- a/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/OAuthCredentialsSupplier.java
+++ b/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/OAuthCredentialsSupplier.java
@@ -16,27 +16,6 @@
  */
 package org.jclouds.oauth.v2.functions;
 
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Charsets;
-import com.google.common.base.Supplier;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import com.google.common.io.ByteSource;
-import com.google.common.util.concurrent.UncheckedExecutionException;
-import org.jclouds.domain.Credentials;
-import org.jclouds.location.Provider;
-import org.jclouds.oauth.v2.domain.OAuthCredentials;
-import org.jclouds.rest.AuthorizationException;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.PrivateKey;
-
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Throwables.propagate;
@@ -47,20 +26,42 @@ import static org.jclouds.oauth.v2.OAuthConstants.OAUTH_ALGORITHM_NAMES_TO_KEYFA
 import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGORITHM;
 import static org.jclouds.util.Throwables2.getFirstThrowableOfType;
 
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.domain.Credentials;
+import org.jclouds.location.Provider;
+import org.jclouds.oauth.v2.domain.OAuthCredentials;
+import org.jclouds.rest.AuthorizationException;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Charsets;
+import com.google.common.base.Supplier;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.io.ByteSource;
+import com.google.common.util.concurrent.UncheckedExecutionException;
+
 /**
  * Loads {@link OAuthCredentials} from a pem private key using the KeyFactory obtained from the JWT Algorithm
  * Name<->KeyFactory name mapping in OAuthConstants. The pem pk algorithm must match the KeyFactory algorithm.
  *
  * @see org.jclouds.oauth.v2.OAuthConstants#OAUTH_ALGORITHM_NAMES_TO_KEYFACTORY_ALGORITHM_NAMES
  */
-@Singleton
-public class OAuthCredentialsSupplier implements Supplier<OAuthCredentials> {
+@Singleton // due to cache
+public final class OAuthCredentialsSupplier implements Supplier<OAuthCredentials> {
 
    private final Supplier<Credentials> creds;
    private final LoadingCache<Credentials, OAuthCredentials> keyCache;
 
-   @Inject
-   public OAuthCredentialsSupplier(@Provider Supplier<Credentials> creds, OAuthCredentialsForCredentials loader,
+   @Inject OAuthCredentialsSupplier(@Provider Supplier<Credentials> creds, OAuthCredentialsForCredentials loader,
                                    @Named(SIGNATURE_OR_MAC_ALGORITHM) String signatureOrMacAlgorithm) {
       this.creds = creds;
       checkArgument(OAUTH_ALGORITHM_NAMES_TO_KEYFACTORY_ALGORITHM_NAMES.containsKey(signatureOrMacAlgorithm),
@@ -74,17 +75,15 @@ public class OAuthCredentialsSupplier implements Supplier<OAuthCredentials> {
     * so that the private key is only recalculated once.
     */
    @VisibleForTesting
-   static class OAuthCredentialsForCredentials extends CacheLoader<Credentials, OAuthCredentials> {
+   static final class OAuthCredentialsForCredentials extends CacheLoader<Credentials, OAuthCredentials> {
       private final String keyFactoryAlgorithm;
 
-      @Inject
-      public OAuthCredentialsForCredentials(@Named(SIGNATURE_OR_MAC_ALGORITHM) String signatureOrMacAlgorithm) {
+      @Inject OAuthCredentialsForCredentials(@Named(SIGNATURE_OR_MAC_ALGORITHM) String signatureOrMacAlgorithm) {
          this.keyFactoryAlgorithm = OAUTH_ALGORITHM_NAMES_TO_KEYFACTORY_ALGORITHM_NAMES.get(checkNotNull(
                  signatureOrMacAlgorithm, "signatureOrMacAlgorithm"));
       }
 
-      @Override
-      public OAuthCredentials load(Credentials in) {
+      @Override public OAuthCredentials load(Credentials in) {
          try {
             String identity = in.identity;
             String privateKeyInPemFormat = in.credential;
@@ -108,18 +107,16 @@ public class OAuthCredentialsSupplier implements Supplier<OAuthCredentials> {
       }
    }
 
-   @Override
-   public OAuthCredentials get() {
+   @Override public OAuthCredentials get() {
       try {
          // loader always throws UncheckedExecutionException so no point in using get()
          return keyCache.getUnchecked(checkNotNull(creds.get(), "credential supplier returned null"));
       } catch (UncheckedExecutionException e) {
-         Throwable authorizationException = getFirstThrowableOfType(e, AuthorizationException.class);
+         AuthorizationException authorizationException = getFirstThrowableOfType(e, AuthorizationException.class);
          if (authorizationException != null) {
-            throw (AuthorizationException) authorizationException;
+            throw authorizationException;
          }
-         throw propagate(e);
+         throw e;
       }
    }
-
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/35156560/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/SignOrProduceMacForToken.java
----------------------------------------------------------------------
diff --git a/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/SignOrProduceMacForToken.java b/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/SignOrProduceMacForToken.java
index 471812a..647fcfa 100644
--- a/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/SignOrProduceMacForToken.java
+++ b/apis/oauth/src/main/java/org/jclouds/oauth/v2/functions/SignOrProduceMacForToken.java
@@ -16,40 +16,42 @@
  */
 package org.jclouds.oauth.v2.functions;
 
-import com.google.common.base.Function;
-import com.google.common.base.Supplier;
-import com.google.common.base.Throwables;
-import org.jclouds.oauth.v2.domain.OAuthCredentials;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Throwables.propagate;
+import static java.lang.String.format;
+import static org.jclouds.oauth.v2.OAuthConstants.NO_ALGORITHM;
+import static org.jclouds.oauth.v2.OAuthConstants.OAUTH_ALGORITHM_NAMES_TO_SIGNATURE_ALGORITHM_NAMES;
+import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGORITHM;
 
-import javax.annotation.PostConstruct;
-import javax.crypto.Mac;
-import javax.inject.Inject;
-import javax.inject.Named;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.Signature;
 import java.security.SignatureException;
 
-import static com.google.common.base.Preconditions.checkState;
-import static java.lang.String.format;
-import static org.jclouds.oauth.v2.OAuthConstants.NO_ALGORITHM;
-import static org.jclouds.oauth.v2.OAuthConstants.OAUTH_ALGORITHM_NAMES_TO_SIGNATURE_ALGORITHM_NAMES;
-import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGORITHM;
+import javax.annotation.PostConstruct;
+import javax.crypto.Mac;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.jclouds.oauth.v2.domain.OAuthCredentials;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.inject.Singleton;
 
 /**
  * Function that signs/produces mac's for  OAuth tokens, provided a {@link Signature} or a {@link Mac} algorithm and
  * {@link PrivateKey}
  */
-public class SignOrProduceMacForToken implements Function<byte[], byte[]> {
+@Singleton // due to signatureOrMacFunction
+public final class SignOrProduceMacForToken implements Function<byte[], byte[]> {
 
    private final Supplier<OAuthCredentials> credentials;
    private final String signatureOrMacAlgorithm;
    private Function<byte[], byte[]> signatureOrMacFunction;
 
-
-   @Inject
-   public SignOrProduceMacForToken(@Named(SIGNATURE_OR_MAC_ALGORITHM) String signatureOrMacAlgorithm,
+   @Inject SignOrProduceMacForToken(@Named(SIGNATURE_OR_MAC_ALGORITHM) String signatureOrMacAlgorithm,
                                    Supplier<OAuthCredentials> credentials) {
       checkState(OAUTH_ALGORITHM_NAMES_TO_SIGNATURE_ALGORITHM_NAMES.containsKey(signatureOrMacAlgorithm),
               format("the signature algorithm %s is not supported", signatureOrMacAlgorithm));
@@ -74,14 +76,13 @@ public class SignOrProduceMacForToken implements Function<byte[], byte[]> {
       }
    }
 
-   @Override
-   public byte[] apply(byte[] input) {
+   @Override public byte[] apply(byte[] input) {
       return signatureOrMacFunction.apply(input);
    }
 
    private static class MessageAuthenticationCodeGenerator implements Function<byte[], byte[]> {
 
-      private Mac mac;
+      private final Mac mac;
 
       private MessageAuthenticationCodeGenerator(String macAlgorithm, PrivateKey privateKey) throws
               NoSuchAlgorithmException, InvalidKeyException {
@@ -89,8 +90,7 @@ public class SignOrProduceMacForToken implements Function<byte[], byte[]> {
          this.mac.init(privateKey);
       }
 
-      @Override
-      public byte[] apply(byte[] input) {
+      @Override public byte[] apply(byte[] input) {
          this.mac.update(input);
          return this.mac.doFinal();
       }
@@ -98,7 +98,7 @@ public class SignOrProduceMacForToken implements Function<byte[], byte[]> {
 
    private static class SignatureGenerator implements Function<byte[], byte[]> {
 
-      private Signature signature;
+      private final Signature signature;
 
       private SignatureGenerator(String signatureAlgorithm, PrivateKey privateKey) throws NoSuchAlgorithmException,
               InvalidKeyException {
@@ -106,13 +106,12 @@ public class SignOrProduceMacForToken implements Function<byte[], byte[]> {
          this.signature.initSign(privateKey);
       }
 
-      @Override
-      public byte[] apply(byte[] input) {
+      @Override public byte[] apply(byte[] input) {
          try {
             signature.update(input);
             return signature.sign();
          } catch (SignatureException e) {
-            throw Throwables.propagate(e);
+            throw propagate(e);
          }
       }
    }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/35156560/apis/oauth/src/main/java/org/jclouds/oauth/v2/handlers/OAuthErrorHandler.java
----------------------------------------------------------------------
diff --git a/apis/oauth/src/main/java/org/jclouds/oauth/v2/handlers/OAuthErrorHandler.java b/apis/oauth/src/main/java/org/jclouds/oauth/v2/handlers/OAuthErrorHandler.java
index 78d7844..ddc88a8 100644
--- a/apis/oauth/src/main/java/org/jclouds/oauth/v2/handlers/OAuthErrorHandler.java
+++ b/apis/oauth/src/main/java/org/jclouds/oauth/v2/handlers/OAuthErrorHandler.java
@@ -16,6 +16,9 @@
  */
 package org.jclouds.oauth.v2.handlers;
 
+import static javax.ws.rs.core.Response.Status;
+import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
+
 import org.jclouds.http.HttpCommand;
 import org.jclouds.http.HttpErrorHandler;
 import org.jclouds.http.HttpResponse;
@@ -23,17 +26,8 @@ import org.jclouds.http.HttpResponseException;
 import org.jclouds.rest.AuthorizationException;
 import org.jclouds.rest.ResourceNotFoundException;
 
-import javax.inject.Singleton;
-
-import static javax.ws.rs.core.Response.Status;
-import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
-
-/**
- * This will parse and set an appropriate exception on the command object.
- */
-@Singleton
-public class OAuthErrorHandler implements HttpErrorHandler {
-   public void handleError(HttpCommand command, HttpResponse response) {
+public final class OAuthErrorHandler implements HttpErrorHandler {
+   @Override public void handleError(HttpCommand command, HttpResponse response) {
       // it is important to always read fully and close streams
       byte[] data = closeClientButKeepContentStream(response);
       String message = data != null ? new String(data) : null;

http://git-wip-us.apache.org/repos/asf/jclouds/blob/35156560/apis/oauth/src/main/java/org/jclouds/oauth/v2/handlers/OAuthTokenBinder.java
----------------------------------------------------------------------
diff --git a/apis/oauth/src/main/java/org/jclouds/oauth/v2/handlers/OAuthTokenBinder.java b/apis/oauth/src/main/java/org/jclouds/oauth/v2/handlers/OAuthTokenBinder.java
deleted file mode 100644
index 1030804..0000000
--- a/apis/oauth/src/main/java/org/jclouds/oauth/v2/handlers/OAuthTokenBinder.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jclouds.oauth.v2.handlers;
-
-import org.jclouds.http.HttpRequest;
-import org.jclouds.oauth.v2.domain.TokenRequest;
-import org.jclouds.oauth.v2.domain.TokenRequestFormat;
-import org.jclouds.rest.Binder;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/**
- * Generic implementation of a token binder. Uses a provided {@link TokenRequestFormat} to actually bind tokens to
- * requests.
- */
-@Singleton
-public class OAuthTokenBinder implements Binder {
-
-   private final TokenRequestFormat tokenRequestFormat;
-
-   @Inject
-   OAuthTokenBinder(TokenRequestFormat tokenRequestFormat) {
-      this.tokenRequestFormat = tokenRequestFormat;
-   }
-
-   @Override
-   public <R extends HttpRequest> R bindToRequest(R request, Object input) {
-      return tokenRequestFormat.formatRequest(request, (TokenRequest) input);
-   }
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/35156560/apis/oauth/src/main/java/org/jclouds/oauth/v2/internal/SubtypeAdapterFactory.java
----------------------------------------------------------------------
diff --git a/apis/oauth/src/main/java/org/jclouds/oauth/v2/internal/SubtypeAdapterFactory.java b/apis/oauth/src/main/java/org/jclouds/oauth/v2/internal/SubtypeAdapterFactory.java
deleted file mode 100644
index f09bea6..0000000
--- a/apis/oauth/src/main/java/org/jclouds/oauth/v2/internal/SubtypeAdapterFactory.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jclouds.oauth.v2.internal;
-
-import com.google.gson.Gson;
-import com.google.gson.TypeAdapter;
-import com.google.gson.TypeAdapterFactory;
-import com.google.gson.reflect.TypeToken;
-
-/**
- * Type adapter used to serialize all subtypes of a value. This can be used to force serialization for an {@link
- * com.google.auto.value.AutoValue} generated class.
- */
-public abstract class SubtypeAdapterFactory<T> extends TypeAdapter<T> implements TypeAdapterFactory {
-   private final Class<T> baseClass;
-
-   protected SubtypeAdapterFactory(Class<T> baseClass) {
-      this.baseClass = baseClass;
-   }
-
-   /** Accepts duty for any subtype. When using AutoValue properly, this will be the generated form. */
-   @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
-      if (!(baseClass.isAssignableFrom(typeToken.getRawType()))) {
-         return null;
-      }
-      return (TypeAdapter<T>) this;
-   }
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/35156560/apis/oauth/src/main/java/org/jclouds/oauth/v2/json/JWTTokenRequestFormat.java
----------------------------------------------------------------------
diff --git a/apis/oauth/src/main/java/org/jclouds/oauth/v2/json/JWTTokenRequestFormat.java b/apis/oauth/src/main/java/org/jclouds/oauth/v2/json/JWTTokenRequestFormat.java
deleted file mode 100644
index 41d9804..0000000
--- a/apis/oauth/src/main/java/org/jclouds/oauth/v2/json/JWTTokenRequestFormat.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jclouds.oauth.v2.json;
-
-import static com.google.common.base.Charsets.UTF_8;
-import static com.google.common.base.Joiner.on;
-import static com.google.common.io.BaseEncoding.base64Url;
-import static org.jclouds.io.Payloads.newUrlEncodedFormPayload;
-
-import java.util.Set;
-
-import javax.inject.Inject;
-
-import org.jclouds.http.HttpRequest;
-import org.jclouds.io.Payload;
-import org.jclouds.json.Json;
-import org.jclouds.oauth.v2.domain.TokenRequest;
-import org.jclouds.oauth.v2.domain.TokenRequestFormat;
-
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.ImmutableSet;
-
-/**
- * Formats a token request into JWT format namely:
- * - transforms the token request to json
- * - creates the base64 header.claimset portions of the payload.
- * - uses the provided signer function to create a signature
- * - creates the full url encoded payload as described in:
- * https://developers.google.com/accounts/docs/OAuth2ServiceAccount
- * <p/>
- */
-public class JWTTokenRequestFormat implements TokenRequestFormat {
-
-   private static final String ASSERTION_FORM_PARAM = "assertion";
-   private static final String GRANT_TYPE_FORM_PARAM = "grant_type";
-   private static final String GRANT_TYPE_JWT_BEARER = "urn:ietf:params:oauth:grant-type:jwt-bearer";
-
-   private final Function<byte[], byte[]> signer;
-   private final Json json;
-
-   @Inject JWTTokenRequestFormat(Function<byte[], byte[]> signer, Json json) {
-      this.signer = signer;
-      this.json = json;
-   }
-
-   @Override public <R extends HttpRequest> R formatRequest(R request, TokenRequest tokenRequest) {
-
-      String encodedHeader = json.toJson(tokenRequest.header());
-      String encodedClaimSet = json.toJson(tokenRequest.claimSet());
-
-      encodedHeader = base64Url().omitPadding().encode(encodedHeader.getBytes(UTF_8));
-      encodedClaimSet = base64Url().omitPadding().encode(encodedClaimSet.getBytes(UTF_8));
-
-      byte[] signature = signer.apply(on(".").join(encodedHeader, encodedClaimSet).getBytes(UTF_8));
-      String encodedSignature = signature != null ?  base64Url().omitPadding().encode(signature) : "";
-
-      // the final assertion in base 64 encoded {header}.{claimSet}.{signature} format
-      String assertion = on(".").join(encodedHeader, encodedClaimSet, encodedSignature);
-      Payload payload = newUrlEncodedFormPayload(ImmutableMultimap.<String, String> builder()
-                           .put(GRANT_TYPE_FORM_PARAM, GRANT_TYPE_JWT_BEARER)
-                           .put(ASSERTION_FORM_PARAM, assertion).build());
-
-      return (R) request.toBuilder().payload(payload).build();
-   }
-
-   @Override public String type() {
-      return "JWT";
-   }
-
-   @Override public Set<String> requiredClaims() {
-      // exp and ist (expiration and emission times) are assumed mandatory already
-      return ImmutableSet.of("iss", "scope", "aud");
-   }
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/35156560/apis/oauth/src/test/java/org/jclouds/oauth/v2/binders/OAuthTokenBinderTest.java
----------------------------------------------------------------------
diff --git a/apis/oauth/src/test/java/org/jclouds/oauth/v2/binders/OAuthTokenBinderTest.java b/apis/oauth/src/test/java/org/jclouds/oauth/v2/binders/OAuthTokenBinderTest.java
new file mode 100644
index 0000000..b73d49b
--- /dev/null
+++ b/apis/oauth/src/test/java/org/jclouds/oauth/v2/binders/OAuthTokenBinderTest.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.oauth.v2.binders;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertSame;
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.jclouds.ContextBuilder;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.oauth.v2.OAuthApiMetadata;
+import org.jclouds.oauth.v2.OAuthTestUtils;
+import org.jclouds.oauth.v2.domain.ClaimSet;
+import org.jclouds.oauth.v2.domain.Header;
+import org.jclouds.oauth.v2.domain.TokenRequest;
+import org.jclouds.util.Strings2;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+@Test(groups = "unit", testName = "OAuthTokenBinderTest")
+public class OAuthTokenBinderTest {
+   public static final String STRING_THAT_GENERATES_URL_UNSAFE_BASE64_ENCODING = "§1234567890'+±!\"#$%&/()" +
+         "=?*qwertyuiopº´WERTYUIOPªàsdfghjklç~ASDFGHJKLÇ^<zxcvbnm," +
+         ".->ZXCVBNM;:_@€";
+
+   public void testPayloadIsUrlSafe() throws IOException {
+
+      OAuthTokenBinder tokenRequestFormat = ContextBuilder.newBuilder(new OAuthApiMetadata()).overrides
+              (OAuthTestUtils.defaultProperties(null)).build().utils()
+              .injector().getInstance(OAuthTokenBinder.class);
+      Header header = Header.create("a", "b");
+      ClaimSet claimSet = ClaimSet.create(0, 0,
+            ImmutableMap.of("ist", STRING_THAT_GENERATES_URL_UNSAFE_BASE64_ENCODING));
+      TokenRequest tokenRequest = TokenRequest.create(header, claimSet);
+      HttpRequest request = tokenRequestFormat.bindToRequest(
+            HttpRequest.builder().method("GET").endpoint("http://localhost").build(), tokenRequest);
+
+      assertNotNull(request.getPayload());
+
+      String payload = Strings2.toStringAndClose(request.getPayload().getInput());
+
+      // make sure the paylod is in the format {header}.{claims}.{signature}
+      Iterable<String> parts = Splitter.on(".").split(payload);
+
+      assertSame(Iterables.size(parts), 3);
+
+      assertTrue(!payload.contains("+"));
+      assertTrue(!payload.contains("/"));
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/35156560/apis/oauth/src/test/java/org/jclouds/oauth/v2/json/JWTTokenRequestFormatTest.java
----------------------------------------------------------------------
diff --git a/apis/oauth/src/test/java/org/jclouds/oauth/v2/json/JWTTokenRequestFormatTest.java b/apis/oauth/src/test/java/org/jclouds/oauth/v2/json/JWTTokenRequestFormatTest.java
deleted file mode 100644
index bc82809..0000000
--- a/apis/oauth/src/test/java/org/jclouds/oauth/v2/json/JWTTokenRequestFormatTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jclouds.oauth.v2.json;
-
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertSame;
-import static org.testng.Assert.assertTrue;
-
-import java.io.IOException;
-
-import org.jclouds.ContextBuilder;
-import org.jclouds.http.HttpRequest;
-import org.jclouds.oauth.v2.OAuthApiMetadata;
-import org.jclouds.oauth.v2.OAuthTestUtils;
-import org.jclouds.oauth.v2.domain.ClaimSet;
-import org.jclouds.oauth.v2.domain.Header;
-import org.jclouds.oauth.v2.domain.TokenRequest;
-import org.jclouds.oauth.v2.domain.TokenRequestFormat;
-import org.jclouds.util.Strings2;
-import org.testng.annotations.Test;
-
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-
-@Test(groups = "unit")
-public class JWTTokenRequestFormatTest {
-   public static final String STRING_THAT_GENERATES_URL_UNSAFE_BASE64_ENCODING = "§1234567890'+±!\"#$%&/()" +
-         "=?*qwertyuiopº´WERTYUIOPªàsdfghjklç~ASDFGHJKLÇ^<zxcvbnm," +
-         ".->ZXCVBNM;:_@€";
-
-   public void testPayloadIsUrlSafe() throws IOException {
-
-      TokenRequestFormat tokenRequestFormat = ContextBuilder.newBuilder(new OAuthApiMetadata()).overrides
-              (OAuthTestUtils.defaultProperties(null)).build().utils()
-              .injector().getInstance(TokenRequestFormat.class);
-      Header header = Header.create("a", "b");
-      ClaimSet claimSet = ClaimSet.create(0, 0,
-            ImmutableMap.of("ist", STRING_THAT_GENERATES_URL_UNSAFE_BASE64_ENCODING));
-      TokenRequest tokenRequest = TokenRequest.create(header, claimSet);
-      HttpRequest request = tokenRequestFormat.formatRequest(HttpRequest.builder().method("GET").endpoint
-              ("http://localhost").build(), tokenRequest);
-
-      assertNotNull(request.getPayload());
-
-      String payload = Strings2.toStringAndClose(request.getPayload().getInput());
-
-      // make sure the paylod is in the format {header}.{claims}.{signature}
-      Iterable<String> parts = Splitter.on(".").split(payload);
-
-      assertSame(Iterables.size(parts), 3);
-
-      assertTrue(!payload.contains("+"));
-      assertTrue(!payload.contains("/"));
-   }
-}