首页 > 解决方案 > 使用 YouTube v3 api 身份验证时 OAuth2.0 无效凭据

问题描述

我花了几天时间研究如何通过 Google api 控制台和 Firebase api 实现“简单”的 Google 登录。由于我没有服务器,我希望只有在用户使用该应用程序时才能访问他的帐户。我的目标是能够获取用户的频道 ID,这样我就可以加载他所有上传的视频。

为此,我在 google 控制台 api 中创建了一个新项目,将其链接到 Firebase 并使用此教程链接将其集成到我的应用程序中。我到了用户可以选择Gmail帐户并登录的地步,该应用程序还提示用户需要允许的一系列YouTube权限。之后,我得到了用户的 accountUid(与 Firebase 控制台中显示的相同)。

从这一点开始,我感到困惑,因为一些教程提到了我没有的令牌访问、刷新令牌、客户端 ID、客户端密码,并且不确定这些凭据是否对我的应用程序是必需的。

用户成功登录后,我使用此 API 调用来获取他的 YouTube 帐户的频道:

 @GET("channels")
    fun getChannelsList(@Query("part") part :String = "contentDetails,snippet",@Query("mine") mine : Boolean = true, @Header("Authorization") accessToken : String) : Single<ChannelResponse>

但我收到了这个错误:

{
  "error": {
  "errors": [
  {
     "domain": "global",
     "reason": "authError",
     "message": "Invalid Credentials",
     "locationType": "header",
     "location": "Authorization"
  }
  ],
  "code": 401,
  "message": "Invalid Credentials"
}
}

从这个链接中,我还尝试将“Bearer”作为标题值的一部分,结果相同。

我也试过 googleSignInAccount.idToken 但它也没有帮助。

这是我的 LoginActivity 的代码:

class LoginActivity : BaseActivity(), View.OnClickListener,OnCompleteListener<AuthResult>{

    var idTokenString = ""
    val TAG = "LoginActivity"
    val mAuth = FirebaseAuth.getInstance()
    private var mAuthListener: FirebaseAuth.AuthStateListener? = null
    var googleAccount : GoogleSignInAccount?=null

    override fun layoutRes(): Int {
        return app.globe.com.youtubeplaylist.R.layout.activity_login
    }

    override fun initUI() {
        login.setOnClickListener(this)
        logout.setOnClickListener(this)
    }



    companion object {
        const val RC_SIGN_IN = 1000
    }

    private var googleSignInClient : GoogleSignInClient?=null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)


        val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestIdToken(getString(app.globe.com.youtubeplaylist.R.string.default_web_client_id))
            .requestEmail()
            .requestScopes(Scope("https://www.googleapis.com/auth/youtube.readonly"),
                           Scope("https://www.googleapis.com/auth/youtube.force-ssl"))
            .build()


        googleSignInClient = GoogleSignIn.getClient(this,gso)
        googleSignInClient!!.signInIntent

    }

    override fun onStart() {
        super.onStart()
        val currentUser = mAuth.currentUser
        checkAccount(currentUser)
    }

    override fun onClick(v: View) {
        when (v.id) {
            app.globe.com.youtubeplaylist.R.id.login -> {
                signIn()
            }
            app.globe.com.youtubeplaylist.R.id.logout ->{
                signOut()
            }
        }
    }

    private fun signOut()
    {
        FirebaseAuth.getInstance().signOut()
        checkAccount(null)
    }

    private fun signIn() {
        val signInIntent = googleSignInClient!!.signInIntent
        startActivityForResult(signInIntent, RC_SIGN_IN)
    }



    public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        // Result returned from launching the Intent from GoogleSignInClient.getSignInIntent(...);
        if (requestCode == RC_SIGN_IN) {
            // The Task returned from this call is always completed, no need to attach
            // a listener.
            val task = GoogleSignIn.getSignedInAccountFromIntent(data)
            try {
                // Google Sign In was successful, authenticate with Firebase
                googleAccount = task.getResult(ApiException::class.java)
                firebaseAuthWithGoogle(googleAccount!!)
            } catch (e: ApiException) {
                // Google Sign In failed, update UI appropriately
                Log.w(TAG, "Google sign in failed", e)
                // ...
            }

        }
    }

    private fun firebaseAuthWithGoogle(acct : GoogleSignInAccount)
    {
        Log.d(TAG, "firebaseAuthWithGoogle:" + acct.id)

        val credential = GoogleAuthProvider.getCredential(acct.idToken, null)



    mAuth.signInWithCredential(credential)
            .addOnCompleteListener(this, this)

    }



    override fun onComplete(task: Task<AuthResult>) {

        if (task.isSuccessful) {
            // Sign in success, update UI with the signed-in user's information
            Log.d(TAG, "signInWithCredential:success")
            var user = mAuth.currentUser
            checkAccount(user)
        } else {
            // If sign in fails, display a message to the user.
            Log.w(TAG, "signInWithCredential:failure", task.exception)
            Snackbar.make(main_layout, "Authentication Failed.", Snackbar.LENGTH_SHORT).show()
            checkAccount(null)
        }

    }


    private fun checkAccount(account : FirebaseUser?)
    {
        if(account!=null)
        {
            val mainIntent = Intent(this,MainActivity::class.java)
            startActivity(mainIntent)
        }
        else
        {
            login.visibility = View.VISIBLE
            logout.visibility = View.GONE
        }
    }    
}

谢谢!

标签: androidfirebasegoogle-play-services

解决方案


default_web_client_id可能是错的。

不管app.globe.com.youtubeplaylist.R.string.default_web_client_id可能是什么。

请参阅GoogleSignInOptions.BuilderrequestIdToken (String serverClientId)

指定请求已验证用户的 ID 令牌。

请求 ID 令牌需要指定服务器客户端 ID。

serverClientId将验证令牌完整性的服务器的客户端 ID。


转到console.firebase.google.com,选择项目,然后单击

Authentication> Sign-In method> Google> Web SDK configuration

在那里您可以找到要使用的“Web 客户端 ID”和“Web 客户端密码”。

同样在 Google Cloud API 凭据页面上,作为“Web 应用程序的客户端 ID”。


范围https://www.googleapis.com/auth/youtube.force-ssl可能需要范围https://www.googleapis.com/auth/youtube(这两者都用于管理 YouTube 帐户)。对于查看内容,范围https://www.googleapis.com/auth/youtube.readonly就足够了。


推荐阅读