heremaps / here-aaa-java-sdk

HERE Authentication, Authorization, and Accounting Java Client library.
Apache License 2.0
27 stars 27 forks source link

Any interest in making it Android friendly? #55

Open boriguen opened 5 years ago

boriguen commented 5 years ago

Hi team,

I'm trying to use this library in an Android project with minSdkVersion 24. Here is a quick recap of the problems I face:

First, in order to avoid build issues with Apache httpcomponents, I had to exclude things as follow: implementation('com.here.account:here-oauth-client:0.4.16') { exclude group: 'org.apache.httpcomponents', module: 'httpclient' } And then I ended up using the pure JavaHttpProvider. I naively ran some unit tests and the authentication went through. Then, I ran an instrumentation test and hit the wall mentioned above. Indeed, the library uses java.util.Base64 while Android only provides android.util.Base64, leading to the runtime issue: java.lang.NoClassDefFoundError: Failed resolution of: Ljava/util/Base64. I have found it in OAuth1Signer and SignatureCalculator.

My current workaround to get going is to copy those classes and make the small one-line changes to have them work with Android. I now use the Apache Base64 implementation in module 'commons-codec:commons-codec:1.10'.

I now have class AndroidOAuth1Signer.java:

import org.apache.commons.codec.binary.Base64;
.
.
String nonce = new String(Base64.encodeBase64(bytes)).substring(0, NONCE_LENGTH);

And class AndroidSignatureCalculator.java:

import org.apache.commons.codec.binary.Base64;
.
.
byte[] keyBytes = Base64.decodeBase64(key);
.
.
return new String(Base64.encodeBase64(signedBytes));
.
.
Plus a few more replacements

So I'm using those two classes above via third class ClientAuthorizationRequestProviderFromAndroidProperties.java:

package com.here.account.android;

import com.here.account.auth.OAuth1ClientCredentialsProvider;
import com.here.account.auth.provider.FromProperties;
import com.here.account.http.HttpProvider;
import com.here.account.util.SettableSystemClock;

import java.util.Properties;

/**
 * ClientAuthorizationRequestProviderFromAndroidProperties is the class allowing HERE AAA from given
 * properties.
 *
 * It does override getClientAuthorizer() in order to use classes
 * like AndroidOAuth1Signer and AndroidSignatureCalculator specifically modified for Android usage.
 *
 * @author guenebau
 * @since 1.0.0
 */
public class ClientAuthorizationRequestProviderFromAndroidProperties extends FromProperties {
    /**
     * The OAuth 1 signer created from properties.
     */
    private final AndroidOAuth1Signer mOAuth1Signer;

    /**
     * Class constructor.
     *
     * @param properties the properties containing credentials related values.
     */
    public ClientAuthorizationRequestProviderFromAndroidProperties(Properties properties) {
        super(new SettableSystemClock(), properties);
        mOAuth1Signer = new AndroidOAuth1Signer(
                getClock(),
                properties.getProperty(OAuth1ClientCredentialsProvider.FromProperties.ACCESS_KEY_ID_PROPERTY),
                properties.getProperty(OAuth1ClientCredentialsProvider.FromProperties.ACCESS_KEY_SECRET_PROPERTY));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public HttpProvider.HttpRequestAuthorizer getClientAuthorizer() {
        return mOAuth1Signer;
    }
}

It does work well and I can get authorization from HERE Account.

So please let me know if there is any Android friendly direction you would like to explore.

Thanks,

Boris

kenmccracken commented 5 years ago

Hi Boris,

Thank you for your interest in making here-aaa-java-sdk builds be Android friendly. However, your proposed patch of using apache commons-codec may not be possible. Previous versions of this library used apache commons-codec. This library caused conflicts with dependencies pulled in for certain use cases. So, we had to switch to java.util.Base64 in https://github.com/heremaps/here-aaa-java-sdk/pull/35 .

Does your Android-friendly requirement affect build time or only runtime? One possibility could be to create a Base64 interface in this project, allowing the developer to inject the Base64 implementation of choice, while keeping this project building with and using java.util.Base64 by default. However, for an Android environment, you could inject an alternate implementation of the Base64 interface using the your library of choice (commons-codec or android.util.Base64).

Regards, -Ken

boriguen commented 5 years ago

Hi Ken,

Thanks for the feedback. The issue happens at runtime since I guess, my build classpath includes the Java SDK. I could likely make the classpath stricter.

Anyways, I like the idea of injecting the Base64 implementation of choice at runtime.

Thanks,

Boris

kenmccracken commented 5 years ago

Hi,

What about using Android >= API Level 26, or 8.0? It seems these might support java.util.Base64, according to https://developer.android.com/reference/java/util/Base64

Thanks, -Ken