我知道有几个帖子讨论android的signin/oauth,但是没有一个能解决我的问题。
在google教程中,我很难调和auth流和AccountManager的使用。
使用GoogleSignInApi,我最终得到了一个创作代码。到目前一切尚好。接下来,文档建议将authcode交换为authtoken/recovery令牌。https://developers.google.com/identity/sign-in/android/offline-access有一个很好的例子,说明如何将auth代码发送到后端进行交换。
这个流程的唯一问题是,我没有自己的后端,因为我只想访问。sheets调用需要一个GoogleCredential对象,这是我无法从authcode获得的,也不能通过GoogleSignInAccount对象获得。
所以,我的问题是:
下面是我正在尝试制作的sheets调用的简化版本。
GoogleCredential credential = new GoogleCredential().setAccessToken("TEST_ACCESS_TOKEN_FROM_OAUTH_PLAYGROUND");
mService = new com.google.api.services.sheets.v4.Sheets.Builder(
transport, jsonFactory, credential)
.setApplicationName("Google Sheets API Android Quickstart")
.build();
更新:为了取得一些进展,我最终实现了一个服务器端流来交换令牌。我很确定,这不是正确的技术,因为它需要在应用程序中使用client_secret。
第1部分: SignInActivity基于防火墙代码实验室。我需要一个防火墙帐户,所以我觉得我必须使用GoogleSignInApi。
public class SignInActivity extends AppCompatActivity implements GoogleApiClient.OnConnectionFailedListener,
View.OnClickListener {
private static final int RC_SIGN_IN = 9001;
private GoogleApiClient mGoogleApiClient;
private FirebaseAuth mFirebaseAuth;
public static final String PREF_ACCOUNT_NAME = "accountName";
public static final String PREF_ID_TOKEN = "idToken";
public static final String PREF_AUTH_CODE = "authCode";
public static final Scope SHEETS_SCOPE = new Scope(SheetsScopes.SPREADSHEETS);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sign_in);
SignInButton signInButton = (SignInButton) findViewById(R.id.sign_in_button);
signInButton.setOnClickListener(this);
Log.d(TAG, getString(R.string.default_web_client_id));
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id))
.requestScopes(SHEETS_SCOPE)
.requestServerAuthCode(getString(R.string.default_web_client_id))
.requestEmail()
.build();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
// Initialize FirebaseAuth
mFirebaseAuth = FirebaseAuth.getInstance();
}
private void handleFirebaseAuthResult(AuthResult authResult) {
// ...
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.sign_in_button:
signIn();
break;
default:
return;
}
}
private void signIn() {
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
startActivityForResult(signInIntent, RC_SIGN_IN);
}
@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) {
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
if (result.isSuccess()) {
// Google Sign In was successful, authenticate with Firebase
GoogleSignInAccount account = result.getSignInAccount();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.getApplicationContext());
SharedPreferences.Editor editor = prefs.edit();
editor.putString(PREF_ACCOUNT_NAME, account.getEmail());
editor.putString(PREF_ID_TOKEN, account.getIdToken());
editor.putString(PREF_AUTH_CODE, account.getServerAuthCode());
editor.apply();
// TODO: it would be great to do the exchange of the authcode now but it's doing a
// network call and can't be on the main thread.
// I really need this one
firebaseAuthWithGoogle(account);
} else {
// Google Sign In failed
Log.e(TAG, "Google Sign In failed.");
}
}
}
private void firebaseAuthWithGoogle(GoogleSignInAccount acct) {
Log.d(TAG, "firebaseAuthWithGoogle:" + acct.getId());
AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
mFirebaseAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
Log.d(TAG, "signInWithCredential:onComplete:" + task.isSuccessful());
// 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.
if (!task.isSuccessful()) {
Log.w(TAG, "signInWithCredential", task.getException());
Toast.makeText(SignInActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
} else {
startActivity(new Intent(SignInActivity.this, MainActivity.class));
finish();
}
}
});
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
// An unresolvable error has occurred and Google APIs (including Sign-In) will not
// be available.
Log.d(TAG, "onConnectionFailed:" + connectionResult);
Toast.makeText(this, "Google Play Services error.", Toast.LENGTH_SHORT).show();
}
}
第2部分: DataManager是一个实用程序类,应用程序使用它访问工作表数据。它不使用sheets代码实验室中推荐的流程,因为它不允许我用相同的用户数据设置firebase帐户。
public class DataManager {
public static final String UNDEF = "undefined";
private com.google.api.services.sheets.v4.Sheets mService = null;
// this is the play copy
private static String mSheetID = SHEET_ID;
private static final String PREF_ACCESS_TOKEN = "accessToken";
private static final String PREF_REFRESH_TOKEN = "refreshToken";
private static final String PREF_EXPIRES_IN_SECONDS = "expiresInSec";
private Context mContext;
private String mAccessToken;
private String mRefreshToken;
private Long mExpiresInSeconds;
private String mAuthCode;
public DataManager(Context context) {
mContext = context;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
mAuthCode = prefs.getString(SignInActivity.PREF_AUTH_CODE, UNDEF);
mAccessToken = prefs.getString(PREF_ACCESS_TOKEN, UNDEF);
mRefreshToken = prefs.getString(PREF_REFRESH_TOKEN, UNDEF);
mExpiresInSeconds = prefs.getLong(PREF_EXPIRES_IN_SECONDS, 0);
}
private void exchangeCodeForToken(String authCode) {
try {
GoogleTokenResponse tokenResponse =
new GoogleAuthorizationCodeTokenRequest(
new NetHttpTransport(),
JacksonFactory.getDefaultInstance(),
"https://www.googleapis.com/oauth2/v4/token",
mContext.getString(R.string.default_web_client_id),
// TODO: the app shouldn't have to use the client secret
{CLIENT_SECRET},
authCode,
"")
.execute();
mAccessToken = tokenResponse.getAccessToken();
mRefreshToken = tokenResponse.getRefreshToken();
mExpiresInSeconds = tokenResponse.getExpiresInSeconds();
// TODO: do I really need to store and pass the three values individually?
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(PREF_ACCESS_TOKEN, mAccessToken);
editor.putString(PREF_REFRESH_TOKEN, mRefreshToken);
editor.putLong(PREF_EXPIRES_IN_SECONDS, mExpiresInSeconds);
editor.remove(SignInActivity.PREF_AUTH_CODE);
editor.apply();
} catch (Exception e) {
Log.e(TAG, "Token exchange failed with " + e.getMessage());
}
}
private void refreshAccessToken(String refreshToken) {
try {
// TODO: what to do here?
throw new Exception("TBD");
} catch (Exception e) {
Log.e(TAG, "Token refresh failed with " + e.getMessage());
}
}
private GoogleCredential getCredential() {
if (mAuthCode != UNDEF) {
exchangeCodeForToken(mAuthCode);
}
// TODO: handle missing or expired token
if (mRefreshToken != UNDEF && mExpiresInSeconds < 30) {
refreshAccessToken(mRefreshToken);
}
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(new NetHttpTransport())
.setJsonFactory(JacksonFactory.getDefaultInstance())
.build();
credential.setAccessToken(mAccessToken);
if (mRefreshToken != UNDEF) {
credential.setRefreshToken(mRefreshToken);
credential.setExpiresInSeconds(mExpiresInSeconds);
}
return credential;
}
// Set up credential and service object, then issue api call.
public ArrayList<Foo> getFooListFromServer() throws IOException {
try {
GoogleCredential credential = getCredential();
HttpTransport transport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
mService = new com.google.api.services.sheets.v4.Sheets.Builder(
transport, jsonFactory, credential)
.setApplicationName(mContext.getString(R.string.app_name))
.build();
return getDataFromServer();
} catch (IOException exception) {
// ...
throw exception;
} catch (Exception e) {
Log.e(TAG, "something else is going on " + e.toString());
throw e;
}
}
/**
* Actually fetch the data from google
*
* @return List of Foos
* @throws IOException
*/
private ArrayList<Foo> getDataFromServer() throws IOException {
ArrayList<Foo> foos = new ArrayList<Foo>();
ValueRange response = this.mService.spreadsheets().values()
.get(mSheetID, mRange)
.setValueRenderOption("UNFORMATTED_VALUE")
.setDateTimeRenderOption("FORMATTED_STRING")
.execute();
//...
return foos;
}
}
发布于 2016-11-23 11:36:22
如果使用for Sheets API,您的凭据问题将很容易避免。
以下是指南中提到的步骤:
Step 1: Acquire a SHA1 fingerprint
Step 2: Turn on the Google Sheets API
Step 3: Create a new Android project
Step 4: Prepare the project
Step 5: Setup the sample
OAuth客户端ID可以在Google控制台中找到。
发布于 2020-04-23 09:08:30
我知道我的回答很晚了..。但我只想帮助大家。之后,RnD of 3天,最后,我找到了一个工作解决方案的刷新令牌是空在oAuth谷歌通过Google签名在android
每一个使用这种方法的人:
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id))
.requestScopes(SHEETS_SCOPE)
.requestServerAuthCode(getString(R.string.default_web_client_id))
.requestEmail()
.build();
再添加一个参数:
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id))
.requestScopes(SHEETS_SCOPE)
.requestServerAuthCode(getString(R.string.default_web_client_id), true) //For offline access or background access token generation
.requestEmail()
.build();
之后,您将获得一个服务器auth令牌,它将返回refresh_token和access_token。
https://stackoverflow.com/questions/40719389
复制相似问题