首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用android GoogleSignInApi凭据执行google api访问

使用android GoogleSignInApi凭据执行google api访问
EN

Stack Overflow用户
提问于 2016-11-21 11:45:49
回答 2查看 1.5K关注 0票数 4

我知道有几个帖子讨论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对象获得。

所以,我的问题是:

  1. 我可以在哪里发送我通过GoogleSignInApi接收到的编写代码,以便将其交换为authtoken。
  2. 是否有处理exchange请求和刷新魔术的库,或者是否期望我自己捕获刷新令牌并发出另一个auth令牌请求。
  3. 是否有更好的方法来获得访问工作表的正确凭据,同时也将GoogleSignInApi用于防火墙服务?
  4. 如果我确实按照建议使用GoogleAuthorizationCodeTokenRequest进行服务器端访问,那么在客户机中使用客户端机密是否可以接受?可能不会。

下面是我正在尝试制作的sheets调用的简化版本。

代码语言:javascript
运行
复制
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。

代码语言:javascript
运行
复制
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帐户。

代码语言:javascript
运行
复制
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;
    }
}
EN

回答 2

Stack Overflow用户

发布于 2016-11-23 11:36:22

如果使用for Sheets API,您的凭据问题将很容易避免。

以下是指南中提到的步骤:

代码语言:javascript
运行
复制
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控制台中找到。

票数 1
EN

Stack Overflow用户

发布于 2020-04-23 09:08:30

我知道我的回答很晚了..。但我只想帮助大家。之后,RnD of 3天,最后,我找到了一个工作解决方案的刷新令牌是空在oAuth谷歌通过Google签名在android

每一个使用这种方法的人:

代码语言:javascript
运行
复制
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();

再添加一个参数:

代码语言:javascript
运行
复制
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。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40719389

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档