Skip to content

Commit 5e9713b

Browse files
committed
Merge branch 'master' into Development
2 parents 5b7fb6a + 8e8b939 commit 5e9713b

File tree

1 file changed

+277
-0
lines changed

1 file changed

+277
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
/*
2+
* This program is free software: you can redistribute it and/or modify
3+
* it under the terms of the GNU General Public License as published by
4+
* the Free Software Foundation, either version 3 of the License, or
5+
* (at your option) any later version.
6+
*
7+
* This program is distributed in the hope that it will be useful,
8+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
* GNU General Public License for more details.
11+
*
12+
* You should have received a copy of the GNU General Public License
13+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
14+
*/
15+
16+
package com.pokegoapi.auth;
17+
18+
import POGOProtos.Networking.Envelopes.RequestEnvelopeOuterClass.RequestEnvelope.AuthInfo;
19+
import com.pokegoapi.exceptions.LoginFailedException;
20+
import com.pokegoapi.exceptions.RemoteServerException;
21+
import com.pokegoapi.util.Log;
22+
import com.squareup.moshi.Moshi;
23+
import lombok.Getter;
24+
import okhttp3.HttpUrl;
25+
import okhttp3.OkHttpClient;
26+
import okhttp3.Request;
27+
import okhttp3.RequestBody;
28+
import okhttp3.Response;
29+
30+
import java.io.IOException;
31+
import java.net.URISyntaxException;
32+
33+
public class GoogleCredentialProvider extends CredentialProvider {
34+
35+
public static final String SECRET = "NCjF1TLi2CcY6t5mt0ZveuL7";
36+
public static final String CLIENT_ID = "848232511240-73ri3t7plvk96pj4f85uj8otdat2alem.apps.googleusercontent.com";
37+
public static final String OAUTH_ENDPOINT = "https://accounts.google.com/o/oauth2/device/code";
38+
public static final String OAUTH_TOKEN_ENDPOINT = "https://www.googleapis.com/oauth2/v4/token";
39+
private static final String TAG = GoogleCredentialProvider.class.getSimpleName();
40+
//We try and refresh token 5 minutes before it actually expires
41+
private static final long REFRESH_TOKEN_BUFFER_TIME = 5 * 60 * 1000;
42+
private final OkHttpClient client;
43+
44+
private final OnGoogleLoginOAuthCompleteListener onGoogleLoginOAuthCompleteListener;
45+
46+
private long expiresTimestamp;
47+
48+
private String tokenId;
49+
50+
@Getter
51+
private String refreshToken;
52+
53+
private AuthInfo.Builder authbuilder;
54+
55+
/**
56+
* Used for logging in when one has a persisted refreshToken.
57+
*
58+
* @param client OkHttp client
59+
* @param refreshToken Refresh Token Persisted by user
60+
* @throws LoginFailedException When login fails
61+
*/
62+
public GoogleCredentialProvider(OkHttpClient client, String refreshToken)
63+
throws LoginFailedException, RemoteServerException {
64+
this.client = client;
65+
this.refreshToken = refreshToken;
66+
onGoogleLoginOAuthCompleteListener = null;
67+
refreshToken(refreshToken);
68+
authbuilder = AuthInfo.newBuilder();
69+
}
70+
71+
/**
72+
* Used for logging in when you dont have a persisted refresh token.
73+
*
74+
* @param client OkHttp client
75+
* @param onGoogleLoginOAuthCompleteListener Callback to know verification url and also persist refresh token
76+
* @throws LoginFailedException When login fails
77+
*/
78+
public GoogleCredentialProvider(OkHttpClient client,
79+
OnGoogleLoginOAuthCompleteListener onGoogleLoginOAuthCompleteListener)
80+
throws LoginFailedException {
81+
this.client = client;
82+
if (onGoogleLoginOAuthCompleteListener != null) {
83+
this.onGoogleLoginOAuthCompleteListener = onGoogleLoginOAuthCompleteListener;
84+
} else {
85+
throw new LoginFailedException("You need to implement OnGoogleLoginOAuthCompleteListener");
86+
}
87+
login();
88+
authbuilder = AuthInfo.newBuilder();
89+
}
90+
91+
/**
92+
* Given the refresh token fetches a new access token and returns AuthInfo.
93+
*
94+
* @param refreshToken Refresh token persisted by the user after initial login
95+
* @throws LoginFailedException If we fail to get tokenId
96+
*/
97+
public void refreshToken(String refreshToken) throws LoginFailedException, RemoteServerException {
98+
HttpUrl url = HttpUrl.parse(OAUTH_TOKEN_ENDPOINT).newBuilder()
99+
.addQueryParameter("client_id", CLIENT_ID)
100+
.addQueryParameter("client_secret", SECRET)
101+
.addQueryParameter("refresh_token", refreshToken)
102+
.addQueryParameter("grant_type", "refresh_token")
103+
.build();
104+
//Empty request body
105+
RequestBody reqBody = RequestBody.create(null, new byte[0]);
106+
Request request = new Request.Builder()
107+
.url(url)
108+
.method("POST", reqBody)
109+
.build();
110+
111+
Response response = null;
112+
try {
113+
response = client.newCall(request).execute();
114+
} catch (IOException e) {
115+
throw new RemoteServerException("Network Request failed to fetch refreshed tokenId", e);
116+
}
117+
Moshi moshi = new Moshi.Builder().build();
118+
GoogleAuthTokenJson googleAuthTokenJson = null;
119+
try {
120+
googleAuthTokenJson = moshi.adapter(GoogleAuthTokenJson.class).fromJson(response.body().string());
121+
Log.d(TAG, "" + googleAuthTokenJson.getExpiresIn());
122+
} catch (IOException e) {
123+
throw new RemoteServerException("Failed to unmarshal the Json response to fetch refreshed tokenId", e);
124+
}
125+
if (googleAuthTokenJson.getError() != null) {
126+
throw new LoginFailedException(googleAuthTokenJson.getError());
127+
} else {
128+
Log.d(TAG, "Refreshed Token " + googleAuthTokenJson.getIdToken());
129+
expiresTimestamp = System.currentTimeMillis()
130+
+ (googleAuthTokenJson.getExpiresIn() * 1000 - REFRESH_TOKEN_BUFFER_TIME);
131+
tokenId = googleAuthTokenJson.getIdToken();
132+
}
133+
}
134+
135+
/**
136+
* Starts a login flow for google using googles device oauth endpoint.
137+
*/
138+
public void login() throws LoginFailedException {
139+
140+
HttpUrl url = HttpUrl.parse(OAUTH_ENDPOINT).newBuilder()
141+
.addQueryParameter("client_id", CLIENT_ID)
142+
.addQueryParameter("scope", "openid email https://www.googleapis.com/auth/userinfo.email")
143+
.build();
144+
145+
//Create empty body
146+
RequestBody reqBody = RequestBody.create(null, new byte[0]);
147+
148+
Request request = new Request.Builder()
149+
.url(url)
150+
.method("POST", reqBody)
151+
.build();
152+
Response response = null;
153+
try {
154+
response = client.newCall(request).execute();
155+
} catch (IOException e) {
156+
throw new LoginFailedException("Network Request failed to fetch tokenId", e);
157+
}
158+
159+
Moshi moshi = new Moshi.Builder().build();
160+
161+
GoogleAuthJson googleAuth = null;
162+
try {
163+
googleAuth = moshi.adapter(GoogleAuthJson.class).fromJson(response.body().string());
164+
Log.d(TAG, "" + googleAuth.getExpiresIn());
165+
} catch (IOException e) {
166+
throw new LoginFailedException("Failed to unmarshell the Json response to fetch tokenId", e);
167+
}
168+
Log.d(TAG, "Get user to go to:"
169+
+ googleAuth.getVerificationUrl()
170+
+ " and enter code:" + googleAuth.getUserCode());
171+
onGoogleLoginOAuthCompleteListener.onInitialOAuthComplete(googleAuth);
172+
173+
GoogleAuthTokenJson googleAuthTokenJson;
174+
try {
175+
while ((googleAuthTokenJson = poll(googleAuth)) == null) {
176+
Thread.sleep(googleAuth.getInterval() * 1000);
177+
}
178+
} catch (InterruptedException e) {
179+
throw new LoginFailedException("Sleeping was interrupted", e);
180+
} catch (IOException e) {
181+
throw new LoginFailedException(e);
182+
} catch (URISyntaxException e) {
183+
throw new LoginFailedException(e);
184+
}
185+
186+
Log.d(TAG, "Got token: " + googleAuthTokenJson.getIdToken());
187+
onGoogleLoginOAuthCompleteListener.onTokenIdReceived(googleAuthTokenJson);
188+
expiresTimestamp = System.currentTimeMillis()
189+
+ (googleAuthTokenJson.getExpiresIn() * 1000 - REFRESH_TOKEN_BUFFER_TIME);
190+
tokenId = googleAuthTokenJson.getIdToken();
191+
refreshToken = googleAuthTokenJson.getRefreshToken();
192+
}
193+
194+
195+
private GoogleAuthTokenJson poll(GoogleAuthJson json) throws URISyntaxException, IOException, LoginFailedException {
196+
HttpUrl url = HttpUrl.parse(OAUTH_TOKEN_ENDPOINT).newBuilder()
197+
.addQueryParameter("client_id", CLIENT_ID)
198+
.addQueryParameter("client_secret", SECRET)
199+
.addQueryParameter("code", json.getDeviceCode())
200+
.addQueryParameter("grant_type", "http://oauth.net/grant_type/device/1.0")
201+
.addQueryParameter("scope", "openid email https://www.googleapis.com/auth/userinfo.email")
202+
.build();
203+
204+
//Empty request body
205+
RequestBody reqBody = RequestBody.create(null, new byte[0]);
206+
Request request = new Request.Builder()
207+
.url(url)
208+
.method("POST", reqBody)
209+
.build();
210+
211+
Response response = client.newCall(request).execute();
212+
213+
Moshi moshi = new Moshi.Builder().build();
214+
GoogleAuthTokenJson token = moshi.adapter(GoogleAuthTokenJson.class).fromJson(response.body().string());
215+
216+
if (token.getError() == null) {
217+
return token;
218+
} else {
219+
return null;
220+
}
221+
}
222+
223+
@Override
224+
public String getTokenId() throws LoginFailedException, RemoteServerException {
225+
if (isTokenIdExpired()) {
226+
refreshToken(refreshToken);
227+
}
228+
return tokenId;
229+
}
230+
231+
/**
232+
* Refreshes tokenId if it has expired
233+
*
234+
* @return AuthInfo object
235+
* @throws LoginFailedException When login fails
236+
*/
237+
@Override
238+
public AuthInfo getAuthInfo() throws LoginFailedException, RemoteServerException {
239+
if (isTokenIdExpired()) {
240+
refreshToken(refreshToken);
241+
}
242+
authbuilder.setProvider("google");
243+
authbuilder.setToken(AuthInfo.JWT.newBuilder().setContents(tokenId).setUnknown2(59).build());
244+
return authbuilder.build();
245+
}
246+
247+
@Override
248+
public boolean isTokenIdExpired() {
249+
if (System.currentTimeMillis() > expiresTimestamp) {
250+
return true;
251+
} else {
252+
return false;
253+
}
254+
}
255+
256+
/**
257+
* This callback will be called when we get the
258+
* verification url and device code.
259+
* This will allow applications to
260+
* programmatically redirect user to redirect user.
261+
*/
262+
public interface OnGoogleLoginOAuthCompleteListener {
263+
/**
264+
* Good Hook to provide custom redirects to verification url page.
265+
*
266+
* @param googleAuthJson GoogleAuth object
267+
*/
268+
void onInitialOAuthComplete(GoogleAuthJson googleAuthJson);
269+
270+
/**
271+
* Good Idea to persist the refresh token recieved here
272+
*
273+
* @param googleAuthTokenJson GoogleAuthToken object
274+
*/
275+
void onTokenIdReceived(GoogleAuthTokenJson googleAuthTokenJson);
276+
}
277+
}

0 commit comments

Comments
 (0)