1

I am working on an android app that connects to Google Books API directly. Part of its working is to fetch user's private book data as well. (such as bookshelves)

I used Google Sign-in for Android for authentication. However, i needed an access token for the request's authorization as well.

THE PROBLEM:-

Google Sign-in does a great job handling the authentication part but i was stuck in implementing the authorization part since it provided no methods for it. Following is the working solution i came up with for authorization.

Q: Is my current solution (given below) okay (if not the preferred way) for getting an access token ?

CURRENT SOLUTION:-

Firstly, I requested the authorization code by using the requestServerAuthCode(...) method and passed the web app client id (which was auto created for Google Sign in by Google API console):-

...
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestScopes(new Scope(SCOPE_BOOKS))
                .requestServerAuthCode(WEB_APP_CLIENT_ID)  //NOTE: this was auto generated for Google Sign-in along with my android client id.
                .requestEmail()
                .build();
...

Then, i used the received auth token (by calling getServerAuthCode()) and used it to get the access token manually,

MainActivity.java

...
Task<GoogleSignInAccount> accountTask = GoogleSignIn.getSignedInAccountFromIntent(data);
GoogleSignInAccount account = accountTask.getResult(ApiException.class);
new TokenTask().execute(account.getServerAuthCode());
...

TokenTask.java

@Override
    protected String doInBackground(String... authToken) { 

            //Time to get the access token from the authToken 
            ...
            final URL url = new URL("https://www.googleapis.com/oauth2/v4/token");

            conn = (HttpsURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            ...

            final StringBuilder b = new StringBuilder();
            b.append("code=").append(authToken[0]).append('&')
                    .append("client_id=").append(WEB_APP_CLIENT_ID).append('&')   //NOTE: this client id was auto generated for Google Sign-in along with my android client id.
                    .append("client_secret=").append(WEB_APP_CLIENT_SECRET).append('&')
                    .append("redirect_uri=").append("").append('&')
                    .append("grant_type=").append("authorization_code");

            final byte[] postData = b.toString().getBytes(StandardCharsets.UTF_8);

            os = conn.getOutputStream();
            os.write(postData);
/*
The response contains fields such as: access_token, expires_in, refresh_token etc...
*/


            final int responseCode = conn.getResponseCode();
            if (200 <= responseCode && responseCode <= 299) {
                is = conn.getInputStream();
                isr = new InputStreamReader(is);
                br = new BufferedReader(isr);
            } else {
                Log.d("Error:", conn.getResponseMessage());
                return null;
            }

            b.setLength(0);
            String output;
            while ((output = br.readLine()) != null) {
                b.append(output);
            }

            final JSONObject jsonResponse = new JSONObject(b.toString());
            String mAccessToken = jsonResponse.getString("access_token");

NOTE:-

I got the idea about this solution from here. Although the web client id passed into requestServerAuthCode(...) is supposed to be our server side app's client id. but since i don't have any web app, i am using the web client id auto-generated by the google api console for google sign-in (declared as WEB_APP_CLIENT_ID in the above code).

syed_noorullah
  • 155
  • 2
  • 13
  • 1
    it's not the preferred way to include the client_secret in you mobile or web app, see [this](https://auth0.com/docs/applications/concepts/client-secret). Also google policy [here](https://developers.google.com/identity/protocols/oauth2/policies#secure-credentials). – Bertrand Martel Jun 14 '20 at 01:58
  • 1
    [this post](https://stackoverflow.com/questions/1934187/oauth-secrets-in-mobile-apps/1934234#1934234) is about storing secrets (generally speaking) on mobile apps for oauth – Bertrand Martel Jun 14 '20 at 02:00
  • 1
    @BertrandMartel - thanks for the reply mate. its really weird how google says not to include client credentials while on the same guide they themselves have:- https://developers.google.com/identity/protocols/oauth2/native-app#exchange-authorization-code – syed_noorullah Jun 14 '20 at 02:08
  • 1
    It seems, google states that application should store the `tokens` in a secure way, but doesn't tell anything about storing the client_secret – Bertrand Martel Jun 14 '20 at 02:17
  • 1
    while they assume at the beginning : `Installed apps are distributed to individual devices, and it is assumed that these apps cannot keep secrets` – Bertrand Martel Jun 14 '20 at 02:18
  • @BertrandMartel :D yes that is what i was wondering when i first read that they were talking about tokens not client credentials. – syed_noorullah Jun 14 '20 at 02:31
  • @BertrandMartel they do tell about storing client credentials in a secure place like a secret manager (such as google cloud manager). see link : https://developers.google.com/identity/protocols/oauth2/policies#secure-credentials. However, they tell us to "never transmit these credentials as plain text." My question arises there that if i do place my credentials in a secure place instead of my code repository, still I'll need to "decrypt" before transmitting them to the google oauth server and ill have to do that IN MY CODE REPOSITORY :D – syed_noorullah Jun 14 '20 at 02:39
  • 1
    @BertrandMartel so even if my credentials are not in my code repository, my "decryption algorithm" is. – syed_noorullah Jun 14 '20 at 02:39
  • 1
    After reading [Oauth2 for native app](https://tools.ietf.org/html/rfc8252#section-1) I've found nothing about storing credentials. My guess is that they think that PKCE protection for the authorization code is enough. I've found also that okta is also [on the same page](https://developer.okta.com/blog/2018/04/10/oauth-authorization-code-grant-type#when-to-use-the-authorization-code-flow) as long as there is PKCE to protect the code. So I guess this flow is ok from the general guidelines but maybe not the most secure flow in the world – Bertrand Martel Jun 14 '20 at 02:41
  • 1
    @BertrandMartel pardon me sir, since i am a beginner in using oauth. so do i pass – syed_noorullah Jun 14 '20 at 02:47
  • @BertrandMartel i hope you're there for a reply. I found another [method for getting an access token directly](https://developers.google.com/android/reference/com/google/android/gms/auth/GoogleAuthUtil#public-static-string-gettoken-context-context,-account-account,-string-scope,-bundle-extras) from a google api. But it is mentioned there about some "better approaches" which i couldn't understand. Is this method fit for my above use case ? – syed_noorullah Jun 25 '20 at 10:53

1 Answers1

-1

You should use Authorization Code Flow (PKCE) in your mobile app these days, as recommended by Google. Rather than a client secret you provide a code_challenge while signing in and a code_verifier when swapping the authorization code for a token.

Out of interest, my Android Blog Post shows some example messages with the above parameters. It also has a code sample you can run and some stuff on user experience + token storage.

Gary Archer
  • 22,534
  • 2
  • 12
  • 24
  • 1
    According to the following google guide:- https://developers.google.com/identity/protocols/oauth2/native-app#exchange-authorization-code We have to provide the client secret along with the code_challenge for exchanging auth code for an access token. – syed_noorullah Jun 14 '20 at 09:58
  • I suspect you can omit the client_secret when supplying a code_verifier - worth trying. Using PKCE means you are not using stronger security than relying on a client secret in a public client. – Gary Archer Jun 14 '20 at 10:27
  • isnt the code_verifier generated BEFORE generating the auth token.?.. in my case, the auth token is generated by the googleSignInClient's method requestServerAuthCode(String client_id)... im only using that for exchanging an access token. – syed_noorullah Jun 14 '20 at 10:33
  • The messages for a mobile app should match steps 8 and 10 from the blog post in my answer above. I would aim to use Google classes in line with those fields. With OAuth tech I always recommend a strong understanding of the messages before writing code. – Gary Archer Jun 14 '20 at 11:01