2016-11-28 350 views
22

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

我在Firebase-UI for Android - Auth图书馆的帮助下,成功地将Google Sign-In集成到了我的应用程序中。从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 
     } 
    } 
}); 

Google Sign-In for Web guide,它可能通过调用var token = result.credential.accessToken;来获得访问令牌,但我无法找到Android的类似的方法。

任何输入?如果我没有提供足够的信息,请在这里留言。谢谢:)

+0

你从控制台 –

+0

启用API哪个控制台@KrunalKapadiya?我已启用Google Firebase身份验证控制台 – Wilik

+0

https://console.developers.google.com/apis/dashboard?project=YOUR_PROJECT_ID,启用YouTube Data API v3 –

回答

30

你这样做会给你firebase id令牌,见here


有三种类型的标记,你会在火力遇到:

  • 火力地堡ID令牌

    的创火力地堡时的火力地堡的应用程序用户的迹象。这些令牌是经过签名的JWT,可以在Firebase项目中安全地识别用户。这些令牌包含用户的基本配置文件信息,包括用户的ID字符串,该字符串对Firebase项目是唯一的。由于可以验证ID令牌的完整性,因此可以将它们发送到后端服务器以标识当前登录的用户。

  • 身份提供令牌

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

  • 火力地堡定制令牌

    由您自定义的身份验证系统创建允许用户在登录使用身份验证系统火力地堡的应用程序。自定义令牌是使用服务帐户的私钥签名的JWT。 Firebase应用使用这些令牌,就像他们使用联合身份提供商返回的令牌一样。现在


,你越来越是火力ID令牌,你需要的是身份提供令牌。

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

因此,我们使用firebase登录谷歌的方式被提及here

我会在下面添加完整的代码,在UI中显示一个按钮,点击后会将用户登录到Google帐户。然后,我会得到谷歌的访问令牌,然后将其发送到火力点,在那里它被转换成火力令牌ID。

我假定你已经配置为谷歌Android应用程序登录,如果没有,你可以进入细节here


(切东西短,只要看看下面的步骤5,如果你已经做了设置好的)
代码

  1. 配置谷歌签到和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(); 
    
  2. 添加谷歌登入按钮,您的应用程序

    <com.google.android.gms.common.SignInButton 
        android:id="@+id/sign_in_button" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" /> 
    
  3. 设置签到点击监听器

    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); 
        } 
    }); 
    
  4. 覆盖OnActivityResult方法在活动

    @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 
         // ... 
        } 
    } 
    
  5. 火力地堡认证与谷歌SignInAccount

    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(); 
           } 
          } 
         }); 
    } 
    
  6. 最后一步:验证听众的火力地堡

    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); 
        } 
    } 
    

所以,你的答案就在第5步,这是就在您通过身份验证之前,以及您身份验证之后在谷歌登录enticated。

希望它能帮助!


UPDATE:

其重要的是在步骤1中,您请求的令牌标识,否则在步骤5中,你会得到空令牌ID。更多信息请参见here。我已经更新了第1步:


UPDATE:

按照讨论中,检索到的令牌是JWT令牌书面here。我们需要的是谷歌访问令牌。下面的代码使用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(); 
     } 
    } 
}); 

这里是具有访问令牌的输出根据需要:

I/onResponse: { 
      "expires_in": 3600, 
      "token_type": "Bearer", 
      "refresh_token": "1\/xz1eb0XU3....nxoALEVQ", 
      "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjQxMWY1Ym......yWVsUA", 
      "access_token": "ya29.bQKKYah-........_tkt980_qAGIo9yeWEG4" 
    } 

现在希望它有帮助!

+0

感谢您的惊人回答,但'acct.getIdToken()'不是Google Access令牌,根据[此SO回答](http://stackoverflow.com/a/10299106/5292961),Google Access令牌开始用'ya29.AHES ...',只有60个字符长('acct.getIdToken()'返回1088个字符)。 再次感谢您的回答!至少现在我只需要知道如何从'id token'获取'access token'。经过快速研究,看起来像我必须部署后端来检索访问令牌。 [参考](http://stackoverflow.com/questions/33998335/how-to-get-access-token-after-user-is-signed-in-from-gmail-in-android) – Wilik

+0

好吧,我想我在这里弄错了,你在这里得到的标记是JWT(Json Web Token)。如果您不知道JWT和OAuth令牌之间的区别,请通过Google进行。你说的60字符长是OAuth访问令牌。所以,你需要做的就是使用这个jwt令牌来命中'https:// www.googleapis.com/oauth2/v4/token',现在你可以在客户端自己做,而不需要服务器端。我将更新我的代码以从android本身获取oauth访问令牌。感谢您指出,将尽快更新。 –

+1

刚刚更新的代码来自我之前评论中的链接。我试过了,我得到了这个响应'{“error”:“invalid_grant”,“error_description”:“Bad Request”}'。我应该在'code'中输入与你的答案相同的值,或者我可以在哪里获得'设备代码'?从[本指南](https://developers.google.com/identity/protocols/OAuth2InstalledApp),看起来'code'的值是来自授权请求的响应。现在我很困惑。哈哈 – Wilik

12

尝试GoogleAuthUtil.getToken其中scope像“的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 { 
     } 
    } 
} 
+0

感谢一群伙计:) – Shahal

+0

感谢兄弟保存马天 –

+0

希望我有更多upvotes给。谢谢您的帮助。 – MikeCocoa

1

我正在关注的@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; 
    } 
} 

关键是使用GoogleSignIn.getLastSignedInAccount(context)

相关问题