簡體   English   中英

如何在沒有后端服務器的 Android 應用程序中為 PhotosLibraryClient 驗證用戶

[英]How do you authenticate a user for PhotosLibraryClient in an Android app with no backend server

以前,我使用的庫是: com.google.apis:google-api-services-photoslibrary:v1-rev1-1.23.0但我被告知這並不意味着要發布,並且它有一些錯誤。 所以我嘗試切換正確的: com.google.photos.library:google-photos-library-client:1.4.0但是,這有不同的身份驗證流程。

基本上,所有文檔和示例都涉及客戶端機密的問題,以及提供訪問令牌的 MY 服務器,UserCredentials 所需,而 UserCredentials 又用於 FixedCredentialsProvider 和 PhotosLibraryClient。

但我沒有服務器 - 這是一個獨立的應用程序。 在開發者控制台上設置客戶端 ID 時,當您僅指定應用訪問權限時,它不會創建客戶端密鑰(而是使用應用的簽名)。 鑒於它允許我創建該 ID - 我認為必須有一種沒有客戶端機密的方法。

對於另一個庫,流程是:

  • 使用GoogleSignInClient.getSignInIntent()startActivityForResult()確定賬戶
  • 假設成功,使用onActivityResult返回的意圖創建一個Task<GoogleSignInAccount>
  • 從任務中獲取GoogleSignInAccount ,然后從中獲取getAccount()
  • 創建一個GoogleAccountCredential.usingOAuth2()傳遞 Google 相冊所需的范圍,然后setSelectedAccount()
  • 使用 `PhotosLibrary.Builder() 和創建的憑據

但是對於新的,您需要使用UserCredentials ,它需要一個客戶端密碼(之前不需要,並且在生成 Android ID 時也沒有創建)和一個訪問令牌。

似乎我應該能夠使用GoogleAuthUtil.getToken獲取訪問令牌,但稍后仍需要客戶端機密。 getToken 也不起作用 - 我收到身份驗證異常。 可能是因為我的范圍錯誤,但我不確定。

我也看過使用https://github.com/erickogi/AndroidGooglePhotosApi/blob/master/app/src/main/java/ke/co/calista/googlephotos/Utils/AccessTokenFactory.kt來獲取令牌,但是還需要客戶端機密。

這是登錄(成功)帳戶並獲取令牌的活動。 但我無法彌補這與獲得圖書館客戶之間的差距。

import android.accounts.Account;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableAuthException;
import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInClient;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.Scope;
import com.google.android.gms.tasks.Task;
import com.google.api.gax.core.FixedCredentialsProvider;
import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.UserCredentials;
import com.google.photos.library.v1.PhotosLibraryClient;
import com.google.photos.library.v1.PhotosLibrarySettings;
import com.rjhartsoftware.logcatdebug.D;

import java.io.IOException;

public class MainActivity extends AppCompatActivity {

    public static final String PHOTO_SCOPE = "https://www.googleapis.com/auth/photoslibrary";

    // Request codes
    private static final int RC_SIGN_IN = 9001;

    private GoogleSignInClient mGoogleSignInClient;

    private Account mAccount;
    private String mServerAuthCode;
    private AccessToken mToken;

    @SuppressLint("SetJavaScriptEnabled")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        D.init(BuildConfig.VERSION_NAME, BuildConfig.DEBUG);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestScopes(new Scope(PHOTO_SCOPE))
                .requestEmail()
                .build();

        mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
    }

    @Override
    public void onStart() {
        super.onStart();

        // Check if the user is already signed in and all required scopes are granted
        GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
        if (GoogleSignIn.hasPermissions(account, new Scope(PHOTO_SCOPE))) {
            mAccount = account.getAccount();
            mServerAuthCode = account.getServerAuthCode();
            new GetToken().execute();
        } else {
            signIn();
        }
    }

    @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) {
            Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
            handleSignInResult(task);
        }

    }

    private void signIn() {
        Intent signInIntent = mGoogleSignInClient.getSignInIntent();
        startActivityForResult(signInIntent, RC_SIGN_IN);
    }

    private void handleSignInResult(@NonNull Task<GoogleSignInAccount> completedTask) {
        D.log(D.GENERAL, "handleSignInResult:" + completedTask.isSuccessful()); //NON-NLS

        try {
            GoogleSignInAccount account = completedTask.getResult(ApiException.class);

            if (account != null) {
                // Store the account from the result
                mAccount = account.getAccount();
                mServerAuthCode = account.getServerAuthCode();
                new GetToken().execute();
            }
        } catch (ApiException e) {
            D.warn(D.GENERAL, "handleSignInResult:error", e); //NON-NLS

            // Clear the local account
            mAccount = null;
            mServerAuthCode = null;

        }
    }

    private class GetToken extends AsyncTask<Void, Void, Void> {

        @Override
        protected Void doInBackground(Void... voids) {
            try {
                mToken = null;
                String token = GoogleAuthUtil.getToken(MainActivity.this, mAccount, PHOTO_SCOPE);
                mToken = new AccessToken(token, null);
            } catch (UserRecoverableAuthException e) {
                D.warn(D.GENERAL, "Recoverable Auth Error getting token", e);
            } catch (IOException e) {
                D.error(D.GENERAL, "IO Error getting token", e);
            } catch (GoogleAuthException e) {
                D.warn(D.GENERAL, "Auth Error getting token", e);
            }
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            if (mToken != null) {
                PhotosLibraryClient client = getLibraryClient();
            }
        }
    }

    public PhotosLibraryClient getLibraryClient() {

        // At this point there is an account and server auth code, but we can't get a client yet

        try {
            UserCredentials.Builder credBuilder = UserCredentials.newBuilder()
                    .setClientId("<From Google API Dashboard>")
                    .setClientSecret("<This doesn't exist!")
                    .setAccessToken(mToken);

            UserCredentials creds = credBuilder.build();

            PhotosLibrarySettings.Builder settingsBuilder = PhotosLibrarySettings.newBuilder();
            settingsBuilder.setCredentialsProvider(FixedCredentialsProvider.create(creds));

            PhotosLibrarySettings settings = settingsBuilder.build();

            return PhotosLibraryClient.initialize(settings);
        } catch (IOException e) {
            D.error(D.GENERAL, "Error logging in to Google Photos API", e);
        }
        return null;
    }

}

即使您使用的是 Android 應用程序,您仍然可以從開發人員控制台創建一個“Web 應用程序”類型的憑證並使用它。 你不需要服務器。 您可以在 Android 應用程序中將該憑據與 GoogleSignIn 結合使用以獲取 serverAuthCode。 棘手的部分是,您需要使用登錄結果中的 serverAuthCode 對https://www.googleapis.com/oauth2/v4/token進行 http 調用,以獲得 UserCredentials 所需的訪問令牌.建造者。 這就是您引用Github 項目正在做的事情。 這個方法在這個問題中有很多代碼示例: 如何在用戶從 Android 中的 Gmail 登錄后獲取訪問令牌?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM