Home » Android » How to implement OAuth 1.0a using retrofit for woocommerce Api in Android

How to implement OAuth 1.0a using retrofit for woocommerce Api in Android

Posted by: admin June 15, 2020 Leave a comment

Questions:

I am currently working on a woocommerce api, I need to integrate the api using retrofit. The web site is in HTTP so HTTP Basic authentication cannot be used over plain HTTP as the keys are susceptible to interception. The API uses OAuth 1.0a “one-legged” authentication to ensure your API keys cannot be intercepted. I have gone through this artical to understand which OAuth methord to be used on http.

I have successfully implemented the api using Scribe but I want to implement the api using retrofit, after googling I found Interceptor be Jake Wharton Oauth1SigningInterceptor.

so I implemented that in retrofit for Authentication but the api call return

{"code":"woocommerce_rest_cannot_view","message":"Sorry, you cannot list resources.","data":{"status":401}}

The same api if I call using Scribe Return a successful Response

The below is how I call the Api

BasicAuthInterceptor.java(modified from jake wharton Oauth1SigningInterceptor)

public class BasicAuthInterceptor implements Interceptor {
private static final Escaper ESCAPER = UrlEscapers.urlFormParameterEscaper();
private static final String OAUTH_CONSUMER_KEY = "oauth_consumer_key";
private static final String OAUTH_NONCE = "oauth_nonce";
private static final String OAUTH_SIGNATURE = "oauth_signature";
private static final String OAUTH_SIGNATURE_METHOD = "oauth_signature_method";
private static final String OAUTH_SIGNATURE_METHOD_VALUE = "HMAC-SHA1";
private static final String OAUTH_TIMESTAMP = "oauth_timestamp";

private static final String OAUTH_VERSION = "oauth_version";
private static final String OAUTH_VERSION_VALUE = "1.0";

private final String consumerKey;
private final String consumerSecret;

private final Random random;
private BasicAuthInterceptor(String consumerKey, String consumerSecret, Random random) {
    this.consumerKey = consumerKey;
    this.consumerSecret = consumerSecret;

    this.random = random;
}
@Override
public Response intercept(Chain chain) throws IOException {
    return chain.proceed(signRequest(chain.request()));
}

public Request signRequest(Request request) throws IOException {
    byte[] nonce = new byte[32];
    random.nextBytes(nonce);
    String oauthNonce = ByteString.of(nonce).base64().replaceAll("\W", "");
    String oauthTimestamp = String.valueOf(System.currentTimeMillis());

    String consumerKeyValue = ESCAPER.escape(consumerKey);


    SortedMap<String, String> parameters = new TreeMap<>();
    parameters.put(OAUTH_CONSUMER_KEY, consumerKeyValue);

    parameters.put(OAUTH_NONCE, oauthNonce);
    parameters.put(OAUTH_TIMESTAMP, oauthTimestamp);
    parameters.put(OAUTH_SIGNATURE_METHOD, OAUTH_SIGNATURE_METHOD_VALUE);
    parameters.put(OAUTH_VERSION, OAUTH_VERSION_VALUE);

    HttpUrl url = request.url();
    for (int i = 0; i < url.querySize(); i++) {
        parameters.put(ESCAPER.escape(url.queryParameterName(i)),
                ESCAPER.escape(url.queryParameterValue(i)));
    }


    RequestBody requestBody = request.body();
    Buffer body = new Buffer();

    if (requestBody != null) {
        requestBody.writeTo(body);
    }

    while (!body.exhausted()) {
        long keyEnd = body.indexOf((byte) '=');
        if (keyEnd == -1) throw new IllegalStateException("Key with no value: " + body.readUtf8());
        String key = body.readUtf8(keyEnd);
        body.skip(1); // Equals.

        long valueEnd = body.indexOf((byte) '&');
        String value = valueEnd == -1 ? body.readUtf8() : body.readUtf8(valueEnd);
        if (valueEnd != -1) body.skip(1); // Ampersand.

        parameters.put(key, value);
    }

    Buffer base = new Buffer();
    String method = request.method();
    base.writeUtf8(method);
    base.writeByte('&');
    base.writeUtf8(ESCAPER.escape(request.url().newBuilder().query(null).build().toString()));
    base.writeByte('&');

    boolean first = true;
    for (Map.Entry<String, String> entry : parameters.entrySet()) {
        if (!first) base.writeUtf8(ESCAPER.escape("&"));
        first = false;
        base.writeUtf8(ESCAPER.escape(entry.getKey()));
        base.writeUtf8(ESCAPER.escape("="));
        base.writeUtf8(ESCAPER.escape(entry.getValue()));
    }

    String signingKey =
            ESCAPER.escape(consumerSecret);// + "&" + ESCAPER.escape(accessSecret);

    SecretKeySpec keySpec = new SecretKeySpec(signingKey.getBytes(), "HmacSHA1");
    Mac mac;
    try {
        mac = Mac.getInstance("HmacSHA1");
        mac.init(keySpec);
    } catch (NoSuchAlgorithmException | InvalidKeyException e) {
        throw new IllegalStateException(e);
    }
    byte[] result = mac.doFinal(base.readByteArray());
    String signature = ByteString.of(result).base64();

    String authorization = "OAuth "
            + OAUTH_CONSUMER_KEY + "=\"" + consumerKeyValue + "\", "
            + OAUTH_NONCE + "=\"" + oauthNonce + "\", "
            + OAUTH_SIGNATURE + "=\"" + ESCAPER.escape(signature) + "\", "
            + OAUTH_SIGNATURE_METHOD + "=\"" + OAUTH_SIGNATURE_METHOD_VALUE + "\", "
            + OAUTH_TIMESTAMP + "=\"" + oauthTimestamp + "\", "

            + OAUTH_VERSION + "=\"" + OAUTH_VERSION_VALUE + "\"";

    Log.d("message","--"+authorization);


    return request.newBuilder()
            .addHeader("Authorization", authorization)
    .addHeader("Content-Type", "application/json;charset=UTF-8")
    .addHeader("Accept", "application/json;versions=1")
            .build();
}

public static final class Builder {
    private String consumerKey;
    private String consumerSecret;

    private Random random = new SecureRandom();


    public Builder consumerKey(String consumerKey) {
        if (consumerKey == null) throw new NullPointerException("consumerKey = null");
        this.consumerKey = consumerKey;
        return this;
    }

    public Builder consumerSecret(String consumerSecret) {
        if (consumerSecret == null) throw new NullPointerException("consumerSecret = null");
        this.consumerSecret = consumerSecret;
        return this;
    }



    public Builder random(Random random) {
        if (random == null) throw new NullPointerException("random == null");
        this.random = random;
        return this;
    }



    public BasicAuthInterceptor build() {
        if (consumerKey == null) throw new IllegalStateException("consumerKey not set");
        if (consumerSecret == null) throw new IllegalStateException("consumerSecret not set");

    }
}
}

Remote Api Call

public final class RemoteApiCalls {
private static final String TAG = "RemoteApiCalls";

public static final class Builder {
    RemoteRetrofitInterfaces mService;
    Retrofit mRetrofit;

    public Builder remoteApiCall(String url,Context mContext) {
        return remoteApiCall(mContext,url, 40, 40, 40);
    }

    BasicAuthInterceptor oauth1 = new BasicAuthInterceptor.Builder()
            .consumerKey("keyhere")//i have added keys
            .consumerSecret("secert here")//i have added secertkeys
            .build();

    public Builder remoteApiCall(Context mContext, String url, int connectionTimeout, int readTimeout, int writeTimeout) {

         HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(20, TimeUnit.SECONDS)
                .writeTimeout(20, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS).addInterceptor(interceptor).addInterceptor(oauth1)
                .build();

        mRetrofit = new Retrofit.Builder()
                .baseUrl(url).addConverterFactory(GsonConverterFactory.create())
                .client(client)
                .build();
        mService = mRetrofit.create(RemoteRetrofitInterfaces.class);


        return this;
    }



    public void getProductCatogry()

    {

        Call<ProductCategoryResponse> mApiCall = mService.getListCategory();
        mApiCall.enqueue(new Callback<ProductCategoryResponse>() {
            @Override
            public void onResponse(Call<ProductCategoryResponse> call, Response<ProductCategoryResponse> response) {

                if (response.isSuccessful()) {


                } else {

                }
            }

            @Override
            public void onFailure(Call<ProductCategoryResponse> call, Throwable t) {
t.printStackTrace();
            }


        });

    }

}

}

RemoteRetrofitInterfaces.java

public interface RemoteRetrofitInterfaces {

@GET("products")
Call<ProductCategoryResponse> getListCategory();
}

in Main Activity I call

 new RemoteApiCalls.Builder().remoteApiCall("http://mywebsite.com/wp-json/wc/v1/",getApplicationContext()).getProductCatogry();

still am getting the 401 error

{"code":"woocommerce_rest_cannot_view","message":"Sorry, you cannot list resources.","data":{"status":401}}

Woo commerce version used is Version 2.6.4
APi Version is v1

can any one help me to fix this issue I want to implement this with retrofit itself.

How to&Answers:

Finally i found the solution hope this will help some other

i go through various documents

1)Using the WooCommerce REST API – Introduction

2)woocommerce-rest-api-docs

3)Scribe

4)scribe:1.3.5

After referring above documents and Source codes finally i created a library which do the OAuth 1.0a “one-legged” authentication for woocommerce HTTP android

The full description has added in the read me section of my library

Check The Library Here

WoocommerceAndroidOAuth1 LIBRARY