2016-11-28 402 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)

相關問題