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.
Go to: WooCommerce > Settings > Advanced > REST API. Note: Keys/Apps was found at WooCommerce > Settings > API > Key/Apps prior to WooCommerce 3.4. Select Add Key. You are taken to the Key Details screen. Add a Description.
OAuth is a token based authorization method which uses an access token for interaction between user and API. OAuth requires several steps and requests against the API to get your access token. Register an app for the API you want to develop. Use the developer sites of the public API you're going to develop for.
Self-hosted running WooCommerce 3.5 or higher, with the Jetpack plugin installed, connected, and activated (more info on Jetpack at Getting Started with Jetpack) use your device to visit the Google Play Store, search for WooCommerce by Automattic, and install the app, or use your device to open this link and get started from there: WooCommerce.app
The My Store page is the default view when launching the WooCommerce Android app. It is one of four main sections, which are: My Store – high-level overview of sales and top performing products for this day, week, month, or year. Orders – search or filter existing orders, or take payment for new ones.
Finally i found the solution hope this will help some other
i go through various documents
1)Using the WooCommerce REST API – Introduction
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
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