How to Get Access Token After User Is Signed in from Gmail in Android

How to get access token after user is signed in from Gmail in Android?

For your requirements, you can use the following code:

Firstly, make sure you have a valid Web OAuth 2.0 Client ID:

<!-- Server Client ID.  This should be a valid Web OAuth 2.0 Client ID obtained
from https://console.developers.google.com/ -->
<string name="server_client_id">...e4p8.apps.googleusercontent.com</string>

Then inside Activity class:

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

...

// For sample only: make sure there is a valid server client ID.
validateServerClientID();

// [START configure_signin]
// Configure sign-in to request offline access to the user's ID, basic
// profile, and Google Drive. The first time you request a code you will
// be able to exchange it for an access token and refresh token, which
// you should store. In subsequent calls, the code will only result in
// an access token. By asking for profile access (through
// DEFAULT_SIGN_IN) you will also get an ID Token as a result of the
// code exchange.
String serverClientId = getString(R.string.server_client_id);
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(new Scope(Scopes.DRIVE_APPFOLDER))
.requestServerAuthCode(serverClientId)
.requestEmail()
.build();
// [END configure_signin]

// Build GoogleAPIClient with the Google Sign-In API and the above options.
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
}

private void getAuthCode() {
// Start the retrieval process for a server auth code. If requested, ask for a refresh
// token. Otherwise, only get an access token if a refresh token has been previously
// retrieved. Getting a new access token for an existing grant does not require
// user consent.
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
startActivityForResult(signInIntent, RC_GET_AUTH_CODE);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if (requestCode == RC_GET_AUTH_CODE) {
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
Log.d(TAG, "onActivityResult:GET_AUTH_CODE:success:" + result.getStatus().isSuccess());

if (result.isSuccess()) {
// [START get_auth_code]
GoogleSignInAccount acct = result.getSignInAccount();
String authCode = acct.getServerAuthCode();

// Show signed-in UI.
mAuthCodeTextView.setText(getString(R.string.auth_code_fmt, authCode));
updateUI(true);

// TODO(user): send code to server and exchange for access/refresh/ID tokens.
// [END get_auth_code]
} else {
// Show signed-out UI.
updateUI(false);
}
}
}

You can see the entire code at the following ServerAuthCodeActivity.java

The result, if you use that sample, looks like the following screenshot:

BNK's screenshot

Then, you can follow the steps mentioned at the Google's documentation below (from step #3. Send the auth code to your app's backend using HTTPS POST):

Google Sign-In for Android - Enabling Server-Side Access


UPDATE: from the comments, if you want to get access token directly from android client app, please use the following sample code (replaced with your client_id, client_secret and the auth code)

OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new FormEncodingBuilder()
.add("grant_type", "authorization_code")
.add("client_id", "812741506391-h38jh0j4fv0ce1krdkiq0hfvt6n5amrf.apps.googleusercontent.com")
.add("client_secret", "{clientSecret}")
.add("redirect_uri","")
.add("code", "4/4-GMMhmHCXhWEzkobqIHGG_EnNYYsAkukHspeYUk9E8")
.build();
final Request request = new Request.Builder()
.url("https://www.googleapis.com/oauth2/v4/token")
.post(requestBody)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(final Request request, final IOException e) {
Log.e(LOG_TAG, e.toString());
}

@Override
public void onResponse(Response response) throws IOException {
try {
JSONObject jsonObject = new JSONObject(response.body().string());
final String message = jsonObject.toString(5);
Log.i(LOG_TAG, message);
} catch (JSONException e) {
e.printStackTrace();
}
}
});

Please use compile 'com.squareup.okhttp:okhttp:2.6.0' (ver 3-RC1 will have different classes)

With a sucessful response, you will have the following info in logcat:

I/onResponse: {
"expires_in": 3600,
"token_type": "Bearer",
"refresh_token": "1\/xz1eb0XU3....nxoALEVQ",
"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjQxMWY1Ym......yWVsUA",
"access_token": "ya29.bQKKYah-........_tkt980_qAGIo9yeWEG4"
}

Get Google Refresh token from existing user auth code

You need to call

            .requestServerAuthCode(getClientId(),true)

instead of

            .requestServerAuthCode(getClientId())

If you want to get a refresh token. The javascript code you posted works with refresh tokens.

This is the easiest way to authenticate your app because all other kinds of token will expire. Refresh tokens dont expire. The Google Sign in library gives you an access code which you are already using to obtain a refresh token. You need to save this refresh token and send it to Google in order to obtain access tokens. The access token can then finally be used to make API requests.

By the way, that is actually what 'offline access' refers to. Took me a while to figure this out as well.

Retrieve Google Access Token after authenticated using Firebase Authentication

The way you are doing will give you firebase id token, see here.


There are three types of tokens you will come across in firebase :

  • Firebase ID tokens

    Created by Firebase when a user signs in to a Firebase app. These tokens are signed JWTs that securely identify a user in a Firebase project. These tokens contain basic profile information for a user, including the user's ID string, which is unique to the Firebase project. Because the integrity of ID tokens can be verified, you can send them to a backend server to identify the currently signed-in user.

  • Identity Provider tokens

    Created by federated identity providers, such as Google and Facebook. These tokens can have different formats, but are often OAuth 2.0 access tokens. Firebase apps use these tokens to verify that users have successfully authenticated with the identity provider, and then convert them into credentials usable by Firebase services.

  • Firebase Custom tokens

    Created by your custom auth system to allow users to sign in to a Firebase app using your auth system. Custom tokens are JWTs signed using a service account's private key. Firebase apps use these tokens much like they use the tokens returned from federated identity providers.


Now, what you are getting is firebase Id token, what you need is Identity Provider Token.

Its simple to get Identity Provider token, its just one step previous to the step you have shown.

So, the way we sign in google using firebase is mentioned here.

I will add below the complete code which displays a button in UI, which on clicked, will sign in user to google account. And then I will get the google access token, which is then send to firebase, where it is converted to firebase token Id.

I presume that you have configured android app for google sign in, if not, you can go into details here.


(To cut things short, just look at Step 5 below, if you have done setup already.)

Code :

  1. Configure Google SignIn and GoogleApiClient :

     // Configure sign-in to request the user's ID, email address, and basic
    // profile. ID and basic profile are included in DEFAULT_SIGN_IN.
    GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
    .requestIdToken(getString(R.string.default_web_client_id))
    .requestEmail()
    .build();

    // NOTE :
    // The string passed to requestIdToken, default_web_client_id,
    // can be obtained from credentials page (https://console.developers.google.com/apis/credentials).
    // There mentioned Web application type client ID is this string.

    // ...
    // Build a GoogleApiClient with access to the Google Sign-In API and the
    // options specified by gso.
    mGoogleApiClient = new GoogleApiClient.Builder(this)
    .enableAutoManage(this /* Activity */, this /* OnConnectionFailedListener */)
    .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
    .build();
  2. Add the Google Sign-In button to your app

    <com.google.android.gms.common.SignInButton
    android:id="@+id/sign_in_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
  3. Set SignIn Click Listener

    findViewById(R.id.sign_in_button).setOnClickListener(new OnClickListener() {
    public void onClick(View v){
    Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
    startActivityForResult(signInIntent, RC_SIGN_IN);
    }
    });
  4. Override OnActivityResult method in Activity :

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...);
    if (requestCode == RC_SIGN_IN) {
    // Google Sign In was successful, authenticate with Firebase
    GoogleSignInAccount account = result.getSignInAccount();
    firebaseAuthWithGoogle(account); // This method is implemented in step 5.
    } else {
    // Google Sign In failed, update UI appropriately
    // ...
    }
    }
  5. Firebase Authentication With Google SignInAccount

    String idTokenString = "";
    ...
    private void firebaseAuthWithGoogle(GoogleSignInAccount acct) {
    Log.d(TAG, "Google User Id :" + acct.getId());

    // --------------------------------- //
    // BELOW LINE GIVES YOU JSON WEB TOKEN, (USED TO GET ACCESS TOKEN) :
    Log.d(TAG, "Google JWT : " + acct.getIdToken());
    // --------------------------------- //

    // Save this JWT in global String :
    idTokenString = acct.getIdToken();

    AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
    mAuth.signInWithCredential(credential)
    .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
    @Override
    public void onComplete(@NonNull Task<AuthResult> task) {
    Log.d(TAG, "signInWithCredential:onComplete:" + task.isSuccessful());

    if(task.isSuccessful()){
    // --------------------------------- //
    // BELOW LINE GIVES YOU FIREBASE TOKEN ID :
    Log.d(TAG, "Firebase User Access Token : " + task.getResult().getToken());
    // --------------------------------- //
    }
    // If sign in fails, display a message to the user. If sign in succeeds
    // the auth state listener will be notified and logic to handle the
    // signed in user can be handled in the listener.
    else {
    Log.w(TAG, "signInWithCredential", task.getException());
    Toast.makeText(GoogleSignInActivity.this, "Authentication failed.",
    Toast.LENGTH_SHORT).show();
    }
    }
    });
    }
  6. Final Step : Auth Listeners for Firebase

    private FirebaseAuth mAuth;
    private FirebaseAuth.AuthStateListener mAuthListener;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    // ...
    mAuth = FirebaseAuth.getInstance();
    mAuthListener = new FirebaseAuth.AuthStateListener() {
    @Override
    public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
    FirebaseUser user = firebaseAuth.getCurrentUser();
    if (user != null) {
    // User is signed in
    Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
    } else {
    // User is signed out
    Log.d(TAG, "onAuthStateChanged:signed_out");
    }
    // ...
    }
    };
    // ...
    }

    //...

    @Override
    public void onStart() {
    super.onStart();
    mAuth.addAuthStateListener(mAuthListener);
    }

    @Override
    public void onStop() {
    super.onStop();
    if (mAuthListener != null) {
    mAuth.removeAuthStateListener(mAuthListener);
    }
    }

So, your answer lies in Step 5, which was just before you authenticated to firebase and just after you authenticated in google sign in.

Hope it helps !


UPDATE :

Its important that in Step 1, you request for token Id, otherwise in Step 5, you will get null token id. For more see here. I have updated Step 1.


UPDATE :

As per discussion, the retrieved token was JWT token as written here. And what we need is google access token. Below code uses the JWT token to fire at OAuth backend and retrieve this access token :

(Note : I have used okhttp version 2.6.0, other versions might have different methods)

Code :

...
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new FormEncodingBuilder()
.add("grant_type", "authorization_code")
.add("client_id", "<Your-client-id>") // something like : ...apps.googleusercontent.com
.add("client_secret", "{Your-client-secret}")
.add("redirect_uri","")
.add("code", "4/4-GMMhmHCXhWEzkobqIHGG_EnNYYsAkukHspeYUk9E8") // device code.
.add("id_token", idTokenString) // This is what we received in Step 5, the jwt token.
.build();

final Request request = new Request.Builder()
.url("https://www.googleapis.com/oauth2/v4/token")
.post(requestBody)
.build();

client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(final Request request, final IOException e) {
Log.e(LOG_TAG, e.toString());
}

@Override
public void onResponse(Response response) throws IOException {
try {
JSONObject jsonObject = new JSONObject(response.body().string());
final String message = jsonObject.toString(5);
Log.i(LOG_TAG, message);
} catch (JSONException e) {
e.printStackTrace();
}
}
});

Here is the output which has access token as required :

I/onResponse: {
"expires_in": 3600,
"token_type": "Bearer",
"refresh_token": "1\/xz1eb0XU3....nxoALEVQ",
"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjQxMWY1Ym......yWVsUA",
"access_token": "ya29.bQKKYah-........_tkt980_qAGIo9yeWEG4"
}

Hope now it helps !

Get Google signed user information

If someone else need to implements something alike, I'll share how I did it.
There is a silentSignIn from Google SDK which allows to do it.

So, this is the code:

val signInOptions = getSignInOptions(this)
val googleSignInClient = GoogleSignIn.getClient(this, signInOptions)
googleSignInClient.silentSignIn().addOnCompleteListener {
val account: GoogleSignInAccount? = it.result
if (account != null) {
// success
}
else {
// failure
}
}


Related Topics



Leave a reply



Submit