Reputation: 155
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).
Upvotes: 1
Views: 694
Reputation: 29208
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.
Upvotes: -1