[Huawei Developers] John Wo:
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:

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

<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

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/002/383/434/2640052000002383434.20200904181213.57790242724850717930272221632637:50510917114619:2800:9A96454A06D683BD268A611107F1F949D9FD169BE3E85B9B1C53111D9369699F.png"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/002/383/434/2640052000002383434.20200904181213.57790242724850717930272221632637:50510917114619:2800:9A96454A06D683BD268A611107F1F949D9FD169BE3E85B9B1C53111D9369699F.png[/IMG]

App information panel on AGC

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

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

It will look similar to this:

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"
}

<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" />

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/002/383/434/2640052000002383434.20200904193955.61935585652317294551195927074503:50510917114619:2800:2F7C30E0092D155F70C045C4C8BEC233B60944B687FC4849F2C26326F91C4352.jpg"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/002/383/434/2640052000002383434.20200904193955.61935585652317294551195927074503:50510917114619:2800:2F7C30E0092D155F70C045C4C8BEC233B60944B687FC4849F2C26326F91C4352.jpg[/IMG]

Login screen

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

<?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

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

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

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.

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/002/383/434/2640052000002383434.20200904194301.27935527615441021060269495702436:50510917114619:2800:9A251E11295F9CEF07015B525A5E787299A1BFC76D746A981D87DA8F2FCF7A8A.jpg"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/002/383/434/2640052000002383434.20200904194301.27935527615441021060269495702436:50510917114619:2800:9A251E11295F9CEF07015B525A5E787299A1BFC76D746A981D87DA8F2FCF7A8A.jpg[/IMG]

Huawei Sign In page

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

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.

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.

private fun obtainUserInfo(idToken: String?) {
CoroutineScope(IO).launch {
if (idToken == null) return@launch
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)
}
}

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/002/383/434/2640052000002383434.20200904200758.92260655759113727469356467994086:50510917114619:2800:BA25CED1933BC0AE36EE5C46B71AF3E41BF2287FF9F7E983D3504E8563B99BB3.png"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/002/383/434/2640052000002383434.20200904200758.92260655759113727469356467994086:50510917114619:2800:BA25CED1933BC0AE36EE5C46B71AF3E41BF2287FF9F7E983D3504E8563B99BB3.png[/IMG]

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