• Introducing XDA Computing: Discussion zones for Hardware, Software, and more!    Check it out!
  • Fill out your device list and let everyone know which phones you have!    Edit Your Device Inventory

Sign In with Huawei ID in non-Huawei devices [Kotlin]

Search This thread

John Wo

Official Huawei Rep
Apr 28, 2020
182
22
More information like this, you can visit HUAWEI Developer Forum

In a previous post I've written about how to sign in with your Google account in devices without Google Play Services. But, what if a user has a Huawei ID and wants to use it to sign in your app from another device, maybe you can ask the user to install the HMS Core APK, but this can confuse some users. If this is your situation you can use the AppAuth library and the Huawei OAUTH APIs to easily offer the sign in with Huawei for all devices.

Previous requirements

A developer account

An app project on AGC

Adding the required dependencies

For this example we will use the next libraries:

AppAuth: It will allow us to open the sign in page in a web browser and catch the result in our app.

Okio: Required by AppAuth to process data.

Account Kit: Although we won't use the Account Kit capabilities, this dependency allow us to add the Huawei Id Sign In button.

Kotlin Coroutines: Will allow us obtain the user information and download the profile picture without blocking the main thread.

Material Components: Will be used to show a Snackbar telling the user if something went wrong.

Now add the corresponding dependencies in your app leve build.gradle:

Code:
implementation 'net.openid:appauth:0.7.1'
implementation 'com.squareup.okio:okio:1.15.0'
implementation 'com.huawei.agconnect:agconnect-core:1.4.1.300'
implementation 'com.huawei.hms:hwid:5.0.1.301'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7"
implementation 'com.google.android.material:material:1.2.0'

Configuring AppAuth

Add the next under the "application" node in your AndroidManifest

Code:
<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="com.huawei.apps.APP_ID" />
    </intent-filter>
</activity>

Replace APP_ID for your own app Id from the project view on your AGC console

2640052000002383434.20200904181213.57790242724850717930272221632637:50510917114619:2800:9A96454A06D683BD268A611107F1F949D9FD169BE3E85B9B1C53111D9369699F.png


App information panel on AGC

Add the next under defaultConfig in your app level build.gradle

Code:
manifestPlaceholders = [
        'appAuthRedirectScheme': 'com.huawei.apps.APP_ID'
]

It will look similar to this:

Code:
defaultConfig {
    applicationId "com.hms.demo.appauthhuawei"
    minSdkVersion 27
    targetSdkVersion 29
    versionCode 1
    versionName "1.0"
    manifestPlaceholders = [
            'appAuthRedirectScheme': 'com.huawei.apps.102839067'
    ]
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

Code:
<com.huawei.hms.support.hwid.ui.HuaweiIdAuthButton
    android:id="@+id/hwid"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="112dp"
    app:hwid_button_theme="hwid_button_theme_full_title"
    app:hwid_color_policy="hwid_color_policy_black"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.497"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

2640052000002383434.20200904193955.61935585652317294551195927074503:50510917114619:2800:2F7C30E0092D155F70C045C4C8BEC233B60944B687FC4849F2C26326F91C4352.jpg

Login screen

I will use the same activity to show the profile information, but will keep the views hidden until I get the data.

Code:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
 
    <com.huawei.hms.support.hwid.ui.HuaweiIdAuthButton
        android:id="@+id/hwid"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="112dp"
        app:hwid_button_theme="hwid_button_theme_full_title"
        app:hwid_color_policy="hwid_color_policy_black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.497"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
 
    <ImageView
        android:id="@+id/pic"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="216dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:srcCompat="@tools:sample/avatars"
        android:visibility="gone"/>
 
    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.501"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/pic"
        android:visibility="gone"/>
 
    <TextView
        android:id="@+id/mail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/name"
        android:visibility="gone"/>
</androidx.constraintlayout.widget.ConstraintLayout>

Making the Sign In request

Prepare all the required information to perform the request

Code:
val AUTH_ENDPOINT = "https://oauth-login.cloud.huawei.com/oauth2/v3/authorize"
val TOKEN_ENDPOINT = "https://oauth-login.cloud.huawei.com/oauth2/v3/token"
val APP_ID = "REPLACE WITH YOUR APP ID"
val HW_REDIRECT_URI = "com.huawei.apps.$APP_ID:/oauth2redirect"
val HW_ID_CODE = 100

Note: The endpoints above may change with the time, check this chart to get the current valid endpoints.

Add an OnClickListener to your Sign In button

Code:
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    hwid.setOnClickListener(this)
}
 
override fun onClick(v: View?) {
    when (v?.id) {
        R.id.hwid -> handleHuaweiId()
    }
}

Now is time to perform the request

Code:
private fun handleHuaweiId() {
 
    val serviceConfig = AuthorizationServiceConfiguration(
        Uri.parse(AUTH_ENDPOINT), // authorization endpoint
        Uri.parse(TOKEN_ENDPOINT)// token endpoint
    )
    val authRequestBuilder = AuthorizationRequest.Builder(
        serviceConfig,  // the authorization service configuration
        APP_ID,  // the client ID, typically pre-registered and static
        ResponseTypeValues.CODE,  //
        Uri.parse(HW_REDIRECT_URI)//The redirect URI
    )
 
    authRequestBuilder.setScope("openid email profile")
    val authRequest = authRequestBuilder.build()
    val authService = AuthorizationService(this)
    val authIntent = authService.getAuthorizationRequestIntent(authRequest)
    startActivityForResult(authIntent, HW_ID_CODE)
    authService.dispose()
}

This will open the Huawei Sign In page on the system default browser.

2640052000002383434.20200904194301.27935527615441021060269495702436:50510917114619:2800:9A251E11295F9CEF07015B525A5E787299A1BFC76D746A981D87DA8F2FCF7A8A.jpg

Huawei Sign In page

Now override the onActivityResult method to get the Sign In result back on your app.

Code:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {
        HW_ID_CODE -> handleHuaweiSignIn(data)
    }
}

You must parse the Auth result to get the Access Token and the ID Token.

Code:
private fun handleHuaweiSignIn(data: Intent?) {
    if (data == null) {
        return
    }
 
    val response = AuthorizationResponse.fromIntent(data)
    val ex = AuthorizationException.fromIntent(data)
    val authState = AuthState(response, ex)
    if (response == null) {
        Snackbar.make(hwid, "Response is null, please try again", Snackbar.LENGTH_SHORT).show()
        return
    }
    val service = AuthorizationService(this)
    service.performTokenRequest(
        response.createTokenExchangeRequest(),
    ) { tokenResponse, exception ->
        service.dispose()
        if (tokenResponse == null) {
            Snackbar.make(
                hwid,
                "Token Exchange failed ${exception?.code}",
                Snackbar.LENGTH_SHORT
            ).show()
        } else {
            authState.update(tokenResponse, exception)
            hwid.visibility = View.GONE
            obtainUserInfo(tokenResponse.idToken)
        }
    }
}

The id token contains the user's information encoded in the next format <headers.payload.signature>, so you must decode it to obtain the requested data. Use a coroutine to decode the information and download the user's profile picture.

Note: To get the user's email, you must add "email" to the request scopes and the user must check the email authorization checkbox on the authorization page. If the user doesn't mark the checkbox you won't receive the email.

Code:
private fun obtainUserInfo(idToken: String?) {
        CoroutineScope(IO).launch {
            if (idToken == null) [email protected]
            val json = decoded(idToken)
            if (json != null) {
                Log.e("JSON", json.toString())
                runOnUiThread {
                    name.text = json.getString("display_name")
                    name.visibility = View.VISIBLE
                    if (json.has("email")) {
                        mail.text = json.getString("email")
                        mail.visibility = View.VISIBLE
                    }
                }
                val bitmap = getBitmap(json.getString("picture"))
                if (bitmap != null) {
                    val resizedBitmap = getResizedBitmap(bitmap, 480, 480)
                    runOnUiThread {
                        pic.setImageBitmap(resizedBitmap)
                        pic.visibility = View.VISIBLE
                    }
                }
            }
            //val conn=URL("https://oauth-login.cloud.huawei.com/.well-known/openid-configuration").openConnection() as HttpURLConnection
            //conn.requestMethod="POST"
 
        }
    }
 
    @Throws(Exception::class)
    private fun decoded(JWTEncoded: String): JSONObject? {
 
        try {
            val split = JWTEncoded.split(".").toTypedArray()
            return JSONObject(getJson(split[1]))
        } catch (e: UnsupportedEncodingException) {
            //Error
            return null
        }
    }
 
    @Throws(UnsupportedEncodingException::class)
    private fun getJson(strEncoded: String): String {
        val decodedBytes: ByteArray = Base64.decode(strEncoded, Base64.URL_SAFE)
        return String(decodedBytes, StandardCharsets.UTF_8)
    }
}

2640052000002383434.20200904200758.92260655759113727469356467994086:50510917114619:2800:BA25CED1933BC0AE36EE5C46B71AF3E41BF2287FF9F7E983D3504E8563B99BB3.png

Profile Screen

Conclusion

Now all your users can use their Huawei IDs to Sign In your app, even in non-Huawei devices. To keep the user signed I recommend you to use Huawei Auth Service.

Check the full example here: https://github.com/DTSE-HWI-Q-A/AppAuthHuawei

Reference

https://developer.huawei.com/consumer/en/doc/HMSCore-Guides-V5/app-auth-access-huaweiid-0000001050434521-V5
 
  • Like
Reactions: Juan7551

Top Liked Posts

  • There are no posts matching your filters.
  • 1
    More information like this, you can visit HUAWEI Developer Forum

    In a previous post I've written about how to sign in with your Google account in devices without Google Play Services. But, what if a user has a Huawei ID and wants to use it to sign in your app from another device, maybe you can ask the user to install the HMS Core APK, but this can confuse some users. If this is your situation you can use the AppAuth library and the Huawei OAUTH APIs to easily offer the sign in with Huawei for all devices.

    Previous requirements

    A developer account

    An app project on AGC

    Adding the required dependencies

    For this example we will use the next libraries:

    AppAuth: It will allow us to open the sign in page in a web browser and catch the result in our app.

    Okio: Required by AppAuth to process data.

    Account Kit: Although we won't use the Account Kit capabilities, this dependency allow us to add the Huawei Id Sign In button.

    Kotlin Coroutines: Will allow us obtain the user information and download the profile picture without blocking the main thread.

    Material Components: Will be used to show a Snackbar telling the user if something went wrong.

    Now add the corresponding dependencies in your app leve build.gradle:

    Code:
    implementation 'net.openid:appauth:0.7.1'
    implementation 'com.squareup.okio:okio:1.15.0'
    implementation 'com.huawei.agconnect:agconnect-core:1.4.1.300'
    implementation 'com.huawei.hms:hwid:5.0.1.301'
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7"
    implementation 'com.google.android.material:material:1.2.0'

    Configuring AppAuth

    Add the next under the "application" node in your AndroidManifest

    Code:
    <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="com.huawei.apps.APP_ID" />
        </intent-filter>
    </activity>

    Replace APP_ID for your own app Id from the project view on your AGC console

    2640052000002383434.20200904181213.57790242724850717930272221632637:50510917114619:2800:9A96454A06D683BD268A611107F1F949D9FD169BE3E85B9B1C53111D9369699F.png


    App information panel on AGC

    Add the next under defaultConfig in your app level build.gradle

    Code:
    manifestPlaceholders = [
            'appAuthRedirectScheme': 'com.huawei.apps.APP_ID'
    ]

    It will look similar to this:

    Code:
    defaultConfig {
        applicationId "com.hms.demo.appauthhuawei"
        minSdkVersion 27
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        manifestPlaceholders = [
                'appAuthRedirectScheme': 'com.huawei.apps.102839067'
        ]
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    Code:
    <com.huawei.hms.support.hwid.ui.HuaweiIdAuthButton
        android:id="@+id/hwid"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="112dp"
        app:hwid_button_theme="hwid_button_theme_full_title"
        app:hwid_color_policy="hwid_color_policy_black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.497"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    2640052000002383434.20200904193955.61935585652317294551195927074503:50510917114619:2800:2F7C30E0092D155F70C045C4C8BEC233B60944B687FC4849F2C26326F91C4352.jpg

    Login screen

    I will use the same activity to show the profile information, but will keep the views hidden until I get the data.

    Code:
    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
     
        <com.huawei.hms.support.hwid.ui.HuaweiIdAuthButton
            android:id="@+id/hwid"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="112dp"
            app:hwid_button_theme="hwid_button_theme_full_title"
            app:hwid_color_policy="hwid_color_policy_black"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.497"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
     
        <ImageView
            android:id="@+id/pic"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="216dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:srcCompat="@tools:sample/avatars"
            android:visibility="gone"/>
     
        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.501"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/pic"
            android:visibility="gone"/>
     
        <TextView
            android:id="@+id/mail"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/name"
            android:visibility="gone"/>
    </androidx.constraintlayout.widget.ConstraintLayout>

    Making the Sign In request

    Prepare all the required information to perform the request

    Code:
    val AUTH_ENDPOINT = "https://oauth-login.cloud.huawei.com/oauth2/v3/authorize"
    val TOKEN_ENDPOINT = "https://oauth-login.cloud.huawei.com/oauth2/v3/token"
    val APP_ID = "REPLACE WITH YOUR APP ID"
    val HW_REDIRECT_URI = "com.huawei.apps.$APP_ID:/oauth2redirect"
    val HW_ID_CODE = 100

    Note: The endpoints above may change with the time, check this chart to get the current valid endpoints.

    Add an OnClickListener to your Sign In button

    Code:
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        hwid.setOnClickListener(this)
    }
     
    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.hwid -> handleHuaweiId()
        }
    }

    Now is time to perform the request

    Code:
    private fun handleHuaweiId() {
     
        val serviceConfig = AuthorizationServiceConfiguration(
            Uri.parse(AUTH_ENDPOINT), // authorization endpoint
            Uri.parse(TOKEN_ENDPOINT)// token endpoint
        )
        val authRequestBuilder = AuthorizationRequest.Builder(
            serviceConfig,  // the authorization service configuration
            APP_ID,  // the client ID, typically pre-registered and static
            ResponseTypeValues.CODE,  //
            Uri.parse(HW_REDIRECT_URI)//The redirect URI
        )
     
        authRequestBuilder.setScope("openid email profile")
        val authRequest = authRequestBuilder.build()
        val authService = AuthorizationService(this)
        val authIntent = authService.getAuthorizationRequestIntent(authRequest)
        startActivityForResult(authIntent, HW_ID_CODE)
        authService.dispose()
    }

    This will open the Huawei Sign In page on the system default browser.

    2640052000002383434.20200904194301.27935527615441021060269495702436:50510917114619:2800:9A251E11295F9CEF07015B525A5E787299A1BFC76D746A981D87DA8F2FCF7A8A.jpg

    Huawei Sign In page

    Now override the onActivityResult method to get the Sign In result back on your app.

    Code:
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (requestCode) {
            HW_ID_CODE -> handleHuaweiSignIn(data)
        }
    }

    You must parse the Auth result to get the Access Token and the ID Token.

    Code:
    private fun handleHuaweiSignIn(data: Intent?) {
        if (data == null) {
            return
        }
     
        val response = AuthorizationResponse.fromIntent(data)
        val ex = AuthorizationException.fromIntent(data)
        val authState = AuthState(response, ex)
        if (response == null) {
            Snackbar.make(hwid, "Response is null, please try again", Snackbar.LENGTH_SHORT).show()
            return
        }
        val service = AuthorizationService(this)
        service.performTokenRequest(
            response.createTokenExchangeRequest(),
        ) { tokenResponse, exception ->
            service.dispose()
            if (tokenResponse == null) {
                Snackbar.make(
                    hwid,
                    "Token Exchange failed ${exception?.code}",
                    Snackbar.LENGTH_SHORT
                ).show()
            } else {
                authState.update(tokenResponse, exception)
                hwid.visibility = View.GONE
                obtainUserInfo(tokenResponse.idToken)
            }
        }
    }

    The id token contains the user's information encoded in the next format <headers.payload.signature>, so you must decode it to obtain the requested data. Use a coroutine to decode the information and download the user's profile picture.

    Note: To get the user's email, you must add "email" to the request scopes and the user must check the email authorization checkbox on the authorization page. If the user doesn't mark the checkbox you won't receive the email.

    Code:
    private fun obtainUserInfo(idToken: String?) {
            CoroutineScope(IO).launch {
                if (idToken == null) [email protected]
                val json = decoded(idToken)
                if (json != null) {
                    Log.e("JSON", json.toString())
                    runOnUiThread {
                        name.text = json.getString("display_name")
                        name.visibility = View.VISIBLE
                        if (json.has("email")) {
                            mail.text = json.getString("email")
                            mail.visibility = View.VISIBLE
                        }
                    }
                    val bitmap = getBitmap(json.getString("picture"))
                    if (bitmap != null) {
                        val resizedBitmap = getResizedBitmap(bitmap, 480, 480)
                        runOnUiThread {
                            pic.setImageBitmap(resizedBitmap)
                            pic.visibility = View.VISIBLE
                        }
                    }
                }
                //val conn=URL("https://oauth-login.cloud.huawei.com/.well-known/openid-configuration").openConnection() as HttpURLConnection
                //conn.requestMethod="POST"
     
            }
        }
     
        @Throws(Exception::class)
        private fun decoded(JWTEncoded: String): JSONObject? {
     
            try {
                val split = JWTEncoded.split(".").toTypedArray()
                return JSONObject(getJson(split[1]))
            } catch (e: UnsupportedEncodingException) {
                //Error
                return null
            }
        }
     
        @Throws(UnsupportedEncodingException::class)
        private fun getJson(strEncoded: String): String {
            val decodedBytes: ByteArray = Base64.decode(strEncoded, Base64.URL_SAFE)
            return String(decodedBytes, StandardCharsets.UTF_8)
        }
    }

    2640052000002383434.20200904200758.92260655759113727469356467994086:50510917114619:2800:BA25CED1933BC0AE36EE5C46B71AF3E41BF2287FF9F7E983D3504E8563B99BB3.png

    Profile Screen

    Conclusion

    Now all your users can use their Huawei IDs to Sign In your app, even in non-Huawei devices. To keep the user signed I recommend you to use Huawei Auth Service.

    Check the full example here: https://github.com/DTSE-HWI-Q-A/AppAuthHuawei

    Reference

    https://developer.huawei.com/consumer/en/doc/HMSCore-Guides-V5/app-auth-access-huaweiid-0000001050434521-V5