Search code examples
javaoauthrequesttwitter-oauthsignature

OAuth 1.0 "Could not authenticate you."


Hı first of all this is a twitter auth. I can login, I can get the timeline etc etc. I can get those things because they dont want an extra paramaters. But now ı want to add "tweet_mode=extended" paramaters to that.

 public String extendedtweet(String token, String secret,String id) throws IOException{

    String url="https://api.twitter.com/1.1/statuses/lookup.json";
    headercreator hc=new headercreator(outhconsumerkey,consumersecret,token,secret);
    Map<String,String> requestparams = new HashMap<String, String>();
    Log.e("id",id);
    requestparams.put("id",id);
    String header=hc.generateHeader("GET",url,requestparams);
    Log.e("header",header);

    Response response =request(url,"","GET","Authorization",header);
    String jsonData=response.body().string();
    JsonObject js=new Gson().fromJson(jsonData, JsonObject.class);
    return js.get("full_text").getAsString();


}

When ı Delete the

        requestparams.put("id",id);

Line at there it says id paramaters is missing.That is Nice because that shows me the problem is at the Headercreator class which is here :

   package com.example.twittertestvol1;

import android.os.Build;
import android.util.Log;

import androidx.annotation.RequiresApi;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * Class to generate Oauth 1.0a header for Twitter
 *
 */
@RequiresApi(api = Build.VERSION_CODES.N)
public class headercreator {

    private String consumerKey;
    private String consumerSecret;
    private String signatureMethod;
    private String token;
    private String tokenSecret;
    private String version;

    public headercreator(String consumerKey, String consumerSecret, String token, String tokenSecret) {
        this.consumerKey = consumerKey;
        this.consumerSecret = consumerSecret;
        this.token = token;
        this.tokenSecret = tokenSecret;
        this.signatureMethod = "HMAC-SHA1";
        this.version = "1.0";
    }

    private static final String oauth_consumer_key = "oauth_consumer_key";
    private static final String oauth_token = "oauth_token";
    private static final String oauth_signature_method = "oauth_signature_method";
    private static final String oauth_timestamp = "oauth_timestamp";
    private static final String oauth_nonce = "oauth_nonce";
    private static final String oauth_version = "oauth_version";
    private static final String oauth_signature = "oauth_signature";
    private static final String HMAC_SHA1 = "HmacSHA1";

    /**
     * Generates oAuth 1.0a header which can be pass as Authorization header
     *
     * @param httpMethod
     * @param url
     * @param requestParams
     * @return
     */
    public String generateHeader(String httpMethod, String url, Map<String, String> requestParams) {
        StringBuilder base = new StringBuilder();
        String nonce = getNonce();
        String timestamp = getTimestamp();
        String baseSignatureString = generateSignatureBaseString(httpMethod, url, requestParams, nonce, timestamp);
        String signature = encryptUsingHmacSHA1(baseSignatureString);
        base.append("OAuth ");
        append(base, oauth_consumer_key, consumerKey);
        append(base, oauth_token, token);
        append(base, oauth_signature_method, signatureMethod);
        append(base, oauth_timestamp, timestamp);
        append(base, oauth_nonce, nonce);
        append(base, oauth_version, version);
        append(base, oauth_signature, signature);
        base.deleteCharAt(base.length() - 1);
        return base.toString();
    }

    /**
     * Generate base string to generate the oauth_signature
     *
     * @param httpMethod
     * @param url
     * @param requestParams
     * @return
     */
    private String generateSignatureBaseString(String httpMethod, String url, Map<String, String> requestParams, String nonce, String timestamp) {
        Map<String, String> params = new HashMap<>();
        if (requestParams!=null)
        {
            requestParams.entrySet().forEach(entry -> {
                put(params, entry.getKey(), entry.getValue());
            });

        }
        put(params, oauth_consumer_key, consumerKey);
        put(params, oauth_nonce, nonce);
        put(params, oauth_signature_method, signatureMethod);
        put(params, oauth_timestamp, timestamp);
        put(params, oauth_token, token);
        put(params, oauth_version, version);
        Map<String, String> sortedParams = params.entrySet().stream().sorted(Map.Entry.comparingByKey())
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new));
        StringBuilder base = new StringBuilder();
        sortedParams.entrySet().forEach(entry -> {
            base.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
        });
        base.deleteCharAt(base.length() - 1);
        String baseString = httpMethod.toUpperCase() + "&" + encode(url) + "&" + encode(base.toString());
        return baseString;
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    private String encryptUsingHmacSHA1(String input) {
        String secret = new StringBuilder().append(encode(consumerSecret)).append("&").append(encode(tokenSecret)).toString();
        byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
        SecretKey key = new SecretKeySpec(keyBytes, HMAC_SHA1);
        Mac mac;
        try {
            mac = Mac.getInstance(HMAC_SHA1);
            mac.init(key);
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            e.printStackTrace();
            return null;
        }
        byte[] signatureBytes = mac.doFinal(input.getBytes(StandardCharsets.UTF_8));
        return new String(Base64.getEncoder().encode(signatureBytes));
    }

    /**
     * Percentage encode String as per RFC 3986, Section 2.1
     *
     * @param value
     * @return
     */
    private String encode(String value) {
        String encoded = "";
        try {
            encoded = URLEncoder.encode(value, "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        String sb = "";
        char focus;
        for (int i = 0; i < encoded.length(); i++) {
            focus = encoded.charAt(i);
            if (focus == '*') {
                sb += "%2A";
            } else if (focus == '+') {
                sb += "%20";
            } else if (focus == '%' && i + 1 < encoded.length() && encoded.charAt(i + 1) == '7' && encoded.charAt(i + 2) == 'E') {
                sb += '~';
                i += 2;
            } else {
                sb += focus;
            }
        }
        return sb.toString();
    }

    private void put(Map<String, String> map, String key, String value) {
        map.put(encode(key), encode(value));
    }

    private void append(StringBuilder builder, String key, String value) {
        builder.append(encode(key)).append("=\"").append(encode(value)).append("\",");
    }

    private String getNonce() {
        int leftLimit = 48; // numeral '0'
        int rightLimit = 122; // letter 'z'
        int targetStringLength = 10;
        Random random = new Random();

        String generatedString = random.ints(leftLimit, rightLimit + 1).filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97)).limit(targetStringLength)
                .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString();
        return generatedString;

    }

    private String getTimestamp() {
        return Math.round((new Date()).getTime() / 1000.0) + "";
    }

}

Nowı wonder if someone shows me where is the problem why ı cant solve that. And that is the error code and error for that

{"errors":[{"code":32,"message":"Could not authenticate you."}]}

Here is thre request function

private Response request(String url, String bodys, String type, String Headername, String Header) throws IOException {

    OkHttpClient client = new OkHttpClient();
    MediaType mediaType = MediaType.parse("text/plain");
    RequestBody body = RequestBody.create(mediaType,bodys);
    Request request;
    if (!Headername.equals(""))
    {
        if (type.equals("GET"))
        {

            request = new Request.Builder()
                    .url(url)
                    .method(type,null)
                    .addHeader(Headername,Header)
                    .build();

        }
        else{
            request = new Request.Builder()
                    .url(url)
                    .method(type, body)
                    .addHeader(Headername,Header)
                    .build();
        }

    }
    else {
        request = new Request.Builder()
                .url(url)
                .method(type, body)
                .build();

    }

    return client.newCall(request).execute();
}

Solution

  • Issue is at Response response =request(url,"","GET","Authorization",header);.

    You need to pass in the request params when calling the service but you are not passing the id or any other param when calling the request() method. For me following code works:

        public void getTweet() {
            Map<String, String> requestParams = new HashMap<>();
            String id = "1263213348000325633";
            // Pass all request params for header generation
            requestParams.put("id", id);
            requestParams.put("tweet_mode", "extended");
            String url = "https://api.twitter.com/1.1/statuses/lookup.json";
            String header = generator.generateHeader(HttpMethod.GET.name(), url, requestParams);
            HttpHeaders headers = new HttpHeaders();
            headers.add("Authorization", header);
            HttpEntity<String> httpEntity = new HttpEntity<String>("body", headers);
            // Pass all request params in the request
            UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url)
                    .queryParam("id", id)
                    .queryParam("tweet_mode", "extended");
            ResponseEntity<String> response = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, httpEntity, String.class);
            String responseBody = response.getBody();
            assertNotNull(responseBody);
            System.out.println(responseBody);
        }
    

    Working code available here as a junit.