使用Firebase身份验证进行身份验证后检索Google Access令牌

Wil*_*lik 34 android google-login firebase-authentication google-signin

我正在尝试从经过身份验证的用户(使用Firebase身份验证)检索Google Access令牌以访问Google REST API,例如YouTube Data API.

我已经在Firebase-UI for Android - Auth库的帮助下成功地在我的应用中集成了Google登录.从FirebaseUser.getToken()方法检索的令牌不是REST API的有效Google Access令牌.

user.getToken(true).addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
    public void onComplete(@NonNull Task<GetTokenResult> task) {
        if (task.isSuccessful()) {
            String token = task.getResult().getToken();
            // 'token' is not a Google Access Token
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

Google Sign-In for Web指南中,可以通过调用获取访问令牌var token = result.credential.accessToken;,但我在Android中找不到类似的方法.

有什么投入?如果我没有提供足够的信息,请在这里发表评论.谢谢 :)

Abh*_*uri 43

你正在做的方式会给你firebase id令牌,请看这里.


您将在firebase中遇到三种类型的令牌:

  • Firebase ID令牌

    用户登录Firebase应用时由Firebase创建.这些令牌是签名的JWT,可以安全地识别Firebase项目中的用户.这些令牌包含用户的基本配置文件信息,包括用户的ID字符串,这是Firebase项目所特有的.由于可以验证ID令牌的完整性,因此您可以将它们发送到后端服务器以识别当前登录的用户.

  • 身份提供者令牌

    由联合身份提供商(如Google和Facebook)创建.这些令牌可以具有不同的格式,但通常是OAuth 2.0访问令牌.Firebase应用使用这些令牌来验证用户是否已成功通过身份提供商进行身份验证,然后将其转换为Firebase服务可用的凭据.

  • Firebase自定义令牌

    由您的自定义身份验证系统创建,允许用户使用您的身份验证系统登录Firebase应用.自定义令牌是使用服务帐户的私钥签名的JWT.Firebase应用程序使用这些令牌非常类似于使用联合身份提供程序返回的令牌.


现在,你得到的是firebase Id令牌,你需要的是身份提供者令牌.

它很容易获得身份提供者令牌,它只是您显示的步骤之前的一步.

因此,我们在这里提到使用firebase登录谷歌的方式.

我将在下面添加完整的代码,在UI中显示一个按钮,点击后,将登录用户到谷歌帐户.然后我将获得google访问令牌,然后将其发送到firebase,然后将其转换为firebase令牌ID.

我认为你已经为谷歌登录配置了Android应用程序,如果没有,你可以在这里详细介绍.


(简而言之,如果您已经完成设置,请查看下面的步骤5.)
代码:

  1. 配置Google SignIn和GoogleApiClient:

     // Configure sign-in to request the user's ID, email address, and basic
     // profile. ID and basic profile are included in DEFAULT_SIGN_IN.
     GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestIdToken(getString(R.string.default_web_client_id))
        .requestEmail()
        .build();
    
     // NOTE : 
     // The string passed to requestIdToken, default_web_client_id, 
     // can be obtained from credentials page (https://console.developers.google.com/apis/credentials).
     // There mentioned Web application type client ID is this string.
    
    
     // ... 
     // Build a GoogleApiClient with access to the Google Sign-In API and the
     // options specified by gso.
     mGoogleApiClient = new GoogleApiClient.Builder(this)
        .enableAutoManage(this /* Activity */, this /* OnConnectionFailedListener */)
        .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
        .build();
    
    Run Code Online (Sandbox Code Playgroud)
  2. 将Google登录按钮添加到您的应用

    <com.google.android.gms.common.SignInButton
        android:id="@+id/sign_in_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    
    Run Code Online (Sandbox Code Playgroud)
  3. 设置SignIn单击"侦听器"

    findViewById(R.id.sign_in_button).setOnClickListener(new OnClickListener() {
        public void onClick(View v){
            Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
            startActivityForResult(signInIntent, RC_SIGN_IN);   
        }
    });
    
    Run Code Online (Sandbox Code Playgroud)
  4. OnActivityResultActivity中的覆盖方法:

    @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) {
            // Google Sign In was successful, authenticate with Firebase
            GoogleSignInAccount account = result.getSignInAccount();
            firebaseAuthWithGoogle(account); // This method is implemented in step 5.
        } else {
            // Google Sign In failed, update UI appropriately
            // ...
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  5. 使用Google SignInAccount进行Firebase身份验证

    String idTokenString = "";
    ...
    private void firebaseAuthWithGoogle(GoogleSignInAccount acct) {
        Log.d(TAG, "Google User Id :" + acct.getId());
    
        // --------------------------------- //
        // BELOW LINE GIVES YOU JSON WEB TOKEN, (USED TO GET ACCESS TOKEN) : 
        Log.d(TAG, "Google JWT : " + acct.getIdToken());
        // --------------------------------- //
    
        // Save this JWT in global String : 
        idTokenString = acct.getIdToken();
    
        AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
        mAuth.signInWithCredential(credential)
            .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    Log.d(TAG, "signInWithCredential:onComplete:" + task.isSuccessful());
    
                    if(task.isSuccessful()){
                        // --------------------------------- //
                        // BELOW LINE GIVES YOU FIREBASE TOKEN ID : 
                        Log.d(TAG, "Firebase User Access Token : " + task.getResult().getToken());
                        // --------------------------------- //
                    }
                    // 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.
                    else {
                        Log.w(TAG, "signInWithCredential", task.getException());
                        Toast.makeText(GoogleSignInActivity.this, "Authentication failed.",
                                Toast.LENGTH_SHORT).show();
                    }
                }
            });
    }
    
    Run Code Online (Sandbox Code Playgroud)
  6. 最后一步:为Firebase验证监听器

    private FirebaseAuth mAuth;
    private FirebaseAuth.AuthStateListener mAuthListener;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...
        mAuth = FirebaseAuth.getInstance();
        mAuthListener = new FirebaseAuth.AuthStateListener() {
            @Override
            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
                FirebaseUser user = firebaseAuth.getCurrentUser();
                if (user != null) {
                    // User is signed in
                    Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
                } else {
                   // User is signed out
                   Log.d(TAG, "onAuthStateChanged:signed_out");
                }
                // ...
            }
       };
       // ...
    }
    
    //...
    
    @Override
    public void onStart() {
        super.onStart();
        mAuth.addAuthStateListener(mAuthListener);
    }
    
    @Override
    public void onStop() {
        super.onStop();
        if (mAuthListener != null) {
            mAuth.removeAuthStateListener(mAuthListener);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

所以,你的答案在于第5步,就在你对firebase进行身份验证之前,以及在google登录后进行身份验证之后.

希望能帮助到你 !


更新:

重要的是,在步骤1中,您请求令牌ID,否则在步骤5中,您将获得空令牌ID.欲了解更多请参见这里.我已经更新了第1步.


更新:

根据讨论,检索到的令牌是这里写的JWT令牌.我们需要的是谷歌访问令牌.下面的代码使用JWT令牌在OAuth后端触发并检索此访问令牌:

(注意:我使用过okhttp 2.6.0版,其他版本可能有不同的方法)

代码:

...
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new FormEncodingBuilder()
            .add("grant_type", "authorization_code")
            .add("client_id", "<Your-client-id>")   // something like : ...apps.googleusercontent.com
            .add("client_secret", "{Your-client-secret}")
            .add("redirect_uri","")
            .add("code", "4/4-GMMhmHCXhWEzkobqIHGG_EnNYYsAkukHspeYUk9E8") // device code.
            .add("id_token", idTokenString) // This is what we received in Step 5, the jwt token.
            .build();

final Request request = new Request.Builder()
        .url("https://www.googleapis.com/oauth2/v4/token")
        .post(requestBody)
        .build();

client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(final Request request, final IOException e) {
        Log.e(LOG_TAG, e.toString());                
    }

    @Override
    public void onResponse(Response response) throws IOException {
        try {
            JSONObject jsonObject = new JSONObject(response.body().string());
            final String message = jsonObject.toString(5);
            Log.i(LOG_TAG, message);                    
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

以下是根据需要具有访问令牌的输出:

I/onResponse: {
          "expires_in": 3600,
          "token_type": "Bearer",
          "refresh_token": "1\/xz1eb0XU3....nxoALEVQ",
          "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjQxMWY1Ym......yWVsUA",
          "access_token": "ya29.bQKKYah-........_tkt980_qAGIo9yeWEG4"
     }
Run Code Online (Sandbox Code Playgroud)

希望现在有所帮助!


vov*_*kas 28

尝试使用GoogleAuthUtil.getToken,其范围类似于"oauth2:scope1 scope2 scope3"

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestIdToken(getString(R.string.default_web_client_id))
            .requestEmail()
            .build();

    mGoogleApiClient = new GoogleApiClient.Builder(this)
            .enableAutoManage(this, this)
            .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
            .build();
}

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

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == RC_SIGN_IN) {
        GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
        if (result.isSuccess()) {
            final GoogleSignInAccount account = result.getSignInAccount();

                Runnable runnable = new Runnable() {
                    @Override
                    public void run() {
                        try {
                            String scope = "oauth2:"+Scopes.EMAIL+" "+ Scopes.PROFILE;
                            String accessToken = GoogleAuthUtil.getToken(getApplicationContext(), account.getAccount(), scope, new Bundle());
                            Log.d(TAG, "accessToken:"+accessToken); //accessToken:ya29.Gl...

                        } catch (IOException e) {
                            e.printStackTrace();
                        } catch (GoogleAuthException e) {
                            e.printStackTrace();
                        }
                    }
                };
                AsyncTask.execute(runnable);

        } else {
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


cut*_*iko 6

我正在关注@vovkas解决方案,并希望让您知道,通过最近的更新,11.6.0您可以Account更轻松地获得所需的信息,因此,您可以随时随地使用方便的花花公子 AsyncTask来重用所有内容:

public class GetToken extends AsyncTask<Void, Void, String> {

    private final Context context;

    public GetToken(Context context) {
        this.context = context;
    }

    @Override
    protected String doInBackground(Void... voids) {
        try {
            String scope = "oauth2:" + Scopes.EMAIL + " " + Scopes.PROFILE;
            GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(context);
            return GoogleAuthUtil.getToken(context, account.getAccount(), scope, new Bundle());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (GoogleAuthException e) {
            e.printStackTrace();
        }
        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

关键是要使用GoogleSignIn.getLastSignedInAccount(context)