Skip to content

Commit 0ed544c

Browse files
hoi4Philipp Heuer
andauthored
refactor: added accessToken to Android implementation (#173)
Co-authored-by: Philipp Heuer <[email protected]>
1 parent 70fd926 commit 0ed544c

File tree

2 files changed

+112
-18
lines changed

2 files changed

+112
-18
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11

22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
package="com.codetrixstudio.capacitor.GoogleAuth.capacitorgoogleauth">
4+
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
5+
<uses-permission android:name="android.permission.INTERNET"/>
46
</manifest>
57

android/src/main/java/com/codetrixstudio/capacitor/GoogleAuth/GoogleAuth.java

Lines changed: 110 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
package com.codetrixstudio.capacitor.GoogleAuth;
22

3+
import android.accounts.Account;
4+
import android.accounts.AccountManager;
5+
import android.accounts.AccountManagerFuture;
36
import android.content.Intent;
7+
import android.os.Bundle;
8+
import android.util.Log;
9+
410
import androidx.activity.result.ActivityResult;
511

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

21-
import org.json.JSONArray;
2227
import org.json.JSONException;
28+
import org.json.JSONObject;
29+
30+
import java.io.BufferedInputStream;
31+
import java.io.BufferedReader;
32+
import java.io.IOException;
33+
import java.io.InputStream;
34+
import java.io.InputStreamReader;
35+
import java.net.HttpURLConnection;
36+
import java.net.URL;
37+
import java.util.concurrent.ExecutorService;
38+
import java.util.concurrent.Executors;
2339

2440
@CapacitorPlugin()
2541
public class GoogleAuth extends Plugin {
42+
private final static String VERIFY_TOKEN_URL = "https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=";
43+
private final static String FIELD_TOKEN_EXPIRES_IN = "expires_in";
44+
private final static String FIELD_ACCESS_TOKEN = "accessToken";
45+
private final static String FIELD_TOKEN_EXPIRES = "expires";
46+
public static final int KAssumeStaleTokenSec = 60;
47+
2648
private GoogleSignInClient googleSignInClient;
2749

2850
@Override
@@ -55,7 +77,6 @@ public void load() {
5577

5678
@PluginMethod()
5779
public void signIn(PluginCall call) {
58-
saveCall(call);
5980
Intent signInIntent = googleSignInClient.getSignInIntent();
6081
startActivityForResult(call, signInIntent, "signInResult");
6182
}
@@ -69,22 +90,36 @@ protected void signInResult(PluginCall call, ActivityResult result) {
6990
try {
7091
GoogleSignInAccount account = completedTask.getResult(ApiException.class);
7192

72-
JSObject authentication = new JSObject();
73-
authentication.put("idToken", account.getIdToken());
74-
75-
JSObject user = new JSObject();
76-
user.put("serverAuthCode", account.getServerAuthCode());
77-
user.put("idToken", account.getIdToken());
78-
user.put("authentication", authentication);
79-
80-
user.put("displayName", account.getDisplayName());
81-
user.put("email", account.getEmail());
82-
user.put("familyName", account.getFamilyName());
83-
user.put("givenName", account.getGivenName());
84-
user.put("id", account.getId());
85-
user.put("imageUrl", account.getPhotoUrl());
86-
87-
call.resolve(user);
93+
// The accessToken is retrieved by executing a network request against the Google API, so it needs to run in a thread
94+
ExecutorService executor = Executors.newSingleThreadExecutor();
95+
executor.execute(() -> {
96+
try {
97+
JSONObject accessTokenObject = getAuthToken(account.getAccount(), true);
98+
99+
JSObject authentication = new JSObject();
100+
authentication.put("idToken", account.getIdToken());
101+
authentication.put(FIELD_ACCESS_TOKEN, accessTokenObject.get(FIELD_ACCESS_TOKEN));
102+
authentication.put(FIELD_TOKEN_EXPIRES, accessTokenObject.get(FIELD_TOKEN_EXPIRES));
103+
authentication.put(FIELD_TOKEN_EXPIRES_IN, accessTokenObject.get(FIELD_TOKEN_EXPIRES_IN));
104+
105+
JSObject user = new JSObject();
106+
user.put("serverAuthCode", account.getServerAuthCode());
107+
user.put("idToken", account.getIdToken());
108+
user.put("authentication", authentication);
109+
110+
user.put("displayName", account.getDisplayName());
111+
user.put("email", account.getEmail());
112+
user.put("familyName", account.getFamilyName());
113+
user.put("givenName", account.getGivenName());
114+
user.put("id", account.getId());
115+
user.put("imageUrl", account.getPhotoUrl());
116+
117+
call.resolve(user);
118+
} catch (Exception e) {
119+
e.printStackTrace();
120+
call.reject("Something went wrong while retrieving access token", e);
121+
}
122+
});
88123
} catch (ApiException e) {
89124
call.reject("Something went wrong", e);
90125
}
@@ -106,4 +141,61 @@ public void initialize(final PluginCall call) {
106141
call.resolve();
107142
}
108143

144+
// Logic to retrieve accessToken, see https://github.com/EddyVerbruggen/cordova-plugin-googleplus/blob/master/src/android/GooglePlus.java
145+
private JSONObject getAuthToken(Account account, boolean retry) throws Exception {
146+
AccountManager manager = AccountManager.get(getContext());
147+
AccountManagerFuture<Bundle> future = manager.getAuthToken(account, "oauth2:profile email", null, false, null, null);
148+
Bundle bundle = future.getResult();
149+
String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
150+
try {
151+
return verifyToken(authToken);
152+
} catch (IOException e) {
153+
if (retry) {
154+
manager.invalidateAuthToken("com.google", authToken);
155+
return getAuthToken(account, false);
156+
} else {
157+
throw e;
158+
}
159+
}
160+
}
161+
162+
private JSONObject verifyToken(String authToken) throws IOException, JSONException {
163+
URL url = new URL(VERIFY_TOKEN_URL + authToken);
164+
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
165+
urlConnection.setInstanceFollowRedirects(true);
166+
String stringResponse = fromStream(new BufferedInputStream(urlConnection.getInputStream()));
167+
/* expecting:
168+
{
169+
"issued_to": "xxxxxx-xxxxxxxxxxxxxxx.apps.googleusercontent.com",
170+
"audience": "xxxxxx-xxxxxxxxxxxxxxxx.apps.googleusercontent.com",
171+
"user_id": "xxxxxxxxxxxxxxxxxxxx",
172+
"scope": "https://www.googleapis.com/auth/userinfo.email openid https://www.googleapis.com/auth/userinfo.profile",
173+
"expires_in": 3220,
174+
"email": "[email protected]",
175+
"verified_email": true,
176+
"access_type": "online"
177+
}
178+
*/
179+
180+
Log.d("AuthenticatedBackend", "token: " + authToken + ", verification: " + stringResponse);
181+
JSONObject jsonResponse = new JSONObject(stringResponse);
182+
int expires_in = jsonResponse.getInt(FIELD_TOKEN_EXPIRES_IN);
183+
if (expires_in < KAssumeStaleTokenSec) {
184+
throw new IOException("Auth token soon expiring.");
185+
}
186+
jsonResponse.put(FIELD_ACCESS_TOKEN, authToken);
187+
jsonResponse.put(FIELD_TOKEN_EXPIRES, expires_in + (System.currentTimeMillis() / 1000));
188+
return jsonResponse;
189+
}
190+
191+
private static String fromStream(InputStream is) throws IOException {
192+
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
193+
StringBuilder sb = new StringBuilder();
194+
String line;
195+
while ((line = reader.readLine()) != null) {
196+
sb.append(line).append("\n");
197+
}
198+
reader.close();
199+
return sb.toString();
200+
}
109201
}

0 commit comments

Comments
 (0)