loading...

Huawei Cab Application (Login Module with HMS Account Kit and AGC Auth Service) Part-1

singlebubble1 profile image singlebubble ・7 min read

Introduction

Huawei Cab Application is to explore HMS Kits in real time scenario, use this app as reference to CP during HMS integration and understand easily about HMS function.

Login Module

The user can access Huawei Cab Application in Login page by HUAWEI ID or Email ID Login or Mobile Login and Google Sign In.

You can use AGC Auth Service to integrate one or more of the following authentication methods into your app for achieving easy and efficient user registration, and sign-in.

AGC Auth service is not providing user google mail id. So, you can retrieve your mail Id directly from third party google server by ID token, we have covered that one also in this article.

Huawei Cab Application needs user authentication to access application using following method:

Huawei Cab Authentication type:

  1. Huawei Account Kit
  2. Huawei Auth Service

App Screen

Alt Text
Alt Text

Huawei Account Kit

HUAWEI Account Kit provides developers with simple, secure, and quick sign-in, and authorization functions. Instead of entering accounts and passwords, and authorization waiting, users can click the Sign In with Huawei ID button to quickly and securely sign in to app.

HUAWEI Auth Service

Auth Service provides backend services and an SDK to authenticate users to your app. It supports multiple authentication providers such as Phone Number, Google Sign-In, Email ID and more, and report authentication credentials to the AppGallery Connect.

When a user signs in to an app again, the app can obtain the users personal information and other data protected by security rules in other server less functions from Auth Service.

Auth Service can greatly reduce your investment and costs in building an authentication system and its O&M.

Integration Preparations

To integrate HUAWEI Account Kit, you must complete the following preparations:

  • Create an app in AppGallery Connect.
  • Create a project in Android Studio.
  • Generate a signing certificate.
  • Generate a signing certificate fingerprint.
  • Configure the signing certificate fingerprint.
  • Add the app package name and save the configuration file.
  • Add the AppGallery Connect plug-in and the Maven repository in the project-level build.gradle file.
  • Configure the signature file in Android Studio.

Configuring the Development Environment

Enabling HUAWEI Account Kit and Auth Service

  1. Sign in to AppGallery Connect, select My apps and click an App. Choose Develop > Overview > Manage APIs. Alt Text

Enabling Authentication Modes

  1. Sign in to AppGallery Connect, select My apps, and click your App. Choose Develop > Build > Auth Service. If it is the first time that you use Auth Service, click Enable now in the upper right corner. Alt Text
  2. Click Enable in the row of each authentication mode to be enabled. In this codelab, click Enable for Huawei account, Huawei game, and Anonymous account. Alt Text
  3. In the displayed dialog box, configure app information. Required information can be obtained as follows: Huawei account: Obtain the app ID and secret by referring to Querying App Information.

Huawei game: Sign in to AppGallery Connect, select My apps, click your game. Choose Develop > Build > Google Sign In, and obtain the client id and client secret id.
Alt Text

Integrating the Account Kit and Auth Service SDK

If you are using Android Studio, you can integrate the App Linking SDK by using the Maven repository into your Android Studio project before development.

  1. Sign in to AppGallery Connect, select My apps and click your App. Choose Develop > Overview.
  2. Click agconnect-services.json to download the configuration file.
  3. Copy agconnect-services.json file to the app's root directory. Alt Text
  4. Open the build.gradle file in the root directory of your Android Studio project. Alt Text
  5. Configure the following information in the build.gradle file:
buildscript {
    ext.kotlin_version = '1.3.50'
    repositories {
        google()
        jcenter()
        maven {url 'https://developer.huawei.com/repo/'}
        mavenCentral()
    }
    dependencies {
       classpath 'com.android.tools.build:gradle:4.0.0'
       classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
       classpath 'com.huawei.agconnect:agcp:1.3.2.301'
    }
}
  1. Open the build.gradle file in the app directory. Alt Text
  2. Configure the HUAWEI Account Kit service address, Auth Service SDK address, and HUAWEI Game Service address.

     

    // Apply the APM plug-in.
    apply plugin: 'com.huawei.agconnect'
    dependencies {
      implementation 'com.huawei.hms:hwid:4.0.1.300'          
      implementation 'com.huawei.agconnect:agconnect-auth:1.4.0.300'     
      implementation 'net.openid:appauth:0.7.1'
    }
    
  3. Click Sync Now to synchronize the configuration.
    Alt Text

  4. Add into your Manifest.

<activity android:name="net.openid.appauth.RedirectUriReceiverActivity">
     <intent-filter>
         <action android:name="android.intent.action.VIEW"/>
         <category android:name="android.intent.category.DEFAULT"/>
         <category android:name="android.intent.category.BROWSABLE"/>
         <data android:scheme="Your package name"/>
     </intent-filter>
 </activity> 

  1. Add your app client Id from Google Developer Console for Google Sign In.
<?xml version="1.0" encoding="utf-8"?>
   <resources>
     <!--    android-client id-->
     <string name="google_client_ids">Your Client ID</string>
     <string name="redirect_uri">"your_package_name:/oauth2callback"</string>
 </resources>

Code Snipped

private var mAuthManager: HuaweiIdAuthService? = null
private var mAuthParam: HuaweiIdAuthParams? = null

  fun initHuaweiID()   {
      mAuthParam   = HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM)
          .setEmail()
          .setUid()
          .setProfile()
          .setMobileNumber()
          .setIdToken()
          .setAccessToken()
          .createParams()
      mAuthManager   = HuaweiIdAuthManager.getService(this, mAuthParam)
  }

  fun onLoginButtonClick(view : View) {
     if (!isConnected) {
         toast("No network connectivity")
     } else {
         startActivityForResult(mAuthManager?.signInIntent, RC_SIGN_IN)
     }
 }


 fun signInGoogle() {
     val serviceConfiguration =
         AuthorizationServiceConfiguration(
             Uri.parse("https://accounts.google.com/o/oauth2/auth"),  // authorization endpoint
             Uri.parse("https://oauth2.googleapis.com/token")
         )
     val authorizationService = AuthorizationService(this)
     val clientId = getString(R.string.google_client_ids)
     val redirectUri = Uri.parse(getString(R.string.redirect_uri))
     val builder = AuthorizationRequest.Builder(
         serviceConfiguration,
         clientId,
         ResponseTypeValues.CODE,
         redirectUri
     )
     builder.setScopes("openid email profile")
     val request = builder.build()
     val intent = authorizationService.getAuthorizationRequestIntent(request)
     startActivityForResult(
         intent,
         RC_GOOGLE_SIGN_IN
     )
 }

 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
     super.onActivityResult(requestCode, resultCode, data)
     if (requestCode == RC_SIGN_IN) {
         //login success
         //get user message by parseAuthResultFromIntent
         val authHuaweiIdTask =
             HuaweiIdAuthManager.parseAuthResultFromIntent(data)
         if (authHuaweiIdTask.isSuccessful) {
             val huaweiAccount = authHuaweiIdTask.result
             Log.i(
                 TAG,
                 huaweiAccount.uid + " signIn success "
             )
             Log.i(
                 TAG,
                 "AccessToken: " + huaweiAccount.accessToken
             )
             displayInfo(huaweiAccount)
         } else {
             Log.i(
                 TAG,
                 "signIn failed: " + (authHuaweiIdTask.exception as ApiException).statusCode
             )
         }
     } else if (requestCode == RC_GOOGLE_SIGN_IN) {
         val response = AuthorizationResponse.fromIntent(data!!)
         val error = AuthorizationException.fromIntent(data)
         val authState =
             AuthState(response, error)
         if (response != null) {
             Log.i(
                 TAG,
                 String.format("Handled Authorization Response %s ", authState.toString())
             )
             val service = AuthorizationService(this)
             service.performTokenRequest(
                 response.createTokenExchangeRequest()
             ) { tokenResponse, exception ->
                 if (exception != null) {
                     Log.w(
                         TAG,
                         "Token Exchange failed",
                         exception
                     )
                 } else {
                     if (tokenResponse != null) {
                         val suffixUrl = "tokeninfo?id_token=${tokenResponse.idToken}"
                         viewModel.getGmailMailID(suffixUrl)
                         viewModel.googleMailResponse.observe(this@LoginActivity, Observer {
                             if (it != null) {
                                 if (it.error.toString().contentEquals("invalid_token")) {
                                     Toast.makeText(this@LoginActivity, "${it.error_description}", Toast.LENGTH_LONG).show()
                                 } else{
                                     GoogleMailID = "${it.email}"
                                     Log.d("GoogleMail_ID ", "${it.email}")                                  
                                     tokenResponse.idToken?.let { agcAuthWithGoogle(it) }
                                 }
                             } else {
                                 Toast.makeText(this@LoginActivity, "Somethings error. Please try again later", Toast.LENGTH_LONG).show()
                             }
                         })                      
                     }
                 }
             }
         }
     }
     else {
         mCallbackManager!!.onActivityResult(requestCode, resultCode, data)
     }
 }


 fun getGmailMailID(url: String) {
     val googleUserModel: GoogleUserModel
     viewModelScope.launch {
         try {
             MyGoogleApi
                 .invoke()
                 .getGoogleMailID(url)
                 .enqueue(object : Callback<GoogleUserModel> {
                     override fun onFailure(call: Call<GoogleUserModel>, t: Throwable) {
                         Log.e(TAG, "onFailure: "+t.localizedMessage )
                         googleMailResponse.value = null
                     }

                     override fun onResponse(
                         call: Call<GoogleUserModel>,
                         response: Response<GoogleUserModel>
                     ) {
                         if (response.isSuccessful) {
                             googleMailResponse.value  = response.body()
                         } else {
                             googleMailResponse.value  = null
                         }
                     }

                 })
         } catch (e: Exception) {
             e.stackTrace
         }
     }
   }

   private fun createAccount(email: String, password: String) {
     val settings = VerifyCodeSettings.newBuilder()
         .action(VerifyCodeSettings.ACTION_REGISTER_LOGIN) //ACTION_REGISTER_LOGIN/ACTION_RESET_PASSWORD
         .sendInterval(30) // Minimum sending interval, ranging from 30s to 120s.
         .locale(Locale.getDefault()) // Language in which a verification code is sent, which is optional. The default value is Locale.getDefault.
         .build()
     val task =
         EmailAuthProvider.requestVerifyCode(email, settings)
     task.addOnSuccessListener(
         TaskExecutors.uiThread(),
         OnSuccessListener {
             Log.d("Email Auth", " Success")
             val inflater = layoutInflater
             val alertLayout: View =
                 inflater.inflate(R.layout.dialog_verification_code, null)
             val verifyBtn =
                 alertLayout.findViewById<Button>(R.id.verifyBtn)
             val edtverifyBtn = alertLayout.findViewById<EditText>(R.id.smsCodeEt)
             val alert =
                 AlertDialog.Builder(this)
             alert.setTitle("Verifying code")
             // this is set the view from XML inside AlertDialog
             alert.setView(alertLayout)
             // disallow cancel of AlertDialog on click of back button and outside touch
             alert.setCancelable(false)
             val dialog = alert.create()
             verifyBtn.setOnClickListener {
                 if (!edtverifyBtn.text.toString().isEmpty()) {
                     dialog.dismiss()
                     verifyEmailWithCode(
                         edtverifyBtn.text.toString(),
                         email,
                         password
                     )
                 } else {
                     Toast.makeText(
                         this@LoginActivity,
                         "Email Code must not be empty!",
                         Toast.LENGTH_LONG
                     ).show()
                 }
             }
             dialog.show()
         }).addOnFailureListener(
         TaskExecutors.uiThread(),
         OnFailureListener { e -> Log.d("Email Auth", " Failed " + e.message.toString()) })
 }

 private fun verifyEmailWithCode(
     code: String,
     email: String,
     password: String
 ) {
     val emailUser = EmailUser.Builder()
         .setEmail(email)
         .setVerifyCode(code)
         .setPassword(password) // Optional. If this parameter is set, the current user has created a password and can use the password to sign in.
         // If this parameter is not set, the user can only sign in using a verification code.
         .build()
     viewModel.AGCCreateUser_EmailVerificationCode(emailUser)
 }

 private fun startPhoneNumberVerification(phoneNumber: String) {
     val settings = VerifyCodeSettings.newBuilder()
         .action(VerifyCodeSettings.ACTION_REGISTER_LOGIN) //ACTION_REGISTER_LOGIN/ACTION_RESET_PASSWORD
         .sendInterval(30) // Minimum sending interval, which ranges from 30s to 120s.
         .locale(Locale.getDefault()) // Optional. It indicates the language for sending a verification code. The value of locale must contain the language and country/region information. The defualt value is Locale.getDefault.
         .build()
     PhoneAuthProvider.verifyPhoneCode(
         "91",  // Country Code
         phoneNumber,  // Phone Num
         settings,
         object : VerifyCodeSettings.OnVerifyCodeCallBack {
             override fun onVerifySuccess(
                 shortestInterval: String,
                 validityPeriod: String
             ) {
                 Log.d("Phone Auth", " Success")
             }

             override fun onVerifyFailure(e: Exception) {
                 Log.d("Phone Auth", " Failed " + e.message.toString())
             }
         })
 }

 private fun verifyPhoneNumberWithCode(
     code: String,
     phoneNumber: String
 ) {
     val phoneUser = PhoneUser.Builder()
         .setCountryCode("+91")
         .setPhoneNumber(phoneNumber) // The value of phoneNumber must contains the country/region code and mobile number.
         .setVerifyCode(code)
         .setPassword("Your password") // Mandatory. If this parameter is set, a password has been created for the current user by default and the user can sign in using the password.
         // Otherwise, the user can only sign in using a verification code.
         .build()
     viewModel.AGCCreateUser_VerificationCode(phoneUser)
 }


 private fun signin_withverificationcode() {
     val credential = PhoneAuthProvider.credentialWithVerifyCode(
         "+91",
         field_phone_number!!.text.toString(),
         "Your password",
         field_verification_code1!!.text.toString()
     )
     viewModel.AGCGoogleSignIn(credential)
 } 

 private fun validatePhoneNumber(): Boolean {
     val phoneNumber = field_phone_number!!.text.toString()
     if (TextUtils.isEmpty(phoneNumber)) {
         field_phone_number!!.error = "Invalid phone number."
         return false
     }
     return true
   }

Video Demo for Login with Mobile No
Alt Text

Reference
Account Kit:
https://developer.huawei.com/consumer/en/doc/development/HMS-Guides/account-introduction-v4

AGC Auth Service:
https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agc-auth-service-introduction

Posted on by:

singlebubble1 profile

singlebubble

@singlebubble1

live, laough and love coding, cooking and skiing

Discussion

markdown guide