I am implementing oauth by Java with folowing sequence:
1) Sending POST https ://api.twitter.com/oauth/request_token (with callback) Twitter reponse contains oauth_token, oauth_token_secret and oauth_callback_confirmed=true
2) Redirecting to https ://api.twitter.com/oauth/authenticate?oauth_token={oauth_token from previous response from twitter}
3) Twitter login form appears, I click on the button "login".
4) Twitter redirects to {callback_url}?oauth_token={this token equals token from oauth/request_token response}&oauth_verifier={verifier}
5) POST https ://api.twitter.com/oauth/access_token with Oauth header includes oauth_token, message body contains oauth_verifier={returned verifier}
6) Twitter response=Error processing your OAuth request: Invalid oauth_verifier parameter
What is wrong with oauth_verifier?
Compute signature method:
private static String computeSignature(String baseString, String keyString) throws GeneralSecurityException, UnsupportedEncodingException {
SecretKey secretKey = null;
byte[] keyBytes = keyString.getBytes();
secretKey = new SecretKeySpec(keyBytes, "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(secretKey);
byte[] text = baseString.getBytes();
return new String(Base64.encodeBase64(mac.doFinal(text))).trim();
}
Code for first request:
String oauth_signature_method = "HMAC-SHA1";
// generate any fairly random alphanumeric string as the "nonce".
String uuid_string = UUID.randomUUID().toString();
uuid_string = uuid_string.replaceAll("-", "");
String oauth_nonce = uuid_string;
// get the timestamp
Calendar tempcal = Calendar.getInstance();
long ts = tempcal.getTimeInMillis();
String oauth_timestamp = (new Long(ts / 1000)).toString();
String parameter_string = "oauth_callback=" + OauthConstants.TWITTER_OAUTH_CALLBACK
+ "&oauth_consumer_key=" + OauthConstants.TWITTER_OAUTH_CONSUMER_KEY
+ "&oauth_nonce=" + oauth_nonce + "&oauth_signature_method="
+ oauth_signature_method + "&oauth_timestamp=" + oauth_timestamp + "&oauth_version=1.0";
String signature_base_string = get_or_post + "&" + encode(twitter_endpoint) + "&" + encode(parameter_string);
String oauth_signature = "";
try {
oauth_signature = computeSignature(signature_base_string, OauthConstants.TWITTER_OAUTH_CONSUMER_SECRET + "&");
} catch (GeneralSecurityException | UnsupportedEncodingException e) {
...}
String twitter_endpoint = "https://api.twitter.com/oauth/request_token";
String authorization_header_string = "OAuth oauth_callback=\"" + OauthConstants.TWITTER_OAUTH_CALLBACK
+ "\",oauth_consumer_key=\"" + OauthConstants.TWITTER_OAUTH_CONSUMER_KEY
+ "\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"" + oauth_timestamp
+ "\",oauth_nonce=\"" + oauth_nonce + "\",oauth_version=\"1.0\",oauth_signature=\""
+ encode(oauth_signature) + "\"";
// Apache httpcore 4.4.1
HttpProcessor httpproc = HttpProcessorBuilder.create()
.add(new RequestContent())
.add(new RequestTargetHost())
.add(new RequestConnControl())
.add(new RequestUserAgent("ApacheHttp/1.1"))
.add(new RequestExpectContinue(true)).build();
HttpRequestExecutor httpexecutor = new HttpRequestExecutor();
HttpCoreContext context = HttpCoreContext.create();
HttpHost host = new HttpHost(twitter_endpoint_host, 443);
DefaultBHttpClientConnection conn = new DefaultBHttpClientConnection(8 * 1024);
context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn);
context.setAttribute(HttpCoreContext.HTTP_TARGET_HOST, host);
try {
// initialize the HTTPS connection
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, null, null);
SSLSocketFactory ssf = sslcontext.getSocketFactory();
Socket socket = ssf.createSocket();
socket.connect(new InetSocketAddress(host.getHostName(), host.getPort()), 0);
conn.bind(socket);
BasicHttpEntityEnclosingRequest request2 = new BasicHttpEntityEnclosingRequest("POST", twitter_endpoint_path, HttpVersion.HTTP_1_1);
request2.setEntity(new StringEntity("", "UTF-8"));
request2.addHeader("Authorization", authorization_header_string);
httpexecutor.preProcess(request2, httpproc, context);
HttpResponse response2 = httpexecutor.execute(request2, conn, context);
httpexecutor.postProcess(response2, httpproc, context);
} catch(Exception e) {} ...
Code for second request (Redirect to https oauth/authenticate)
public JSONObject getTwitterAuthorizationCodeFromRequestToken(String oauth_token) {
...
String twitter_endpoint = "https://api.twitter.com/oauth/authenticate";
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
try {
FacesContext.getCurrentInstance().getExternalContext().redirect(twitter_endpoint + "?oauth_token=" + encode(oauth_token));
} catch (IOException ex) {...}
...
}
Code for 3rd request (POST oauth/access_token)
public JSONObject getTwitterAccessTokenFromAuthorizationCode(String verifier_or_pin, String oauth_token) {
...
String oauth_signature_method = "HMAC-SHA1";
// generate any fairly random alphanumeric string as the "nonce". Nonce = Number used ONCE.
String uuid_string = UUID.randomUUID().toString();
uuid_string = uuid_string.replaceAll("-", "");
String oauth_nonce = uuid_string;
Calendar tempcal = Calendar.getInstance();
long ts = tempcal.getTimeInMillis();
String oauth_timestamp = (new Long(ts / 1000)).toString();
// the parameter string must be in alphabetical order
String parameter_string = "oauth_consumer_key=" + OauthConstants.TWITTER_OAUTH_CONSUMER_KEY
+ "&oauth_nonce=" + oauth_nonce + "&oauth_signature_method=" + oauth_signature_method
+ "&oauth_timestamp=" + oauth_timestamp + "&oauth_token=" + encode(oauth_token) + "&oauth_version=1.0";
String signature_base_string = get_or_post + "&" + encode(twitter_endpoint) + "&" + encode(parameter_string);
String oauth_signature = "";
try {
oauth_signature = computeSignature(signature_base_string, OauthConstants.TWITTER_OAUTH_CONSUMER_SECRET + "&");
} catch (GeneralSecurityException | UnsupportedEncodingException e) {
...
}
String authorization_header_string = "OAuth oauth_consumer_key=\"" + OauthConstants.TWITTER_OAUTH_CONSUMER_KEY
+ "\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"" + oauth_timestamp
+ "\",oauth_nonce=\"" + oauth_nonce + "\",oauth_version=\"1.0\",oauth_signature=\""
+ encode(oauth_signature) + "\",oauth_token=\"" + encode(oauth_token) + "\"";
HttpProcessor httpproc = HttpProcessorBuilder.create()
.add(new RequestContent())
.add(new RequestTargetHost())
.add(new RequestConnControl())
.add(new RequestUserAgent("ApacheHttp/1.1"))
.add(new RequestExpectContinue(true)).build();
HttpRequestExecutor httpexecutor = new HttpRequestExecutor();
HttpCoreContext context = HttpCoreContext.create();
HttpHost host = new HttpHost(twitter_endpoint_host, 443);
DefaultBHttpClientConnection conn = new DefaultBHttpClientConnection(8 * 1024);
context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn);
context.setAttribute(HttpCoreContext.HTTP_TARGET_HOST, host);
try {
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, null, null);
SSLSocketFactory ssf = sslcontext.getSocketFactory();
Socket socket = ssf.createSocket();
socket.connect(new InetSocketAddress(host.getHostName(), host.getPort()), 0);
conn.bind(socket);
BasicHttpEntityEnclosingRequest request2 = new BasicHttpEntityEnclosingRequest("POST", twitter_endpoint_path);
// Including oauth_verifier value to request body
request2.setEntity(new StringEntity("oauth_verifier=" + encode(verifier_or_pin), "UTF-8"));
request2.addHeader("Authorization", authorization_header_string);
httpexecutor.preProcess(request2, httpproc, context);
HttpResponse response2 = httpexecutor.execute(request2, conn, context);
...
}
I've experienced exact same situation with our JavaScript code base. After struggling a day, I found a solution which resolve the error. That is just adding a "Content-Type" header with value of "application/x-www-form-urlencoded".
My code was used to be work correctly, but it stop working in last couple of months. My guess is that Twitter changed the implementation of OAuth handling recently which force us to add the content-type explicitly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With