我的目标是拥有某种长期访问令牌,以便我的Android应用可以在当天阅读用户Google日历的事件,而无需每次都获得用户批准。
我能够生成 - 我认为是 - 一次性授权代码;但是,当我将它发送到我的服务器端,app引擎时,我得到以下错误响应:
400 OK
{
"error" : "invalid_grant",
"error_description" : "Code was already redeemed."
}
这是被抛出的异常。我只是抓住它并将其作为一种调试方式发回给自己。
我得到的一次性代码以 4 / VUr 开头,所以我假设它是一次性代码而不是常规访问令牌。
目前,在Android上,我允许用户使用Google+登录,以便我拥有自己的电子邮件地址。从那里我使用以下代码请求一次性授权代码:
try {
Bundle appActivities = new Bundle();
appActivities.putString(GoogleAuthUtil.KEY_REQUEST_VISIBLE_ACTIVITIES, "http://schemas.google.com/AddActivity");
String scopes = "oauth2:server:client_id:" + Constants.SERVER_CLIENT_ID + ":api_scope:https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/calendar";
//Plus.SCOPE_PLUS_LOGIN + " " + CalendarScopes.CALENDAR_READONLY;
String acctName = "myGmail";
String token = GoogleAuthUtil.getToken(getApplicationContext(), acctName, scopes, appActivities);
} catch (UserRecoverableAuthException e) {
startActivityForResult(e.getIntent(), 257/*REQUEST_AUTHORIZATION/*/);
} catch (Exception e) {
e.printStackTrace();
}
此代码来自here,似乎这是我必须做的。
然后我将此代码发送到我的App Engine端点。我使用here中的代码来请求访问和刷新令牌。
以下是我用作简单测试的代码:
HttpTransport transport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
//ArrayList<String> scopes = new ArrayList<>();
//scopes.add("https://www.googleapis.com/auth/plus.login");
//scopes.add("https://www.googleapis.com/auth/calendar");
GoogleTokenResponse tokenResponse = new GoogleAuthorizationCodeTokenRequest(transport, jsonFactory,
SERVER_CLIENT_ID, SERVER_CLIENT_SECRET, code, "postmessage")/*.setScopes(scopes)*/.execute();
//urn:ietf:wg:oauth:2.0:oob
//postmessage
code = tokenResponse.getRefreshToken();
我实例化GoogleAuthorizationCodeTokenRequest
仅举几例我看过
https://developers.google.com/accounts/docs/CrossClientAuth#offlineAccess
Google-api-php Refresh Token returns invalid_grant
getting Google oauth authorization token from Android- return with invalid_scope/ Unknown error
以不同方式设置重定向uri不起作用。 我确实为我的app引擎项目填写了同意屏幕。安装的Android客户端ID和Web应用程序客户端ID都在同一个项目中。对于我的应用,我将web应用程序的重定向uri设置为xxxxxxxx.appspot.com。
Gradle for my main app:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
// compile 'com.google.api-client:google-api-client:1.18.0-rc'
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'org.altbeacon:android-beacon-library:2.1.3'
compile 'com.google.apis:google-api-services-calendar:v3-rev118-1.19.1'
compile 'com.google.api-client:google-api-client-android:1.18.0-rc'
compile 'com.google.android.gms:play-services:6.5.87'
compile 'com.google.http-client:google-http-client-jackson:1.19.0'
compile project(path: ':beaconBackend', configuration: 'android-endpoints')
}
Gradle for my backndnd:
dependencies {
appengineSdk 'com.google.appengine:appengine-java-sdk:1.9.14'
compile 'com.google.appengine:appengine-endpoints:1.9.14'
compile 'com.google.appengine:appengine-endpoints-deps:1.9.14'
compile 'javax.servlet:servlet-api:2.5'
}
真的很感激任何帮助!谢谢!
另外,请注意我已尝试使当前访问令牌(或一次性代码?)无效/撤销。
我只需要一种方法来获得某种长生活访问令牌,而无需用户在第一次交互之后。
答案 0 :(得分:2)
自从我参与这个项目以来已经有一段时间了 - 不再需要它了。
问题出在服务器端应用程序中。我只是没有正确地请求它。 一旦我的服务器端应用程序(App Engine Endpoint)从Android应用程序收到刷新令牌,我在服务器端执行了以下操作。
private String refreshAccessToken(String refreshToken, String clientId, String clientSecret) throws IOException {
try {
TokenResponse response =
new GoogleRefreshTokenRequest(new NetHttpTransport(), new JacksonFactory(),
refreshToken, clientId, clientSecret).execute();
return response.getAccessToken();
} catch (TokenResponseException e) {
return null;
}
}