diff --git a/app/HmsDemo.jks b/app/HmsDemo.jks
new file mode 100644
index 0000000..ac79e3e
Binary files /dev/null and b/app/HmsDemo.jks differ
diff --git a/app/agconnect-services.json b/app/agconnect-services.json
new file mode 100644
index 0000000..6ece27f
--- /dev/null
+++ b/app/agconnect-services.json
@@ -0,0 +1,11 @@
+{
+ "client":{
+ "cp_id":"xxxxxx",
+ "product_id":"xxxxxx",
+ "client_id":"xxxxxx",
+ "client_secret":"xxxxxx",
+ "app_id":"xxxxxx",
+ "package_name":"com.jaychang.demo.sa"
+ },
+ "configuration_version":"1.0"
+}
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 89616ff..4db60a6 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
+apply plugin: 'com.huawei.agconnect'
+
buildscript {
repositories {
@@ -19,14 +21,38 @@ android {
minSdkVersion deps.build.minSdkVersion
targetSdkVersion deps.build.targetSdkVersion
versionCode 1
+ multiDexEnabled true
versionName "1.0"
}
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ signingConfigs {
+ release {
+ storeFile file('HmsDemo.jks')
+ keyAlias 'HmsDemo'
+ keyPassword 'xxxxxx'
+ storePassword 'xxxxxx'
+ v1SigningEnabled true
+ v2SigningEnabled true
+ }
+ }
+
buildTypes {
release {
+ signingConfig signingConfigs.release
minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ debug {
+ signingConfig signingConfigs.release
+ debuggable true
}
}
+
lintOptions {
abortOnError false
}
@@ -51,4 +77,8 @@ dependencies {
implementation project(':library-google')
implementation project(':library-instagram')
implementation project(':library-twitter')
+ implementation project(':library-huawei')
+ implementation 'com.huawei.agconnect:agconnect-core:1.0.0.300'
+
+ implementation 'com.android.support:multidex:1.0.1'
}
diff --git a/app/src/main/java/com/jaychang/demo/sa/MainActivity.kt b/app/src/main/java/com/jaychang/demo/sa/MainActivity.kt
index f4d3749..77f2358 100644
--- a/app/src/main/java/com/jaychang/demo/sa/MainActivity.kt
+++ b/app/src/main/java/com/jaychang/demo/sa/MainActivity.kt
@@ -9,10 +9,12 @@ import com.jaychang.sa.SocialUser
import com.jaychang.sa.facebook.SimpleAuth
import java.util.*
+
class MainActivity : AppCompatActivity() {
companion object {
const val FACEBOOK = "FACEBOOK"
+ const val HUAWEI = "HUAWEI"
const val GOOGLE = "GOOGLE"
const val TWITTER = "TWITTER"
const val INSTAGRAM = "INSTAGRAM"
@@ -41,6 +43,22 @@ class MainActivity : AppCompatActivity() {
})
}
+ fun connectHuawei(view: View) {
+ com.jaychang.sa.huawei.SimpleAuth.connectHuawei(object : AuthCallback {
+ override fun onSuccess(socialUser: SocialUser) {
+ ProfileActivity.start(this@MainActivity, HUAWEI, socialUser)
+ }
+
+ override fun onError(error: Throwable) {
+ toast(error.message ?: "")
+ }
+
+ override fun onCancel() {
+ toast("Canceled")
+ }
+ })
+ }
+
fun connectGoogle(view: View) {
val scopes = Arrays.asList(
"https://www.googleapis.com/auth/youtube",
diff --git a/app/src/main/java/com/jaychang/demo/sa/ProfileActivity.kt b/app/src/main/java/com/jaychang/demo/sa/ProfileActivity.kt
index c32a115..4ee0fb6 100644
--- a/app/src/main/java/com/jaychang/demo/sa/ProfileActivity.kt
+++ b/app/src/main/java/com/jaychang/demo/sa/ProfileActivity.kt
@@ -39,6 +39,7 @@ class ProfileActivity : AppCompatActivity() {
fun disconnect(view: View) {
when (type) {
+ MainActivity.HUAWEI -> com.jaychang.sa.huawei.SimpleAuth.disconnectHuawei()
MainActivity.FACEBOOK -> SimpleAuth.disconnectFacebook()
MainActivity.GOOGLE -> com.jaychang.sa.google.SimpleAuth.disconnectGoogle()
MainActivity.TWITTER -> com.jaychang.sa.twitter.SimpleAuth.disconnectTwitter()
@@ -49,6 +50,7 @@ class ProfileActivity : AppCompatActivity() {
fun revoke(view: View) {
when (type) {
+ MainActivity.HUAWEI -> com.jaychang.sa.huawei.SimpleAuth.revokeHuawei()
MainActivity.FACEBOOK -> SimpleAuth.revokeFacebook()
MainActivity.GOOGLE -> com.jaychang.sa.google.SimpleAuth.revokeGoogle()
MainActivity.TWITTER -> {
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index dbeccdb..60ae2a7 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -6,6 +6,20 @@
android:background="#555"
android:orientation="vertical">
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 8796816..f9f2867 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,5 +1,6 @@
SimpleAuth
+ connect Huaweiconnect Facebookconnect Googleconnect Twitter
diff --git a/build.gradle b/build.gradle
index 68c4b2e..a650302 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,11 +4,13 @@ buildscript {
apply from: 'gradle/dependencies.gradle'
repositories {
+ maven { url 'http://developer.huawei.com/repo/' }
mavenCentral()
jcenter()
google()
}
dependencies {
+ classpath 'com.huawei.agconnect:agcp:1.2.0.300'
classpath deps.build.gradlePlugins.android
classpath deps.build.gradlePlugins.bintray
// NOTE: Do not place your application dependencies here; they belong
@@ -18,6 +20,7 @@ buildscript {
allprojects {
repositories {
+ maven { url 'http://developer.huawei.com/repo/' }
mavenCentral()
jcenter()
google()
diff --git a/library-common/src/main/java/com/jaychang/sa/AuthDataHolder.java b/library-common/src/main/java/com/jaychang/sa/AuthDataHolder.java
index 446f944..35dd80b 100644
--- a/library-common/src/main/java/com/jaychang/sa/AuthDataHolder.java
+++ b/library-common/src/main/java/com/jaychang/sa/AuthDataHolder.java
@@ -3,6 +3,7 @@
public class AuthDataHolder {
private static final AuthDataHolder instance = new AuthDataHolder();
+ public AuthData huaweiAuthData;
public AuthData facebookAuthData;
public AuthData googleAuthData;
public AuthData twitterAuthData;
diff --git a/library-huawei/.gitignore b/library-huawei/.gitignore
new file mode 100644
index 0000000..149412d
--- /dev/null
+++ b/library-huawei/.gitignore
@@ -0,0 +1,68 @@
+# Created by .ignore support plugin (hsz.mobi)
+### Android template
+# Built application files
+*.apk
+*.ap_
+
+# Files for the ART/Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+out/
+
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+*.log
+
+# Android Studio Navigation editor temp files
+.navigation/
+
+# Android Studio captures folder
+captures/
+
+# IntelliJ
+*.iml
+.idea/workspace.xml
+.idea/tasks.xml
+.idea/gradle.xml
+.idea/assetWizardSettings.xml
+.idea/dictionaries
+.idea/libraries
+.idea/caches
+
+# Keystore files
+# Uncomment the following line if you do not want to check your keystore files in.
+#*.jks
+
+# External native build folder generated in Android Studio 2.2 and later
+.externalNativeBuild
+
+# Google Services (e.g. APIs or Firebase)
+google-services.json
+
+# Freeline
+freeline.py
+freeline/
+freeline_project_description.json
+
+# fastlane
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots
+fastlane/test_output
+fastlane/readme.md
+
diff --git a/library-huawei/LICENSE b/library-huawei/LICENSE
new file mode 100644
index 0000000..82d0bd6
--- /dev/null
+++ b/library-huawei/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2018 HUAWEI
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/library-huawei/README.md b/library-huawei/README.md
new file mode 100644
index 0000000..d7d48f7
--- /dev/null
+++ b/library-huawei/README.md
@@ -0,0 +1,11 @@
+#### Function introduction
+
+- The process of login with Huawei account authorization
+- Obtain basic user information (user ID, avatar, nickname, etc.) of Huawei account opening
+- Cancel the authorization of the application through the authorization cancellation API interface
+
+#### Instruction for use
+
+- 1. Please refer to the HMS core3.0 Huawei account access guide to complete the access preparation. The link is: https://developer.huawei.com/consumer/cn/codelab/hmsaccounts/index.html.
+- 2. After the preparation, download "agconnect-services. json" in the application and replace the file in the root path of the app.
+
diff --git a/library-huawei/Third Party Open Source Notice.docx b/library-huawei/Third Party Open Source Notice.docx
new file mode 100644
index 0000000..07f6448
Binary files /dev/null and b/library-huawei/Third Party Open Source Notice.docx differ
diff --git a/library-huawei/build.gradle b/library-huawei/build.gradle
new file mode 100644
index 0000000..16e5320
--- /dev/null
+++ b/library-huawei/build.gradle
@@ -0,0 +1,64 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'com.novoda.bintray-release'
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath deps.build.gradlePlugins.kotlin
+ classpath deps.build.gradlePlugins.bintray
+ }
+}
+
+android {
+ compileSdkVersion deps.build.compileSdkVersion
+ buildToolsVersion deps.build.buildToolsVersion
+
+ defaultConfig {
+// minSdkVersion 17
+ minSdkVersion deps.build.minSdkVersion
+ targetSdkVersion deps.build.targetSdkVersion
+ versionCode 1
+ versionName "1.0.0"
+ }
+
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ lintOptions {
+ abortOnError false
+ }
+}
+
+dependencies {
+ api project(':library-common')
+ implementation deps.support.appcompat
+// api deps.huaweiAuth
+// implementation('com.huawei.hms:hwid:3.0.2.301')
+ implementation('com.huawei.hms:hwid:4.0.0.300')
+// implementation 'com.android.support:support-compat:28.0.0'
+ implementation "com.google.code.gson:gson:2.8.5"
+ implementation "com.squareup.okhttp3:okhttp:3.14.2"
+ implementation 'com.squareup.okio:okio:1.14.1'
+ implementation 'com.auth0:java-jwt:3.8.2'
+ implementation 'com.auth0:jwks-rsa:0.8.3'
+ api 'com.huawei.hmf:tasks:1.3.3.300'
+}
+
+repositories { flatDir { dirs '../aars' } }
+
+
+publish {
+ userOrg = 'jaychang0917'
+ groupId = 'com.jaychang'
+ artifactId = 'simpleauth-huawei'
+ publishVersion = '2.1.4'
+ desc = 'A easy to use social authentication android library. (Facebook, Google, Twitter, Instagram, Huawei)'
+ website = 'https://github.com/jaychang0917/SimpleAuth'
+}
diff --git a/library-huawei/proguard-rules.pro b/library-huawei/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/library-huawei/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/library-huawei/src/main/AndroidManifest.xml b/library-huawei/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..3ae5b36
--- /dev/null
+++ b/library-huawei/src/main/AndroidManifest.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/library-huawei/src/main/java/com/jaychang/sa/huawei/Constant.java b/library-huawei/src/main/java/com/jaychang/sa/huawei/Constant.java
new file mode 100644
index 0000000..fc3785f
--- /dev/null
+++ b/library-huawei/src/main/java/com/jaychang/sa/huawei/Constant.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jaychang.sa.huawei;
+
+/**
+ * Request code
+ */
+public class Constant {
+ //login
+ public static final int REQUEST_SIGN_IN_LOGIN = 1002;
+ /**
+ * your app’s client ID,please replace it of yours
+ */
+ public static final String CLIENT_ID = "101090009";
+
+ /**
+ * JWK JSON Web Key endpoint, developer can get the JWK of the last two days from this endpoint
+ * See more about JWK in http://self-issued.info/docs/draft-ietf-jose-json-web-key.html
+ */
+ public static final String CERT_URL = "https://oauth-login.cloud.huawei.com/oauth2/v3/certs";
+ /**
+ * Id Token issue
+ */
+ public static final String ID_TOKEN_ISSUE = "https://accounts.huawei.com";
+}
diff --git a/library-huawei/src/main/java/com/jaychang/sa/huawei/HuaweiIdActivity.java b/library-huawei/src/main/java/com/jaychang/sa/huawei/HuaweiIdActivity.java
new file mode 100644
index 0000000..54b294d
--- /dev/null
+++ b/library-huawei/src/main/java/com/jaychang/sa/huawei/HuaweiIdActivity.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2020 Huawei Technologies Co., Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jaychang.sa.huawei;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.huawei.hmf.tasks.OnFailureListener;
+import com.huawei.hmf.tasks.OnSuccessListener;
+import com.huawei.hmf.tasks.Task;
+
+import com.huawei.hms.support.hwid.HuaweiIdAuthManager;
+import com.huawei.hms.support.hwid.request.HuaweiIdAuthParams;
+import com.huawei.hms.support.hwid.request.HuaweiIdAuthParamsHelper;
+import com.huawei.hms.support.hwid.result.AuthHuaweiId;
+import com.huawei.hms.support.hwid.service.HuaweiIdAuthService;
+import com.jaychang.sa.AuthData;
+import com.jaychang.sa.AuthDataHolder;
+import com.jaychang.sa.SimpleAuthActivity;
+import com.jaychang.sa.SocialUser;
+import com.jaychang.sa.huawei.common.ICallBack;
+
+public class HuaweiIdActivity extends SimpleAuthActivity {
+ // Log tag
+ private static final String TAG = "HuaweiIdActivity";
+
+ private static HuaweiIdAuthService mAuthManager;
+
+ private HuaweiIdAuthParams mAuthParam;
+
+ public static void start(Context context) {
+ Intent intent = new Intent(context, HuaweiIdActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ //Initialize the HuaweiIdSignInClient object by calling the getClient method of HuaweiIdSignIn
+ mAuthParam = new HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM)
+ .setIdToken()
+ .setAccessToken()
+ .createParams();
+ mAuthManager = HuaweiIdAuthManager.getService(HuaweiIdActivity.this, mAuthParam);
+
+ signIn();
+ }
+
+ @Override
+ protected AuthData getAuthData() {
+ return AuthDataHolder.getInstance().huaweiAuthData;
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ }
+
+ private void signIn() {
+ startActivityForResult(mAuthManager.getSignInIntent(), Constant.REQUEST_SIGN_IN_LOGIN);
+ }
+
+ public static void signOut() {
+ Task signOutTask = mAuthManager.signOut();
+ signOutTask.addOnSuccessListener(new OnSuccessListener() {
+ @Override
+ public void onSuccess(Void aVoid) {
+ Log.i(TAG, "signOut Success");
+ }
+ }).addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(Exception e) {
+ Log.i(TAG, "signOut fail");
+ }
+ });
+ }
+
+ public static void revoke() {
+ Task revokeTask = mAuthManager.cancelAuthorization();
+ revokeTask
+ .addOnSuccessListener(
+ new OnSuccessListener() {
+ @Override
+ public void onSuccess(Void aVoid) {
+ Log.i(TAG, "revoke Success");
+ }
+ })
+ .addOnFailureListener(
+ new OnFailureListener() {
+ @Override
+ public void onFailure(Exception e) {
+ Log.i(TAG, "revoke fail");
+ }
+ });
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode != Constant.REQUEST_SIGN_IN_LOGIN) {
+ handCancel();
+ return;
+ }
+ // login success
+ // get user message by getSignedInAccountFromIntent
+ Task authHuaweiIdTask = HuaweiIdAuthManager.parseAuthResultFromIntent(data);
+ if (authHuaweiIdTask.isSuccessful()) {
+ AuthHuaweiId huaweiAccount = authHuaweiIdTask.getResult();
+ Log.i(TAG, huaweiAccount.getDisplayName() + " signIn success ");
+ Log.i(TAG, "AccessToken: " + huaweiAccount.getAccessToken());
+ validateIdToken(huaweiAccount.getIdToken());
+ handleSignInResult(huaweiAccount);
+ } else {
+ Log.i(TAG, "signIn failed: " + authHuaweiIdTask.getException().getMessage());
+ }
+ }
+
+ private void validateIdToken(String idToken) {
+ if (TextUtils.isEmpty(idToken)) {
+ Log.i(TAG, "ID Token is empty");
+ } else {
+ IDTokenParser idTokenParser = new IDTokenParser();
+ try {
+ idTokenParser.verify(idToken, new ICallBack() {
+ @Override
+ public void onSuccess() {
+ }
+
+ @Override
+ public void onSuccess(String idTokenJsonStr) {
+ if (!TextUtils.isEmpty(idTokenJsonStr)) {
+ Log.i(TAG, "id Token Validate Success, verify signature: " + idTokenJsonStr);
+ } else {
+ Log.i(TAG, "Id token validate failed.");
+ }
+ }
+
+ @Override
+ public void onFailed() {
+ Log.i(TAG, "Id token validate failed.");
+ }
+ });
+ } catch (Exception e) {
+ Log.i(TAG, "id Token validate failed." + e.getClass().getSimpleName());
+ } catch (Error e) {
+ Log.i(TAG, "id Token validate failed." + e.getClass().getSimpleName());
+ if (Build.VERSION.SDK_INT < 23) {
+ Log.i(TAG, "android SDK Version is not support. Current version is: " + Build.VERSION.SDK_INT);
+ }
+ }
+ }
+ }
+
+ private void handleSignInResult(AuthHuaweiId account) {
+ final SocialUser user = new SocialUser();
+ user.userId = account.getUid();
+ user.profilePictureUrl = account.getAvatarUriString() != null ? account.getAvatarUriString() : "";
+ user.email = account.getEmail();
+ user.fullName = account.getDisplayName();
+
+ handleSuccess(user);
+ }
+}
diff --git a/library-huawei/src/main/java/com/jaychang/sa/huawei/IDTokenParser.java b/library-huawei/src/main/java/com/jaychang/sa/huawei/IDTokenParser.java
new file mode 100644
index 0000000..e82000a
--- /dev/null
+++ b/library-huawei/src/main/java/com/jaychang/sa/huawei/IDTokenParser.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jaychang.sa.huawei;
+
+import java.io.IOException;
+import java.security.interfaces.RSAPublicKey;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.util.Base64;
+import android.util.Log;
+
+import com.auth0.jwk.InvalidPublicKeyException;
+import com.auth0.jwk.Jwk;
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.JWTDecodeException;
+import com.auth0.jwt.exceptions.JWTVerificationException;
+import com.auth0.jwt.exceptions.TokenExpiredException;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.jaychang.sa.huawei.common.ICallBack;
+
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+/**
+ * Function Description
+ * This is a demo for verify Id Token issued by HuaWei OAuth Server
+ * The demo just show how to verify Id Token in your local server
+ * The open source software depended by this demo may have vulnerabilities,
+ * please refer to the open source software release website and update to
+ * the latest version or replace it with other open source software.
+ * Local validation is much more efficiently than by access the tokeninfo endpoint
+ * You'd better learn more about the JWT and JWK for understanding this demo
+ * See more about JWT in https://jwt.io/
+ * See more about JWK in http://self-issued.info/docs/draft-ietf-jose-json-web-key.html
+ */
+
+public class IDTokenParser {
+ private final static int MAX_PUBLIC_KEY_SIZE = 4;
+
+ private JSONArray mJsonArray = null;
+
+ private RSAPublicKey mRSAPublicKey = null;
+
+ private static final String TAG = IDTokenParser.class.getSimpleName();
+
+ /**
+ * catch the public key in this map
+ */
+ private Map keyId2PublicKey = new HashMap<>();
+
+ public IDTokenParser(){
+ };
+
+ /**
+ * Verify Id Token
+ *
+ * @param idToken Your IdToken
+ * @param callBack Asyn CallBack
+ * @throws InvalidPublicKeyException throw when InvalidPublicKeyException happened
+ */
+ public void verify(String idToken, final ICallBack callBack) throws InvalidPublicKeyException, JWTDecodeException {
+ final DecodedJWT decoder = JWT.decode(idToken);
+ getRSAPublicKeyByKidAsyn(decoder.getKeyId(), new ICallBack() {
+ @Override
+ public void onSuccess() {
+ try {
+ Algorithm algorithm = Algorithm.RSA256(mRSAPublicKey , null);
+ JWTVerifier verifier = JWT.require(algorithm).build();
+ JSONObject jsonObject = new JSONObject(new String(Base64.decode(decoder.getPayload(), Base64.DEFAULT)));
+ // Verify the value of iss
+ if (!decoder.getIssuer().equals(Constant.ID_TOKEN_ISSUE)) {
+ callBack.onFailed();
+ return;
+ }
+ // Verify your app’s client ID.
+ String clientId = decoder.getAudience().get(0);
+ if(decoder.getAudience().size() > 0) {
+ if (!decoder.getAudience().get(0).equals(Constant.CLIENT_ID)) {
+ callBack.onFailed();
+ return;
+ }
+ }
+ // verify signature
+ verifier.verify(decoder);
+ jsonObject.put("alg", decoder.getAlgorithm());
+ jsonObject.put("typ", decoder.getType());
+ jsonObject.put("kid", decoder.getKeyId());
+ callBack.onSuccess(jsonObject.toString());
+ } catch (JWTDecodeException | JSONException e){
+ callBack.onFailed();
+ } catch (TokenExpiredException e) {
+ callBack.onFailed();
+ // jwt token is expire
+ } catch (JWTVerificationException e) {
+ callBack.onFailed();
+ // VERIFY SIGNATURE failed
+ } catch (Exception e) {
+ callBack.onFailed();
+ } catch (Error e) {
+ callBack.onFailed();
+ }
+ }
+
+ @Override
+ public void onSuccess(String result) {
+ }
+
+ @Override
+ public void onFailed() {
+ callBack.onFailed();
+ }
+ });
+ }
+
+ /**
+ * get the RSAPublicKey by kid
+ * Please cache the RSAPublicKey
+ * In the demo we cache it in a map
+ *
+ * @param keyId Input keyId
+ * @param callBack asyn callback
+ * @throws InvalidPublicKeyException throw when InvalidPublicKeyException happened
+ */
+ private void getRSAPublicKeyByKidAsyn(final String keyId, final ICallBack callBack) throws InvalidPublicKeyException {
+ getJwks(new ICallBack() {
+ @Override
+ public void onSuccess() {
+ if (keyId2PublicKey.get(keyId) != null) {
+ mRSAPublicKey = keyId2PublicKey.get(keyId);
+ callBack.onSuccess();
+ } else {
+ if (mJsonArray == null) {
+ mRSAPublicKey = null;
+ return;
+ }
+ if (keyId2PublicKey.size() > MAX_PUBLIC_KEY_SIZE){
+ keyId2PublicKey.clear();
+ }
+
+ try {
+ for (int i = 0; i < mJsonArray.length(); i++) {
+ String kid = mJsonArray.getJSONObject(i).getString("kid");
+ keyId2PublicKey.put(kid, getRsaPublicKeyByJwk(mJsonArray.getJSONObject(i)));
+ }
+ mRSAPublicKey = keyId2PublicKey.get(keyId);
+ callBack.onSuccess();
+ } catch (Exception e) {
+ mRSAPublicKey = null;
+ Log.i(TAG, "getRSAPublicKeyByKid failed: " + e.getMessage());
+ }
+ }
+ }
+
+ @Override
+ public void onSuccess(String keys) {
+ }
+
+ @Override
+ public void onFailed() {
+ mRSAPublicKey = null;
+ }
+ });
+ }
+
+ /**
+ * get jwks from the https://oauth-login.cloud.huawei.com/oauth2/v3/certs endpoint
+ * because the jwk update each day, please cache the jwk,here is the example of jwks
+ * See more about JWK in http://self-issued.info/docs/draft-ietf-jose-json-web-key.html
+ * {
+ * "keys":[
+ * {
+ * "kty":"RSA",
+ * "e":"AQAB",
+ * "use":"sig",
+ * "kid":"670c64e7443941582167f405e0a62a08c6db5becb090f397a45aa572fa000553",
+ * "alg":"RS256",
+ * "n":"AK4C-h_gWpziPmzo6PEBuwxHHD2F9x_LgiE5zl73fVmzBTo3KzRu8nXURQA-uV857r_qEhfsJQyy0Nr_wIkfAU86JsFHcGwzLlJucN12EHXOFY6nLti9tSWUAWaa2HAZuJytyc-DyguLR_nH5IKmGhmcgI26zUG07UPUB2Xnsn-T-K1npnaNI7K7xlbGQy5UUPFjQPBRiV2R_-iGf5KIqCwebXe24wzhbWMnmfb0lilAZyYO7PiQ8UgJJTuZOMbCD7P0dUJwxitHo81OyoVJUGQZpLBZqHVSsRpC0UZuxMEMBPza4R55yZS3gAKkE1xILabCUV-CJ6Gp4c4J1tiFNcc"
+ * },
+ * {
+ * "kty":"RSA",
+ * "e":"AQAB",
+ * "use":"sig",
+ * "kid":"1226cda6b82e6aa140ffe2f32515f1929c3048b2cdba267935fb71963fd3e57a",
+ * "alg":"RS256",
+ * "n":"AMNDMIxlySrGqeV7V3s865ZwzBD0hXVq8ys0H_ZQGMbfWss0WuwHrmIRdq8OQrYoN_o2KZKtUPBsJpJMAZ26JeGqf3dsU_wFEEjNOfrDdyIs86K6gKfQFLewUNycmzMhlqDFlCujAAF33RYn-Xg7UXG3pO_2PdcD1zIrxtawQZ-TQTMgH5mgX_lWO1YfYSuB91xEvUZII1ZYDjLcbkzjZCbvfU4tT2_PV8-gU0UaHI-pcyreUwB1EhleDZUW8MiaIVSr6UIYfHflxBQRrwCcFy-q_u-OeYBS683djmbF-FKZVKlipKim6hGpqIb-PC8pHO_WmM01sNvvMAkF1D5bg0M"
+ * }
+ * ]
+ * }
+ */
+ private void getJwks(final ICallBack iCallBack) {
+ OkHttpClient okHttpClient = new OkHttpClient();
+ final Request request = new Request.Builder()
+ .url(Constant.CERT_URL)
+ .build();
+ Call call = okHttpClient.newCall(request);
+ call.enqueue(new Callback() {
+ @Override
+ public void onFailure(Call call, IOException e) {
+ Log.i(TAG, "Get ID Token failed.");
+ iCallBack.onFailed();
+ }
+
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (response.isSuccessful()) {
+ try {
+ String res = response.body().string();
+ JSONObject jsonObject = new JSONObject(res);
+ mJsonArray = jsonObject.getJSONArray("keys");
+ iCallBack.onSuccess();
+ } catch (NullPointerException | JSONException | IOException e) {
+ Log.i(TAG, "parse JsonArray failed." + e.getMessage());
+ iCallBack.onFailed();
+ }
+ }
+ }
+ });
+ };
+
+ /**
+ * get RsaPublicKey from a JWK
+ * @param jwkObject received JSONObject
+ * @return RsaPublicKey from a JWK
+ * @throws InvalidPublicKeyException
+ */
+ private RSAPublicKey getRsaPublicKeyByJwk(JSONObject jwkObject) throws InvalidPublicKeyException, JSONException {
+ Map additionalAttributes = new HashMap<>();
+ additionalAttributes.put("n", jwkObject.getString("n"));
+ additionalAttributes.put("e", jwkObject.getString("e"));
+ List operations = new ArrayList<>();
+ Jwk jwk = new Jwk(
+ jwkObject.getString("kid"),
+ jwkObject.getString("kty"),
+ jwkObject.getString("alg"),
+ jwkObject.getString("use"),
+ operations,
+ null,
+ null,
+ null,
+ additionalAttributes);
+ return (RSAPublicKey)jwk.getPublicKey();
+ }
+}
+
diff --git a/library-huawei/src/main/java/com/jaychang/sa/huawei/SimpleAuthHuawei.kt b/library-huawei/src/main/java/com/jaychang/sa/huawei/SimpleAuthHuawei.kt
new file mode 100644
index 0000000..422372f
--- /dev/null
+++ b/library-huawei/src/main/java/com/jaychang/sa/huawei/SimpleAuthHuawei.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 Jay Chang Copyright 2020 Huawei Technologies Co., Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * 2020.1.10-Changed modify api implement
+ * Huawei Technologies Co., Ltd.
+ */
+package com.jaychang.sa.huawei
+
+import com.jaychang.sa.AuthCallback
+import com.jaychang.sa.AuthData
+import com.jaychang.sa.AuthDataHolder
+import com.jaychang.sa.Initializer
+
+object SimpleAuth {
+ @JvmStatic
+ fun connectHuawei(listener: AuthCallback) {
+ AuthDataHolder.getInstance().huaweiAuthData = AuthData(listOf(), listener)
+ HuaweiIdActivity.start(Initializer.context)
+ }
+
+ @JvmStatic
+ fun disconnectHuawei() {
+ AuthDataHolder.getInstance().huaweiAuthData = null
+ HuaweiIdActivity.signOut();
+ }
+
+ @JvmStatic
+ fun revokeHuawei() {
+ HuaweiIdActivity.revoke();
+ }
+}
diff --git a/library-huawei/src/main/java/com/jaychang/sa/huawei/common/ICallBack.java b/library-huawei/src/main/java/com/jaychang/sa/huawei/common/ICallBack.java
new file mode 100644
index 0000000..66afffe
--- /dev/null
+++ b/library-huawei/src/main/java/com/jaychang/sa/huawei/common/ICallBack.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jaychang.sa.huawei.common;
+
+/**
+ * Call back
+ */
+public interface ICallBack {
+ void onSuccess();
+
+ void onSuccess(String result);
+
+ void onFailed();
+}