Android OAuth2 Example using Retrofit

If you happen to use Google APIs in your android project that needs OAuth 2.0 authentication, then here is a blog to help you get started with Android OAuth2 example using Retrofit!

What is OAuth 2.0?

Android OAuth2
Android OAuth2

OAuth 2.0 is a industry standard authentication framework, that’ll allow your app limited access to user information using token! This token expires after some time period and a new token is demanded whenever your app need access to user information.

Getting Started with Android OAuth2 Project!

In this Android OAuth2 Example blog, we’ve used Google Search Console API in the following example to build an app that allows user to monitor their website performance on Google Search.

Here the app needs to access user’s vital information so OAuth 2.0 level of authentication is mandatory!

Prerequisite

To get started, please create a new Android Studio project with empty Activity!

Now, go to AndroidManifest.xml and add permission to access the internet (it comes inside <manifest> but before starting of <application> tag!) :

<uses-permission android:name="android.permission.INTERNET" />

Now add Retrofit, GSON and GSON Converter library to your project!


    // retrofit, gson
    implementation 'com.google.code.gson:gson:2.8.5'
    implementation 'com.squareup.retrofit2:retrofit:2.4.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.4.0'

Declaring Variables

Go to MainActivity.java and create the following variables:

   //From Google Cloud Console
    private static final String OAUTH_SCOPE = "https://www.googleapis.com/auth/webmasters";
    private static final String CODE = "code";
    static final String CLIENT_ID = "CLIENT_ID_FROM_GOOGLE_CLOUD_CONSOLE";
    private static final String REDIRECT_URI = "com.apppackage.name:/oauth2redirect";

    //Authorization
    static String AUTHORIZATION_CODE;
    private static final String GRANT_TYPE = "authorization_code";

    //Response
    static String Authcode;
    static String Tokentype;
    static String Refreshtoken;
    static Long Expiresin, ExpiryTime;

First we’ve created OAUTH_SCOPE, this variable depends on API for which you seek OAuth2 level of verification. To know more about OAuth2 Scope for Google APIs, see here.

CLIENT_ID is something what you’ll create in Google Cloud Console after you will enable the required API.

Note: CLIENT_SECRET is not needed and generated for Mobile Apps.

CODE variable is needed to pass as a parameter in URL we will define later, this is just “code”.

Also there is one more variable named: REDIRECT_URI, this is needed because once you’ve received ‘authcode’, the app needs to be redirected to the app, for this REDIRECT_URI is needed.

For Browser to recognize your app, add following code in AndroidManifest.xml inside <application> … </application> tag!

    <intent-filter>
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />

                <data android:scheme="com.apppackage.name/>
            </intent-filter>

Note: Replace com.apppackage.name with your app’s package name!

SetOnClickListener() to Begin Process

Now in the empty activity, have a button. In MainActivity.java code this button’s setOnClickListener which creates an intent to open browser with following code:

Read more: Learn to create button with different design style!

   authButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setData(Uri.parse("https://accounts.google.com/o/oauth2/v2/auth" + "?client_id=" + CLIENT_ID + "&response_type=" + CODE + "&redirect_uri=" + REDIRECT_URI + "&scope=" + OAUTH_SCOPE));
              
                if(intent.resolveActivity(getPackageManager()) != null) {
                    startActivity(intent);
                }
            }
        });

In the code above we are creating a browser intent with following url: https://accounts.google.com/o/oauth2/v2/auth and passing CLIENT_ID, CODE, REDIRECT_URI and OAUTH_SCOPE.

Now, once user of your app Authenticates your request, the response is received back from Server. But its not a straight forward process when it coems to OAuth 2.0 Authorization.

Creating required Classes

We’ll first get an Access Token, this will be used to get Authorization Token, Authorization token is the one that’ll be used to access user data. But once authorization token is expired then another refresh token is used to get another authorization token to access user data again!

Feeling overwhelmed? Let’s first create a Class for OAuth Server named OAuthServer.java

import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;

public class OAuthServer {

    private static final String siteURL = "https://www.googleapis.com/";
    private static String code = MainActivity.Authcode;

    public static OAuthServerIntface oAuthServerIntface = null;

    public static OAuthServerIntface getoAuthServerIntface(){

        if(oAuthServerIntface == null) {

            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(siteURL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();

            oAuthServerIntface = retrofit.create(OAuthServerIntface.class);

        }
        return oAuthServerIntface;
    }

    public interface OAuthServerIntface {

        // @Headers("Accept: application/json")

        /**
         * The call to request a token
         */

        @POST("oauth2/v4/token")
        @FormUrlEncoded
        Call<OAuthToken> getAccessToken(
                @Field("code") String code,
                @Field("client_id") String client_id,
                @Field("redirect_uri") String redirect_uri,
                @Field("grant_type") String grant_type
        );

    }


}

To parse response from server, we will create an OAuthToken Model class for parsing response from server!

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class OAuthToken {
    @SerializedName("access_token")
    @Expose
    private String accessToken;
    @SerializedName("expires_in")
    @Expose
    private Long expiresIn;
    @SerializedName("token_type")
    @Expose
    private String tokenType;
    @SerializedName("refresh_token")
    @Expose
    private String refreshToken;

    public String getAccessToken() {
        return accessToken;
    }

    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }

    public Long getExpiresIn() {
        return expiresIn;
    }

    public void setExpiresIn(Long expiresIn) {
        this.expiresIn = expiresIn;
    }

    public String getTokenType() {
        return tokenType;
    }

    public void setTokenType(String tokenType) {
        this.tokenType = tokenType;
    }

    public String getRefreshToken() {
        return refreshToken;
    }

    public void setRefreshToken(String refreshToken) {
        this.refreshToken = refreshToken;
    }
}

Parsing Response in onResume();

Now once response is received from server we will be redirected back to our app, therefore in our MainActivity.java we will use onResume() and parse the response:

@Override
    protected void onResume() {
        super.onResume();

        //Check response not null
        Uri data = getIntent().getData();

        if (data != null && !TextUtils.isEmpty(data.getScheme())){
            String code = data.getQueryParameter(CODE);

            if (!TextUtils.isEmpty(code)) {

                //Success Toast
                Toast.makeText(MainActivity.this, getString(R.string.success),Toast.LENGTH_LONG).show();
                AUTHORIZATION_CODE = code;

                // Using Retrofit builder getting Authorization code
                Retrofit.Builder builder = new Retrofit.Builder()
                        .baseUrl("https://www.googleapis.com/")
                        .addConverterFactory(GsonConverterFactory.create());

                Retrofit retrofit = builder.build();

                OAuthServer.OAuthServerIntface oAuthServerIntface = retrofit.create(OAuthServer.OAuthServerIntface.class);
                final Call<OAuthToken> accessTokenCall = oAuthServerIntface.getAccessToken(
                        AUTHORIZATION_CODE,
                        CLIENT_ID,
                        REDIRECT_URI,
                        GRANT_TYPE
                );

                accessTokenCall.enqueue(new Callback<OAuthToken>() {
                    @Override
                    public void onResponse(Call<OAuthToken> call, Response<OAuthToken> response) {
                        Authcode = response.body().getAccessToken();
                        Tokentype = response.body().getTokenType();
                        Expiresin = response.body().getExpiresIn();
                        Refreshtoken = response.body().getRefreshToken();
                        ExpiryTime = System.currentTimeMillis() + (Expiresin * 1000);

                        saveData();

                        Intent i = new Intent(MainActivity.this,Overview.class);
                        startActivity(i);

                    }

                    @Override
                    public void onFailure(Call<OAuthToken> call, Throwable t) {
                        Toast.makeText(MainActivity.this, getString(R.string.token_error),Toast.LENGTH_LONG).show();

                    }
                });
            }
            if(TextUtils.isEmpty(code)) {
                //a problem occurs, the user reject our granting request or something like that
                Toast.makeText(this, getString(R.string.email),Toast.LENGTH_LONG).show();
                finish();
            }

        }
    }

Saving Codes!

In the method above you’ll notice saveData() method been called, this is used to save data received from server using SharedPreference. Here is the code for this.

public void  saveData(){

        SharedPreferences.Editor sharedPref = getSharedPreferences("authInfo", Context.MODE_PRIVATE).edit();
        sharedPref.putString("AuthCode", AUTHORIZATION_CODE);
        sharedPref.putString("secCode", Authcode);
        sharedPref.putString("refresh", Refreshtoken);
        sharedPref.putLong("expiry", ExpiryTime);
        sharedPref.apply();

    }

You can load the data back whenever you reopen app back using following method:

    public void loadData(){
        SharedPreferences sharedPref = getSharedPreferences("authInfo",Context.MODE_PRIVATE);
        AUTHORIZATION_CODE = sharedPref.getString("AuthCode", "");
        Authcode = sharedPref.getString("secCode", "");
        Refreshtoken = sharedPref.getString("refresh","");
        ExpiryTime = sharedPref.getLong("expiry",0);


    }

Call loadData(); in onCreate(); method. Now that app is successfully Authorized, we can make API calls using Authorization Code.

Generating new Authorization Token

But this Authorization code will expire after one hour of use and to get the new Authorization Code automatically, create a RefreshTokenGen AsyncTask method.


    class RefreshTokenGen extends AsyncTask<String, Void,String> {

        @Override
        protected String doInBackground(String... strings) {
            final MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
            String jsonStr = "client_id=" + CLIENT_ID + "&refresh_token=" + Refreshtoken + "&grant_type=refresh_token";
            OkHttpClient okHttpClient = new OkHttpClient();
            Request request = new Request.Builder()
                    .url("https://www.googleapis.com/oauth2/v4/token")
                    .post(RequestBody.create(mediaType, jsonStr))
                    .build();

            try {
                okhttp3.Response response=okHttpClient.newCall(request).execute();
                assert response.body() != null;
                //Data Received
                return response.body().string();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);

            try {
                JSONObject lJSONObject = new JSONObject(s);
                String ReAccessToken = lJSONObject.getString("access_token");
                String ReExpiresIn = lJSONObject.getString("expires_in");

                Authcode = ReAccessToken;
                Expiresin = Long.parseLong(ReExpiresIn);
                ExpiryTime = System.currentTimeMillis() + (Expiresin * 1000);

                saveData();

                // Toast.makeText(MainActivity.this, "The new Expiry time is: " + ExpiryTime + " and System time is " + System.currentTimeMillis(), Toast.LENGTH_LONG).show();

            } catch (JSONException e) {
                e.printStackTrace();
            }



        }
    }

So now whenever the Authorization Code expires, new RefreshTokenGen().execute(); has to be called.

For this check we will use the following code:

//Get data from SharedPreference
loadData();

if(AUTHORIZATION_CODE == null || AUTHORIZATION_CODE.equals("")) {

// Do nothing, new Login!

} else if (!AUTHORIZATION_CODE.equals("") && System.currentTimeMillis() > ExpiryTime){
// Authorization Code expired! Getting new Code
new RefreshTokenGen().execute();
Toast.makeText(MainActivity.this, getString(R.string.hi),Toast.LENGTH_LONG).show();

new Handler().postDelayed(new Runnable() {
@Override
public void run() {

Intent i = new Intent(MainActivity.this,Overview.class);
startActivity(i);

}
},2000);


} else if (!AUTHORIZATION_CODE.equals("") && System.currentTimeMillis() <= ExpiryTime){

// Authorization Code Not expired
Intent i = new Intent(MainActivity.this,Overview.class);
startActivity(i);

}
}

We received ExpiryTime in response from server, this helps in determining time left with us using System.getCurrentTimeInMills(); method.

Looks Complicated? Here is a library for you: Simple O Auth by
Roh JeongKeun

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.