Creating a Secure Login on Android using Google OAuth

PubNub Developer Relations - Mar 11 - - Dev Community

With an estimated 4.55 billion people having at least one social media profile as of January 2022, it comes as no surprise that a majority of applications have some sort of social sign-in as an easy way for their users to get onboard. This is key in enhancing the user experience. From a development perspective, there is a major commonality between all these social sign-in providers, like Facebook, Twitter, and Google. That common puzzle piece is OAuth, more specifically OAuth 2.0. But it's not just about using OAuth, it requires the correct understanding of scopes, OAuth Client ID, and the OAuth consent screen for optimal implementation.

What is the difference between Authentication and Authorization?

Authentication (authn) refers to the process of identifying or verifying the user, in this context, it can be done via Google OAuth. On the other hand, authorization (authz) concerns whether the authenticated user has the _permissions needed_ to access a particular resource. This is where scopes play a crucial role as they define the permissions in OAuth.

Both authn and authz are tightly related and typically are found within the OAuth 2.0 process. For both parts of the process, we’re going to need a server to keep our client ID and client secret, which can be managed using Google Cloud.

How Authn and Authz fit into OAuth

OAuth is strictly the standard for authorization, a major misconception among developers is that authn (authentication) is a part of it. OAuth 2.0 also incorporates authn, especially on social platforms like Google, where the processes are run in succession using a combination of native technologies, Javascript, or Kotlin.

Here is an overview of the process:

As you can see, there are two primary components. Initially, we need to have the required authorization from the user. This happens if the user is signed in, this is where a lot of APIs will tie in the authentication piece of OAuth using openid. Once we get the grant from the user account authority, we can start communicating with the resource owner. Upon sending the grant to the authorization server, we receive an access token back. Now, with the access token, we can access the information we need related to the user.

When implementing the OAuth 2.0, it is crucial to set up the correct redirect URIs and callback as any minor mistake in these parameters can result in failure of the authn and authz process. Furthermore, as part of enhancing security and user experience, Google provides the chrome extension for OAuth 2.0.

Authenticating with Google Sign-in on Android

Authenticating applications for Android, particularly with Google sign-in, is a common security measure. But it's not just about adding Google to your Android application, it begins with understanding the OAuth, and then digging into the SDK and Google API.

To get started with Google Sign-in, we'll use Functions, a feature of PubNub which serves as our server. These functions will make the implementation of notifications in the OAuth process simpler. The code snippets for the OAuth process can also be found on GitHub.

In addition, ensure you have your Google API account set up with the correct package name and have your OAuth Client ID and OAuth Consent Screen configured.

Next, we're going to add Google to our Android application so that we can start the sign-in process. First, ensure you have the line repositories { google() } in your project-level Gradle file. This will open up your application to leverage the Google SDK.

Then, head over to the app level Gradle file and update the Google Play services auth dependency. As of January 2022, the most recent version is com.google.android.gms:play-services-auth:19.2.0.

    implementation 'com.google.android.gms:play-services-auth:19.2.0'
Enter fullscreen mode Exit fullscreen mode

Now, even though we have the updated dependency added to our project, we won't be able to go through the sign-in process just yet. You need to edit the Android manifest file to add the Internet permissions concerning Firebase:

    <uses-permission android:name="android.permission.INTERNET" />
Enter fullscreen mode Exit fullscreen mode

Adding the Google Sign-in Button

This stage is fairly straightforward. Add the sign-in component to the activity_main.xml file. The sign-in button is styled by Google, but check out Google’s brand guidance just in case. Remember, good user experience is key in desktop apps and this includes the stylings and identifiers used on the sign-in button.

    <com.google.android.gms.common.SignInButton
                android:id="@+id/sign_in_button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="8dp"
                app:layout_constraintBottom_toBottomOf="parent" android:layout_marginTop="8dp"
                app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"
                android:layout_marginStart="8dp" app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="8dp"
                app:layout_constraintVertical_bias="0.665"
    />
Enter fullscreen mode Exit fullscreen mode

In Android apps, authenticating with Google Sign-in is a common security measure. Here, you can see an example of how this process works in the MainActivity. This snippet serves as a guide for implementing Google sign-in using Kotlin.

class MainActivity : AppCompatActivity() {
    private val RC_SIGN_IN = 9001
    private var mGoogleSignInClient: GoogleSignInClient? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestIdToken(getString(R.string.server_client_id))
            .requestEmail()
            .build()
        mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
    }
    public override fun onStart() {
        super.onStart()
        val mGmailSignIn = findViewById<SignInButton>(R.id.sign_in_button)
        val account = GoogleSignIn.getLastSignedInAccount(this)
        Log.w("Sign In: ", account.toString())
        mGmailSignIn.setOnClickListener {
                signIn()
        }
    }
    public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == RC_SIGN_IN) {
            val task = GoogleSignIn.getSignedInAccountFromIntent(data)
            handleSignInResult(task)
        }
    }
    private fun handleSignInResult(completedTask: Task<GoogleSignInAccount>) {
        Log.w("Sign In: ", completedTask.toString())
        try {
            val account = completedTask.getResult(ApiException::class.java)
            val idToken = account!!.idToken
            Log.w("Sign In: ", idToken.toString())
            authCheck(idToken.toString())
        } catch (e: ApiException) {
            Log.w("Sign In: ", "signInResult:failed code=" + e.statusCode)
        }
    }
    private fun signIn() {
        val signInIntent = mGoogleSignInClient!!.signInIntent
        startActivityForResult(signInIntent, RC_SIGN_IN)
    }
    private fun authCheck(token: String) {
        // Instantiate the RequestQueue.
        val queue = newRequestQueue(this)
        val url = "https://pubsub.pubnub.com/v1/blocks/sub-key/sub-c-87dbd99c-e470-11e8-8d80-3ee0fe19ec50/google?token=$token"
        // Request a string response from the provided URL.
        val stringRequest = StringRequest(
            Request.Method.GET, url,
            Response.Listener<String> { response ->
                // Display the first 500 characters of the response string.
                Log.d("Response: ", response)
            },
            Response.ErrorListener {Log.d("Response: ", "Didn't Work!")})
        // Add the request to the RequestQueue.
        queue.add(stringRequest)
    }
}
Enter fullscreen mode Exit fullscreen mode

With the above code, your app is now capable of authenticating and verifying a user! After successful authentication, the next step involves retrieving the Access token. This process is quite similar to the previous one, but this time, you'll submit a HTTP POST request to the token URL – https://www.googleapis.com/oauth2/v4/token. This can be done using JavaScript and the code can be found on GitHub for reference.

Your PubNub Function might resemble the following:

export default (request, response) => {
    const pubnub = require('pubnub');
    const xhr = require('xhr');
    let headersObject = request.headers;
    let paramsObject = request.params;
    let methodString = request.method;
    let bodyString = request.body;
    // Set the status code - by default it would return 200
    response.status = 200;
    // Set the headers the way you like

    var client_id = "your google client id"
    var client_secret = "your google client secret";
    var redirect = "https://pubsub.pubnub.com/v1/blocks/sub-key/sub-c-87dbd99c-e470-11e8-8d80-3ee0fe19ec50/Auth";

    response.headers['X-Custom-Header'] = 'CustomHeaderValue';
    var dataString = `client_id=${client_id}&client_secret=${client_secret}&redirect_uri=${redirect}&grant_type=authorization_code&code=CODE`;
    var url = `https://www.googleapis.com/oauth2/v4/token${dataString}`;
    var options = {
        "method": 'POST',
        "body": dataString,
    };

    console.log(dataString);
    return xhr.fetch(url, options).then((x) => {
        const body = JSON.parse(x.body);
        console.log(body);
         return response.send(body);
    }).catch((err) => {
        // console.log(err)
        return response.send("Malformed JSON body.");
    });
};
Enter fullscreen mode Exit fullscreen mode

Remember, you need to obtain the authorization code from your Android end-user which you could accomplish by adding this code to your MainActivity.kt file:

Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
try {
    GoogleSignInAccount account = task.getResult(ApiException.class);
    String authCode = account.getServerAuthCode();
    // Show signed-un UI
    updateUI(account);
    // TODO(developer): send code to server and exchange for access/refresh/ID tokens
} catch (ApiException e) {
    Log.w(TAG, "Sign-in failed", e);
    updateUI(null);
}
Enter fullscreen mode Exit fullscreen mode

And with that, you are well on your way to implementing Google sign-in using OAuth 2.0 in your Android app, keeping the user experience smooth and the app secure.

After testing your application with the open internet, you should be able to navigate through the entire sign-up process without any issues. This process might involve Google OAuth for authentication, which comes with a variety of scopes, or permissions, that can be set for your app. Remember, the user experience should be seamless.

Now that you've gone through all the steps, you should have a better understanding of the difference between OAuth client ID-based authentication and authorization. Building on this knowledge, you can start exploring what you can do after a user signs in through the OAuth consent screen.

The package name in your client-side SDK, be it JavaScript, Kotlin, or native to a different platform, should match the one registered on your Google Cloud project. If any external content isn't accessible on this page, you can also view it at https://pubsub-quickstart-app.pubnub.com/signup. This page should demonstrate the correct usage of the redirect URIs and callback parameters required for OAuth.

In need of PubNub's assistance?

Our platform, offers developers the resources to build, deliver, and manage real-time interactivity for web apps, mobile apps, desktop apps, and IoT devices. Engage users with notifications or create private Firebase applications with unique identifiers for advanced interactions.

The backbone of our platform is the industry's largest and most scalable real-time edge messaging network. Powered by Google Cloud services and integrated with Chrome for additional capability, it's complete with over 15 points-of-presence worldwide, supporting 800 million monthly active users, and a reliability of 99.999%, you won't have to worry about outages, concurrency limits, or latency issues due to traffic spikes.

How to start with PubNub

Familiarize yourself with the core concepts behind every PubNub-powered app in less than 5 minutes by checking out the Live Tour. OpenID can be an important part of these concepts. Sign up for a PubNub account for instant access to PubNub keys for free. The PubNub docs will guide you through the installation and setup process, including the code snippets or "snippets" with detailed parameters, regardless of your use case or preferred SDK.

Contents

What is the difference between Authentication and Authorization?How Authn and Authz fit into OAuthAuthenticating with Google Sign-in on AndroidAdding the Google Sign-in ButtonIn need of PubNub's assistance?How to start with PubNub

How can PubNub help you?

This article was originally published on PubNub.com

Our platform helps developers build, deliver, and manage real-time interactivity for web apps, mobile apps, and IoT devices.

The foundation of our platform is the industry's largest and most scalable real-time edge messaging network. With over 15 points-of-presence worldwide supporting 800 million monthly active users, and 99.999% reliability, you'll never have to worry about outages, concurrency limits, or any latency issues caused by traffic spikes.

Experience PubNub

Check out Live Tour to understand the essential concepts behind every PubNub-powered app in less than 5 minutes

Get Setup

Sign up for a PubNub account for immediate access to PubNub keys for free

Get Started

The PubNub docs will get you up and running, regardless of your use case or SDK

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .