Skip to content

Commit

Permalink
refactor: added accessToken to Android implementation (#173)
Browse files Browse the repository at this point in the history
Co-authored-by: Philipp Heuer <philipp@studysmarter.de>
  • Loading branch information
hoi4 and philipp-stsm committed Jan 28, 2022
1 parent 70fd926 commit 0ed544c
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 18 deletions.
2 changes: 2 additions & 0 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.codetrixstudio.capacitor.GoogleAuth.capacitorgoogleauth">
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package com.codetrixstudio.capacitor.GoogleAuth;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerFuture;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

import androidx.activity.result.ActivityResult;

import com.codetrixstudio.capacitor.GoogleAuth.capacitorgoogleauth.R;
Expand All @@ -18,11 +24,27 @@
import com.google.android.gms.common.api.Scope;
import com.google.android.gms.tasks.Task;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@CapacitorPlugin()
public class GoogleAuth extends Plugin {
private final static String VERIFY_TOKEN_URL = "https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=";
private final static String FIELD_TOKEN_EXPIRES_IN = "expires_in";
private final static String FIELD_ACCESS_TOKEN = "accessToken";
private final static String FIELD_TOKEN_EXPIRES = "expires";
public static final int KAssumeStaleTokenSec = 60;

private GoogleSignInClient googleSignInClient;

@Override
Expand Down Expand Up @@ -55,7 +77,6 @@ public void load() {

@PluginMethod()
public void signIn(PluginCall call) {
saveCall(call);
Intent signInIntent = googleSignInClient.getSignInIntent();
startActivityForResult(call, signInIntent, "signInResult");
}
Expand All @@ -69,22 +90,36 @@ protected void signInResult(PluginCall call, ActivityResult result) {
try {
GoogleSignInAccount account = completedTask.getResult(ApiException.class);

JSObject authentication = new JSObject();
authentication.put("idToken", account.getIdToken());

JSObject user = new JSObject();
user.put("serverAuthCode", account.getServerAuthCode());
user.put("idToken", account.getIdToken());
user.put("authentication", authentication);

user.put("displayName", account.getDisplayName());
user.put("email", account.getEmail());
user.put("familyName", account.getFamilyName());
user.put("givenName", account.getGivenName());
user.put("id", account.getId());
user.put("imageUrl", account.getPhotoUrl());

call.resolve(user);
// The accessToken is retrieved by executing a network request against the Google API, so it needs to run in a thread
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
try {
JSONObject accessTokenObject = getAuthToken(account.getAccount(), true);

JSObject authentication = new JSObject();
authentication.put("idToken", account.getIdToken());
authentication.put(FIELD_ACCESS_TOKEN, accessTokenObject.get(FIELD_ACCESS_TOKEN));
authentication.put(FIELD_TOKEN_EXPIRES, accessTokenObject.get(FIELD_TOKEN_EXPIRES));
authentication.put(FIELD_TOKEN_EXPIRES_IN, accessTokenObject.get(FIELD_TOKEN_EXPIRES_IN));

JSObject user = new JSObject();
user.put("serverAuthCode", account.getServerAuthCode());
user.put("idToken", account.getIdToken());
user.put("authentication", authentication);

user.put("displayName", account.getDisplayName());
user.put("email", account.getEmail());
user.put("familyName", account.getFamilyName());
user.put("givenName", account.getGivenName());
user.put("id", account.getId());
user.put("imageUrl", account.getPhotoUrl());

call.resolve(user);
} catch (Exception e) {
e.printStackTrace();
call.reject("Something went wrong while retrieving access token", e);
}
});
} catch (ApiException e) {
call.reject("Something went wrong", e);
}
Expand All @@ -106,4 +141,61 @@ public void initialize(final PluginCall call) {
call.resolve();
}

// Logic to retrieve accessToken, see https://github.com/EddyVerbruggen/cordova-plugin-googleplus/blob/master/src/android/GooglePlus.java
private JSONObject getAuthToken(Account account, boolean retry) throws Exception {
AccountManager manager = AccountManager.get(getContext());
AccountManagerFuture<Bundle> future = manager.getAuthToken(account, "oauth2:profile email", null, false, null, null);
Bundle bundle = future.getResult();
String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
try {
return verifyToken(authToken);
} catch (IOException e) {
if (retry) {
manager.invalidateAuthToken("com.google", authToken);
return getAuthToken(account, false);
} else {
throw e;
}
}
}

private JSONObject verifyToken(String authToken) throws IOException, JSONException {
URL url = new URL(VERIFY_TOKEN_URL + authToken);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setInstanceFollowRedirects(true);
String stringResponse = fromStream(new BufferedInputStream(urlConnection.getInputStream()));
/* expecting:
{
"issued_to": "xxxxxx-xxxxxxxxxxxxxxx.apps.googleusercontent.com",
"audience": "xxxxxx-xxxxxxxxxxxxxxxx.apps.googleusercontent.com",
"user_id": "xxxxxxxxxxxxxxxxxxxx",
"scope": "https://www.googleapis.com/auth/userinfo.email openid https://www.googleapis.com/auth/userinfo.profile",
"expires_in": 3220,
"email": "xxxxxxx@xxxxx.com",
"verified_email": true,
"access_type": "online"
}
*/

Log.d("AuthenticatedBackend", "token: " + authToken + ", verification: " + stringResponse);
JSONObject jsonResponse = new JSONObject(stringResponse);
int expires_in = jsonResponse.getInt(FIELD_TOKEN_EXPIRES_IN);
if (expires_in < KAssumeStaleTokenSec) {
throw new IOException("Auth token soon expiring.");
}
jsonResponse.put(FIELD_ACCESS_TOKEN, authToken);
jsonResponse.put(FIELD_TOKEN_EXPIRES, expires_in + (System.currentTimeMillis() / 1000));
return jsonResponse;
}

private static String fromStream(InputStream is) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
reader.close();
return sb.toString();
}
}

0 comments on commit 0ed544c

Please sign in to comment.