1
votes

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).

1
it's not the preferred way to include the client_secret in you mobile or web app, see this. Also google policy here.Bertrand Martel
this post is about storing secrets (generally speaking) on mobile apps for oauthBertrand Martel
@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:- developers.google.com/identity/protocols/oauth2/…syed_noorullah
It seems, google states that application should store the tokens in a secure way, but doesn't tell anything about storing the client_secretBertrand Martel
while they assume at the beginning : Installed apps are distributed to individual devices, and it is assumed that these apps cannot keep secretsBertrand Martel

1 Answers

-1
votes

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.