entry : map.entrySet()) {
+ params.add(new Parameter(entry.getKey(), entry.getValue()));
+ }
}
}
@@ -41,9 +43,9 @@ public String appendTo(String url) {
if (queryString.equals(EMPTY_STRING)) {
return url;
} else {
- url += url.indexOf(QUERY_STRING_SEPARATOR) == -1 ? QUERY_STRING_SEPARATOR : PARAM_SEPARATOR;
- url += queryString;
- return url;
+ return url
+ + (url.indexOf(QUERY_STRING_SEPARATOR) == -1 ? QUERY_STRING_SEPARATOR : PARAM_SEPARATOR)
+ + queryString;
}
}
@@ -58,9 +60,9 @@ public String asFormUrlEncodedString() {
final StringBuilder builder = new StringBuilder();
for (Parameter p : params) {
- builder.append('&').append(p.asUrlEncodedPair());
+ builder.append(PARAM_SEPARATOR).append(p.asUrlEncodedPair());
}
- return builder.toString().substring(1);
+ return builder.substring(1);
}
public void addAll(ParameterList other) {
@@ -68,7 +70,7 @@ public void addAll(ParameterList other) {
}
public void addQuerystring(String queryString) {
- if (queryString != null && queryString.length() > 0) {
+ if (queryString != null && !queryString.isEmpty()) {
for (String param : queryString.split(PARAM_SEPARATOR)) {
final String[] pair = param.split(PAIR_SEPARATOR);
final String key = OAuthEncoder.decode(pair[0]);
diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/model/Response.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/Response.java
index bce6e584a..553c6cda5 100644
--- a/scribejava-core/src/main/java/com/github/scribejava/core/model/Response.java
+++ b/scribejava-core/src/main/java/com/github/scribejava/core/model/Response.java
@@ -2,68 +2,68 @@
import java.io.IOException;
import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.UnknownHostException;
-import java.util.HashMap;
import java.util.Map;
-import com.github.scribejava.core.exceptions.OAuthException;
import com.github.scribejava.core.utils.StreamUtils;
+import java.io.Closeable;
-public class Response {
+/**
+ * An HTTP response.
+ *
+ *
+ * This response may contain a non-null body stream of the HttpUrlConnection. If so, this body must be closed to avoid
+ * leaking resources. Use either {@link #getBody()} or {@link #close()} to close the body.
+ */
+public class Response implements Closeable {
- private int code;
- private String message;
+ private final int code;
+ private final String message;
+ private final Map headers;
private String body;
private InputStream stream;
- private Map headers;
+ private Closeable[] closeables;
+ private boolean closed;
- public Response(int code, String message, Map headers, String body, InputStream stream) {
+ private Response(int code, String message, Map headers) {
this.code = code;
- this.headers = headers;
- this.body = body;
this.message = message;
+ this.headers = headers;
+ }
+
+ public Response(int code, String message, Map headers, InputStream stream,
+ Closeable... closeables) {
+ this(code, message, headers);
this.stream = stream;
+ this.closeables = closeables;
}
- Response(HttpURLConnection connection) throws IOException {
- try {
- connection.connect();
- code = connection.getResponseCode();
- message = connection.getResponseMessage();
- headers = parseHeaders(connection);
- stream = isSuccessful() ? connection.getInputStream() : connection.getErrorStream();
- } catch (UnknownHostException e) {
- throw new OAuthException("The IP address of a host could not be determined.", e);
- }
+ public Response(int code, String message, Map headers, String body) {
+ this(code, message, headers);
+ this.body = body;
}
- private String parseBodyContents() {
+ private String parseBodyContents() throws IOException {
+ if (stream == null) {
+ return null;
+ }
if ("gzip".equals(getHeader("Content-Encoding"))) {
- body = StreamUtils.getGzipStreamContents(getStream());
+ body = StreamUtils.getGzipStreamContents(stream);
} else {
- body = StreamUtils.getStreamContents(getStream());
+ body = StreamUtils.getStreamContents(stream);
}
return body;
}
- private Map parseHeaders(HttpURLConnection conn) {
- final Map headers = new HashMap<>();
- for (String key : conn.getHeaderFields().keySet()) {
- headers.put(key, conn.getHeaderFields().get(key).get(0));
- }
- return headers;
- }
-
- public final boolean isSuccessful() {
- return getCode() >= 200 && getCode() < 400;
+ public boolean isSuccessful() {
+ return code >= 200 && code < 400;
}
/**
- * Obtains the HTTP Response body
+ * Returns the response body as a string, closing the stream that backs it. Idempotent.
*
- * @return response body
+ * @return body as string
+ * @throws IOException IO Exception
*/
- public String getBody() {
+ public String getBody() throws IOException {
return body == null ? parseBodyContents() : body;
}
@@ -82,7 +82,7 @@ public InputStream getStream() {
*
* @return the status code
*/
- public final int getCode() {
+ public int getCode() {
return code;
}
@@ -118,11 +118,37 @@ public String getHeader(String name) {
@Override
public String toString() {
- return "Response{" +
- "code=" + code +
- ", message='" + message + '\'' +
- ", body='" + body + '\'' +
- ", headers=" + headers +
- '}';
+ return "Response{"
+ + "code=" + code
+ + ", message='" + message + '\''
+ + ", body='" + body + '\''
+ + ", headers=" + headers
+ + '}';
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (closed) {
+ return;
+ }
+ IOException ioException = null;
+ if (closeables != null) {
+ for (Closeable closeable : closeables) {
+ if (closeable == null) {
+ continue;
+ }
+ try {
+ closeable.close();
+ } catch (IOException ioE) {
+ if (ioException != null) {
+ ioException = ioE;
+ }
+ }
+ }
+ }
+ if (ioException != null) {
+ throw ioException;
+ }
+ closed = true;
}
}
diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/model/ScribeJavaConfig.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/ScribeJavaConfig.java
deleted file mode 100644
index 171bbac1a..000000000
--- a/scribejava-core/src/main/java/com/github/scribejava/core/model/ScribeJavaConfig.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.github.scribejava.core.model;
-
-public abstract class ScribeJavaConfig {
-
- private static ForceTypeOfHttpRequest forceTypeOfHttpRequests = ForceTypeOfHttpRequest.NONE;
-
- public static ForceTypeOfHttpRequest getForceTypeOfHttpRequests() {
- return forceTypeOfHttpRequests;
- }
-
- public static void setForceTypeOfHttpRequests(ForceTypeOfHttpRequest forceTypeOfHttpRequests) {
- ScribeJavaConfig.forceTypeOfHttpRequests = forceTypeOfHttpRequests;
- }
-}
diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/model/SignatureType.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/SignatureType.java
deleted file mode 100644
index 26ffe054e..000000000
--- a/scribejava-core/src/main/java/com/github/scribejava/core/model/SignatureType.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.github.scribejava.core.model;
-
-public enum SignatureType {
-
- Header,
- QueryString
-}
diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/model/Verb.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/Verb.java
index 6ca83a517..8df5fd3f3 100644
--- a/scribejava-core/src/main/java/com/github/scribejava/core/model/Verb.java
+++ b/scribejava-core/src/main/java/com/github/scribejava/core/model/Verb.java
@@ -5,5 +5,28 @@
*/
public enum Verb {
- GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE, PATCH
+ GET(false), POST(true), PUT(true), DELETE(false, true), HEAD(false), OPTIONS(false), TRACE(false), PATCH(true);
+
+ private final boolean requiresBody;
+ private final boolean permitBody;
+
+ Verb(boolean requiresBody) {
+ this(requiresBody, requiresBody);
+ }
+
+ Verb(boolean requiresBody, boolean permitBody) {
+ if (requiresBody && !permitBody) {
+ throw new IllegalArgumentException();
+ }
+ this.requiresBody = requiresBody;
+ this.permitBody = permitBody;
+ }
+
+ public boolean isRequiresBody() {
+ return requiresBody;
+ }
+
+ public boolean isPermitBody() {
+ return permitBody;
+ }
}
diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/oauth/AccessTokenRequestParams.java b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/AccessTokenRequestParams.java
new file mode 100644
index 000000000..d45377a56
--- /dev/null
+++ b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/AccessTokenRequestParams.java
@@ -0,0 +1,79 @@
+package com.github.scribejava.core.oauth;
+
+import com.github.scribejava.core.builder.ScopeBuilder;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * not thread safe
+ */
+public class AccessTokenRequestParams {
+
+ private final String code;
+ private String pkceCodeVerifier;
+ private String scope;
+ private Map extraParameters;
+
+ public AccessTokenRequestParams(String code) {
+ this.code = code;
+ }
+
+ public static AccessTokenRequestParams create(String code) {
+ return new AccessTokenRequestParams(code);
+ }
+
+ public AccessTokenRequestParams pkceCodeVerifier(String pkceCodeVerifier) {
+ this.pkceCodeVerifier = pkceCodeVerifier;
+ return this;
+ }
+
+ public AccessTokenRequestParams scope(String scope) {
+ this.scope = scope;
+ return this;
+ }
+
+ public AccessTokenRequestParams scope(ScopeBuilder scope) {
+ this.scope = scope.build();
+ return this;
+ }
+
+ public AccessTokenRequestParams addExtraParameters(Map extraParameters) {
+ if (extraParameters == null || extraParameters.isEmpty()) {
+ return this;
+ }
+ if (this.extraParameters == null) {
+ extraParameters = new HashMap<>();
+ }
+ this.extraParameters.putAll(extraParameters);
+ return this;
+ }
+
+ public AccessTokenRequestParams addExtraParameter(String name, String value) {
+ if (this.extraParameters == null) {
+ extraParameters = new HashMap<>();
+ }
+ this.extraParameters.put(name, value);
+ return this;
+ }
+
+ public AccessTokenRequestParams setExtraParameters(Map extraParameters) {
+ this.extraParameters = extraParameters;
+ return this;
+ }
+
+ public Map getExtraParameters() {
+ return extraParameters;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public String getPkceCodeVerifier() {
+ return pkceCodeVerifier;
+ }
+
+ public String getScope() {
+ return scope;
+ }
+}
diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/oauth/AuthorizationUrlBuilder.java b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/AuthorizationUrlBuilder.java
new file mode 100644
index 000000000..fd02f60dd
--- /dev/null
+++ b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/AuthorizationUrlBuilder.java
@@ -0,0 +1,61 @@
+package com.github.scribejava.core.oauth;
+
+import com.github.scribejava.core.pkce.PKCE;
+import com.github.scribejava.core.pkce.PKCEService;
+import java.util.HashMap;
+import java.util.Map;
+
+public class AuthorizationUrlBuilder {
+
+ private final OAuth20Service oauth20Service;
+
+ private String state;
+ private Map additionalParams;
+ private PKCE pkce;
+ private String scope;
+
+ public AuthorizationUrlBuilder(OAuth20Service oauth20Service) {
+ this.oauth20Service = oauth20Service;
+ }
+
+ public AuthorizationUrlBuilder state(String state) {
+ this.state = state;
+ return this;
+ }
+
+ public AuthorizationUrlBuilder additionalParams(Map additionalParams) {
+ this.additionalParams = additionalParams;
+ return this;
+ }
+
+ public AuthorizationUrlBuilder pkce(PKCE pkce) {
+ this.pkce = pkce;
+ return this;
+ }
+
+ public AuthorizationUrlBuilder initPKCE() {
+ this.pkce = PKCEService.defaultInstance().generatePKCE();
+ return this;
+ }
+
+ public AuthorizationUrlBuilder scope(String scope) {
+ this.scope = scope;
+ return this;
+ }
+
+ public PKCE getPkce() {
+ return pkce;
+ }
+
+ public String build() {
+ final Map params;
+ if (pkce == null) {
+ params = additionalParams;
+ } else {
+ params = additionalParams == null ? new HashMap() : new HashMap<>(additionalParams);
+ params.putAll(pkce.getAuthorizationUrlParams());
+ }
+ return oauth20Service.getApi().getAuthorizationUrl(oauth20Service.getResponseType(), oauth20Service.getApiKey(),
+ oauth20Service.getCallback(), scope == null ? oauth20Service.getDefaultScope() : scope, state, params);
+ }
+}
diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuth10aService.java b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuth10aService.java
index c9c99a03f..f48308b2e 100644
--- a/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuth10aService.java
+++ b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuth10aService.java
@@ -1,21 +1,20 @@
package com.github.scribejava.core.oauth;
-import com.ning.http.client.ProxyServer;
import java.io.IOException;
-import java.util.Map;
import java.util.concurrent.Future;
import com.github.scribejava.core.builder.api.DefaultApi10a;
-import com.github.scribejava.core.model.AbstractRequest;
+import com.github.scribejava.core.builder.api.OAuth1SignatureType;
+import com.github.scribejava.core.httpclient.HttpClient;
+import com.github.scribejava.core.httpclient.HttpClientConfig;
import com.github.scribejava.core.model.OAuth1AccessToken;
import com.github.scribejava.core.model.OAuth1RequestToken;
import com.github.scribejava.core.model.OAuthAsyncRequestCallback;
-import com.github.scribejava.core.model.OAuthConfig;
import com.github.scribejava.core.model.OAuthConstants;
import com.github.scribejava.core.model.OAuthRequest;
-import com.github.scribejava.core.model.OAuthRequestAsync;
import com.github.scribejava.core.model.Response;
-import com.github.scribejava.core.services.Base64Encoder;
-import com.github.scribejava.core.utils.MapUtils;
+import java.io.OutputStream;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
/**
* OAuth 1.0a implementation of {@link OAuthService}
@@ -24,114 +23,145 @@ public class OAuth10aService extends OAuthService {
private static final String VERSION = "1.0";
private final DefaultApi10a api;
+ private final String scope;
- /**
- * Default constructor
- *
- * @param api OAuth1.0a api information
- * @param config OAuth 1.0a configuration param object
- */
- public OAuth10aService(DefaultApi10a api, OAuthConfig config) {
- super(config);
+ public OAuth10aService(DefaultApi10a api, String apiKey, String apiSecret, String callback, String scope,
+ OutputStream debugStream, String userAgent, HttpClientConfig httpClientConfig, HttpClient httpClient) {
+ super(apiKey, apiSecret, callback, debugStream, userAgent, httpClientConfig, httpClient);
this.api = api;
+ this.scope = scope;
}
- /**
- * Retrieve the request token.
- *
- * @return request token
- */
- public OAuth1RequestToken getRequestToken() {
- final OAuthConfig config = getConfig();
- config.log("obtaining request token from " + api.getRequestTokenEndpoint());
- final OAuthRequest request = new OAuthRequest(api.getRequestTokenVerb(), api.getRequestTokenEndpoint(), this);
+ public OAuth1RequestToken getRequestToken() throws IOException, InterruptedException, ExecutionException {
+ if (isDebug()) {
+ log("obtaining request token from %s", api.getRequestTokenEndpoint());
+ }
+ final OAuthRequest request = prepareRequestTokenRequest();
+
+ log("sending request...");
+ try ( Response response = execute(request)) {
+ if (isDebug()) {
+ final String body = response.getBody();
+ log("response status code: %s", response.getCode());
+ log("response body: %s", body);
+ }
+ return api.getRequestTokenExtractor().extract(response);
+ }
+ }
- config.log("setting oauth_callback to " + config.getCallback());
- request.addOAuthParameter(OAuthConstants.CALLBACK, config.getCallback());
- addOAuthParams(request, "");
- appendSignature(request);
+ public Future getRequestTokenAsync() {
+ return getRequestTokenAsync(null);
+ }
- config.log("sending request...");
- final Response response = request.send();
- final String body = response.getBody();
+ public Future getRequestTokenAsync(OAuthAsyncRequestCallback callback) {
+ if (isDebug()) {
+ log("async obtaining request token from %s", api.getRequestTokenEndpoint());
+ }
+ final OAuthRequest request = prepareRequestTokenRequest();
+ return execute(request, callback, new OAuthRequest.ResponseConverter() {
+ @Override
+ public OAuth1RequestToken convert(Response response) throws IOException {
+ final OAuth1RequestToken token = getApi().getRequestTokenExtractor().extract(response);
+ response.close();
+ return token;
+ }
+ });
+ }
- config.log("response status code: " + response.getCode());
- config.log("response body: " + body);
- return api.getRequestTokenExtractor().extract(body);
+ protected OAuthRequest prepareRequestTokenRequest() {
+ final OAuthRequest request = new OAuthRequest(api.getRequestTokenVerb(), api.getRequestTokenEndpoint());
+ String callback = getCallback();
+ if (callback == null) {
+ callback = OAuthConstants.OOB;
+ }
+ if (isDebug()) {
+ log("setting oauth_callback to %s", callback);
+ }
+ request.addOAuthParameter(OAuthConstants.CALLBACK, callback);
+ addOAuthParams(request, "");
+ appendSignature(request);
+ return request;
}
- private void addOAuthParams(AbstractRequest request, String tokenSecret) {
- final OAuthConfig config = getConfig();
+ protected void addOAuthParams(OAuthRequest request, String tokenSecret) {
request.addOAuthParameter(OAuthConstants.TIMESTAMP, api.getTimestampService().getTimestampInSeconds());
request.addOAuthParameter(OAuthConstants.NONCE, api.getTimestampService().getNonce());
- request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, config.getApiKey());
+ request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, getApiKey());
request.addOAuthParameter(OAuthConstants.SIGN_METHOD, api.getSignatureService().getSignatureMethod());
request.addOAuthParameter(OAuthConstants.VERSION, getVersion());
- if (config.hasScope()) {
- request.addOAuthParameter(OAuthConstants.SCOPE, config.getScope());
+ if (scope != null) {
+ request.addOAuthParameter(OAuthConstants.SCOPE, scope);
}
request.addOAuthParameter(OAuthConstants.SIGNATURE, getSignature(request, tokenSecret));
- config.log("appended additional OAuth parameters: " + MapUtils.toString(request.getOauthParameters()));
+ if (isDebug()) {
+ log("appended additional OAuth parameters: %s", request.getOauthParameters());
+ }
}
- public final OAuth1AccessToken getAccessToken(OAuth1RequestToken requestToken, String oauthVerifier) {
- final OAuthConfig config = getConfig();
- config.log("obtaining access token from " + api.getAccessTokenEndpoint());
- final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this);
- prepareAccessTokenRequest(request, requestToken, oauthVerifier);
- final Response response = request.send();
- return api.getAccessTokenExtractor().extract(response.getBody());
+ public OAuth1AccessToken getAccessToken(OAuth1RequestToken requestToken, String oauthVerifier)
+ throws IOException, InterruptedException, ExecutionException {
+ if (isDebug()) {
+ log("obtaining access token from %s", api.getAccessTokenEndpoint());
+ }
+ final OAuthRequest request = prepareAccessTokenRequest(requestToken, oauthVerifier);
+ try ( Response response = execute(request)) {
+ return api.getAccessTokenExtractor().extract(response);
+ }
+ }
+
+ public Future getAccessTokenAsync(OAuth1RequestToken requestToken, String oauthVerifier) {
+ return getAccessTokenAsync(requestToken, oauthVerifier, null);
}
/**
- * Start the request to retrieve the access token. The optionally provided
- * callback will be called with the Token when it is available.
+ * Start the request to retrieve the access token. The optionally provided callback will be called with the Token
+ * when it is available.
*
* @param requestToken request token (obtained previously or null)
* @param oauthVerifier oauth_verifier
* @param callback optional callback
* @return Future
*/
- public final Future getAccessTokenAsync(OAuth1RequestToken requestToken, String oauthVerifier,
+ public Future getAccessTokenAsync(OAuth1RequestToken requestToken, String oauthVerifier,
OAuthAsyncRequestCallback callback) {
- return getAccessTokenAsync(requestToken, oauthVerifier, callback, null);
- }
-
- public final Future getAccessTokenAsync(OAuth1RequestToken requestToken, String oauthVerifier,
- OAuthAsyncRequestCallback callback, ProxyServer proxyServer) {
- final OAuthConfig config = getConfig();
- config.log("async obtaining access token from " + api.getAccessTokenEndpoint());
- final OAuthRequestAsync request
- = new OAuthRequestAsync(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this);
- prepareAccessTokenRequest(request, requestToken, oauthVerifier);
- return request.sendAsync(callback, new OAuthRequestAsync.ResponseConverter() {
+ if (isDebug()) {
+ log("async obtaining access token from %s", api.getAccessTokenEndpoint());
+ }
+ final OAuthRequest request = prepareAccessTokenRequest(requestToken, oauthVerifier);
+ return execute(request, callback, new OAuthRequest.ResponseConverter() {
@Override
- public OAuth1AccessToken convert(com.ning.http.client.Response response) throws IOException {
- return getApi().getAccessTokenExtractor()
- .extract(OAuthRequestAsync.RESPONSE_CONVERTER.convert(response).getBody());
+ public OAuth1AccessToken convert(Response response) throws IOException {
+ final OAuth1AccessToken token = getApi().getAccessTokenExtractor().extract(response);
+ response.close();
+ return token;
}
- }, proxyServer);
+ });
}
- protected void prepareAccessTokenRequest(AbstractRequest request, OAuth1RequestToken requestToken,
- String oauthVerifier) {
- final OAuthConfig config = getConfig();
+ protected OAuthRequest prepareAccessTokenRequest(OAuth1RequestToken requestToken, String oauthVerifier) {
+ final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint());
request.addOAuthParameter(OAuthConstants.TOKEN, requestToken.getToken());
request.addOAuthParameter(OAuthConstants.VERIFIER, oauthVerifier);
- config.log("setting token to: " + requestToken + " and verifier to: " + oauthVerifier);
+ if (isDebug()) {
+ log("setting token to: %s and verifier to: %s", requestToken, oauthVerifier);
+ }
addOAuthParams(request, requestToken.getTokenSecret());
appendSignature(request);
+ return request;
}
- public void signRequest(OAuth1AccessToken token, AbstractRequest request) {
- final OAuthConfig config = getConfig();
- config.log("signing request: " + request.getCompleteUrl());
+ public void signRequest(OAuth1AccessToken token, OAuthRequest request) {
+ if (isDebug()) {
+ log("signing request: %s", request.getCompleteUrl());
+ }
if (!token.isEmpty() || api.isEmptyOAuthTokenParamIsRequired()) {
request.addOAuthParameter(OAuthConstants.TOKEN, token.getToken());
}
- config.log("setting token to: " + token);
+ if (isDebug()) {
+ log("setting token to: %s", token);
+ }
addOAuthParams(request, token.getTokenSecret());
appendSignature(request);
}
@@ -142,8 +172,7 @@ public String getVersion() {
}
/**
- * Returns the URL where you should redirect your users to authenticate your
- * application.
+ * Returns the URL where you should redirect your users to authenticate your application.
*
* @param requestToken the request token you need to authorize
* @return the URL where you should redirect your users
@@ -152,36 +181,36 @@ public String getAuthorizationUrl(OAuth1RequestToken requestToken) {
return api.getAuthorizationUrl(requestToken);
}
- private String getSignature(AbstractRequest request, String tokenSecret) {
- final OAuthConfig config = getConfig();
- config.log("generating signature...");
- config.log("using base64 encoder: " + Base64Encoder.type());
+ private String getSignature(OAuthRequest request, String tokenSecret) {
+ log("generating signature...");
final String baseString = api.getBaseStringExtractor().extract(request);
- final String signature = api.getSignatureService().getSignature(baseString, config.getApiSecret(), tokenSecret);
+ final String signature = api.getSignatureService().getSignature(baseString, getApiSecret(), tokenSecret);
- config.log("base string is: " + baseString);
- config.log("signature is: " + signature);
+ if (isDebug()) {
+ log("base string is: %s", baseString);
+ log("signature is: %s", signature);
+ }
return signature;
}
- private void appendSignature(AbstractRequest request) {
- final OAuthConfig config = getConfig();
- switch (config.getSignatureType()) {
- case Header:
- config.log("using Http Header signature");
+ protected void appendSignature(OAuthRequest request) {
+ final OAuth1SignatureType signatureType = api.getSignatureType();
+ switch (signatureType) {
+ case HEADER:
+ log("using Http Header signature");
final String oauthHeader = api.getHeaderExtractor().extract(request);
request.addHeader(OAuthConstants.HEADER, oauthHeader);
break;
- case QueryString:
- config.log("using Querystring signature");
+ case QUERY_STRING:
+ log("using Querystring signature");
- for (Map.Entry entry : request.getOauthParameters().entrySet()) {
- request.addQuerystringParameter(entry.getKey(), entry.getValue());
+ for (Map.Entry oauthParameter : request.getOauthParameters().entrySet()) {
+ request.addQuerystringParameter(oauthParameter.getKey(), oauthParameter.getValue());
}
break;
default:
- throw new IllegalStateException("Unknown new Signature Type '" + config.getSignatureType() + "'.");
+ throw new IllegalStateException("Unknown new Signature Type '" + signatureType + "'.");
}
}
diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuth20Service.java b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuth20Service.java
index 2c7f8306b..82052e351 100644
--- a/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuth20Service.java
+++ b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuth20Service.java
@@ -1,239 +1,676 @@
package com.github.scribejava.core.oauth;
-import com.github.scribejava.core.services.Base64Encoder;
-import com.ning.http.client.ProxyServer;
-import java.io.IOException;
-import java.nio.charset.Charset;
-import java.util.concurrent.Future;
import com.github.scribejava.core.builder.api.DefaultApi20;
-import com.github.scribejava.core.model.AbstractRequest;
+import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor;
+import com.github.scribejava.core.httpclient.HttpClient;
+import com.github.scribejava.core.httpclient.HttpClientConfig;
+import com.github.scribejava.core.model.DeviceAuthorization;
import com.github.scribejava.core.model.OAuth2AccessToken;
+import com.github.scribejava.core.model.OAuth2AccessTokenErrorResponse;
import com.github.scribejava.core.model.OAuth2Authorization;
import com.github.scribejava.core.model.OAuthAsyncRequestCallback;
-import com.github.scribejava.core.model.OAuthConfig;
import com.github.scribejava.core.model.OAuthConstants;
import com.github.scribejava.core.model.OAuthRequest;
-import com.github.scribejava.core.model.OAuthRequestAsync;
+import com.github.scribejava.core.model.Response;
+import com.github.scribejava.core.model.Verb;
+import com.github.scribejava.core.oauth2.OAuth2Error;
+import com.github.scribejava.core.pkce.PKCE;
+import com.github.scribejava.core.revoke.TokenTypeHint;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
public class OAuth20Service extends OAuthService {
private static final String VERSION = "2.0";
private final DefaultApi20 api;
+ private final String responseType;
+ private final String defaultScope;
+
+ public OAuth20Service(DefaultApi20 api, String apiKey, String apiSecret, String callback, String defaultScope,
+ String responseType, OutputStream debugStream, String userAgent, HttpClientConfig httpClientConfig,
+ HttpClient httpClient) {
+ super(apiKey, apiSecret, callback, debugStream, userAgent, httpClientConfig, httpClient);
+ this.responseType = responseType;
+ this.api = api;
+ this.defaultScope = defaultScope;
+ }
+ // ===== common OAuth methods =====
/**
- * Default constructor
+ * {@inheritDoc}
+ */
+ @Override
+ public String getVersion() {
+ return VERSION;
+ }
+
+ public void signRequest(String accessToken, OAuthRequest request) {
+ api.getBearerSignature().signRequest(accessToken, request);
+ }
+
+ public void signRequest(OAuth2AccessToken accessToken, OAuthRequest request) {
+ signRequest(accessToken == null ? null : accessToken.getAccessToken(), request);
+ }
+
+ /**
+ * Returns the URL where you should redirect your users to authenticate your application.
*
- * @param api OAuth2.0 api information
- * @param config OAuth 2.0 configuration param object
+ * @return the URL where you should redirect your users
*/
- public OAuth20Service(DefaultApi20 api, OAuthConfig config) {
- super(config);
- this.api = api;
+ public String getAuthorizationUrl() {
+ return createAuthorizationUrlBuilder().build();
+ }
+
+ public String getAuthorizationUrl(String state) {
+ return createAuthorizationUrlBuilder()
+ .state(state)
+ .build();
+ }
+
+ /**
+ * Returns the URL where you should redirect your users to authenticate your application.
+ *
+ * @param additionalParams any additional GET params to add to the URL
+ * @return the URL where you should redirect your users
+ */
+ public String getAuthorizationUrl(Map additionalParams) {
+ return createAuthorizationUrlBuilder()
+ .additionalParams(additionalParams)
+ .build();
+ }
+
+ public String getAuthorizationUrl(PKCE pkce) {
+ return createAuthorizationUrlBuilder()
+ .pkce(pkce)
+ .build();
+ }
+
+ public AuthorizationUrlBuilder createAuthorizationUrlBuilder() {
+ return new AuthorizationUrlBuilder(this);
+ }
+
+ public DefaultApi20 getApi() {
+ return api;
+ }
+
+ public OAuth2Authorization extractAuthorization(String redirectLocation) {
+ final OAuth2Authorization authorization = new OAuth2Authorization();
+ int end = redirectLocation.indexOf('#');
+ if (end == -1) {
+ end = redirectLocation.length();
+ }
+ for (String param : redirectLocation.substring(redirectLocation.indexOf('?') + 1, end).split("&")) {
+ final String[] keyValue = param.split("=");
+ if (keyValue.length == 2) {
+ try {
+ switch (keyValue[0]) {
+ case "code":
+ authorization.setCode(URLDecoder.decode(keyValue[1], "UTF-8"));
+ break;
+ case "state":
+ authorization.setState(URLDecoder.decode(keyValue[1], "UTF-8"));
+ break;
+ default: //just ignore any other param;
+ }
+ } catch (UnsupportedEncodingException ueE) {
+ throw new IllegalStateException("jvm without UTF-8, really?", ueE);
+ }
+ }
+ }
+ return authorization;
+ }
+
+ public String getResponseType() {
+ return responseType;
+ }
+
+ public String getDefaultScope() {
+ return defaultScope;
+ }
+
+ protected void logRequestWithParams(String requestDescription, OAuthRequest request) {
+ if (isDebug()) {
+ log("created " + requestDescription + " request with body params [%s], query string params [%s]",
+ request.getBodyParams().asFormUrlEncodedString(),
+ request.getQueryStringParams().asFormUrlEncodedString());
+ }
+ }
+
+ // ===== common AccessToken request methods =====
+ //protected to facilitate mocking
+ protected OAuth2AccessToken sendAccessTokenRequestSync(OAuthRequest request)
+ throws IOException, InterruptedException, ExecutionException {
+ if (isDebug()) {
+ log("send request for access token synchronously to %s", request.getCompleteUrl());
+ }
+ try (Response response = execute(request)) {
+ if (isDebug()) {
+ log("response status code: %s", response.getCode());
+ log("response body: %s", response.getBody());
+ }
+
+ return api.getAccessTokenExtractor().extract(response);
+ }
}
- //sync version, protected to facilitate mocking
- protected OAuth2AccessToken sendAccessTokenRequestSync(OAuthRequest request) {
- return api.getAccessTokenExtractor().extract(request.send().getBody());
+ //protected to facilitate mocking
+ protected Future sendAccessTokenRequestAsync(OAuthRequest request) {
+ return sendAccessTokenRequestAsync(request, null);
}
- //async version, protected to facilitate mocking
- protected Future sendAccessTokenRequestAsync(OAuthRequestAsync request,
- OAuthAsyncRequestCallback callback, ProxyServer proxyServer) {
+ //protected to facilitate mocking
+ protected Future sendAccessTokenRequestAsync(OAuthRequest request,
+ OAuthAsyncRequestCallback callback) {
+ if (isDebug()) {
+ log("send request for access token asynchronously to %s", request.getCompleteUrl());
+ }
- return request.sendAsync(callback, new OAuthRequestAsync.ResponseConverter() {
+ return execute(request, callback, new OAuthRequest.ResponseConverter() {
@Override
- public OAuth2AccessToken convert(com.ning.http.client.Response response) throws IOException {
- return getApi().getAccessTokenExtractor()
- .extract(OAuthRequestAsync.RESPONSE_CONVERTER.convert(response).getBody());
+ public OAuth2AccessToken convert(Response response) throws IOException {
+ log("received response for access token");
+ if (isDebug()) {
+ log("response status code: %s", response.getCode());
+ log("response body: %s", response.getBody());
+ }
+ final OAuth2AccessToken token = api.getAccessTokenExtractor().extract(response);
+ response.close();
+ return token;
}
- }, proxyServer);
+ });
}
- public final OAuth2AccessToken getAccessToken(String code) {
- final OAuthRequest request = createAccessTokenRequest(code,
- new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this));
+ // ===== get AccessToken authorisation code flow methods =====
+ protected OAuthRequest createAccessTokenRequest(AccessTokenRequestParams params) {
+ final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint());
- return sendAccessTokenRequestSync(request);
+ api.getClientAuthentication().addClientAuthentication(request, getApiKey(), getApiSecret());
+
+ request.addParameter(OAuthConstants.CODE, params.getCode());
+ final String callback = getCallback();
+ if (callback != null) {
+ request.addParameter(OAuthConstants.REDIRECT_URI, callback);
+ }
+ final String scope = params.getScope();
+ if (scope != null) {
+ request.addParameter(OAuthConstants.SCOPE, scope);
+ } else if (defaultScope != null) {
+ request.addParameter(OAuthConstants.SCOPE, defaultScope);
+ }
+ request.addParameter(OAuthConstants.GRANT_TYPE, OAuthConstants.AUTHORIZATION_CODE);
+
+ final String pkceCodeVerifier = params.getPkceCodeVerifier();
+ if (pkceCodeVerifier != null) {
+ request.addParameter(PKCE.PKCE_CODE_VERIFIER_PARAM, pkceCodeVerifier);
+ }
+
+ final Map extraParameters = params.getExtraParameters();
+ if (extraParameters != null && !extraParameters.isEmpty()) {
+ for (Map.Entry extraParameter : extraParameters.entrySet()) {
+ request.addParameter(extraParameter.getKey(), extraParameter.getValue());
+ }
+ }
+
+ logRequestWithParams("access token", request);
+ return request;
+ }
+
+ public Future getAccessTokenAsync(String code) {
+ return getAccessToken(AccessTokenRequestParams.create(code), null);
+ }
+
+ public Future getAccessTokenAsync(AccessTokenRequestParams params) {
+ return getAccessToken(params, null);
+ }
+
+ public OAuth2AccessToken getAccessToken(String code) throws IOException, InterruptedException, ExecutionException {
+ return getAccessToken(AccessTokenRequestParams.create(code));
+ }
+
+ public OAuth2AccessToken getAccessToken(AccessTokenRequestParams params)
+ throws IOException, InterruptedException, ExecutionException {
+ return sendAccessTokenRequestSync(createAccessTokenRequest(params));
}
/**
* Start the request to retrieve the access token. The optionally provided callback will be called with the Token
* when it is available.
*
- * @param code code
+ * @param params params
* @param callback optional callback
* @return Future
*/
- public final Future