More information like this, you can visit HUAWEI Developer Forum
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
This article is the continuity of my previous article. To get a better picture or knowledge, refer my previous article first. Below is the link:
https://forums.developer.huawei.com/forumPortal/en/topicview?tid=0202326236067510002&fid=0101187876626530001
Why you are having own server?
Using our own server to communicate with our app is generally the best option for sending push notification directly to our application. Because, our app will recognize and trust our own server which will allow us to control all transactions between our server and user devices.
Also if we need to send notification using Huawei AGC console, we need to copy and paste our users push token every time, which is a tedious job. Having our own server will allow us to store our users token and can send notification easily by fetching the entire list of token from database. That is what we are going to learn today.
Setting up the server
If you are not familiar with setting up your own local server and connecting the server with the device, refer my previous article “Build your own server from scratch to send push notification“. My previous article will help you setting up local server from scratch also, it is healthy to learn something new every day as our brain can store more information than a computer.
Prerequisite
1) We must have latest version of Node installed.
2) We must have latest version of Visual Studio Code installed.
3) We must have latest version of MongoDB database installed.
4) Laptop/desktop and Huawei mobile device must share same Wi-Fi connection.
5) We must have a working app integrate with HMS Push Kit.
We will divide this article into two parts
1) Server Side: The server side contains Node, Express, Request and JavaScript.
2) Client Side: The client side contains Android Native, Java, Retrofit and HMS Push Kit.
Demo
Send notification from server
Notification on device side
Goal
Our goal here is to subscribe, unsubscribe and send notification using topic-based message sending, also when user will tap the notification it will take user to a specific page. All this will be done using our local server side.
Server Side
Obtaining app-level access token API
The request header uses AccessToken for authentication, which is obtained using the service API provided by the open platform. The service API provides two modes to obtain AccessToken: authorization code mode and client password mode. This API uses the client password mode.
Do not apply for a new app-level access token each time before the server API is called. Frequent application requests may trigger rejection, leading to application failure within a specified period. Each access token has a validity period.
Within the validity period, it can be used repeatedly. You are advised to apply for an access token again, only when the server API is accessed and HTTP result code 401 is return
Code:
var request = require("request");
const getAppToken = (callBack) => {
var options = {
method: 'POST',
url: 'https://oauth-login.cloud.huawei.com/oauth2/v3/token',
headers:
{
'content-type': 'application/x-www-form-urlencoded',
host: 'Login.cloud.huawei.com',
post: '/oauth2/v2/token HTTP/1.1'
},
form:
{
grant_type: "client_credentials",
client_secret: 'Put Your Client Secret Here...',
client_id: 'Put Your APP ID Here ...'
}
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
var tokenValue = JSON.parse(body);
callBack(tokenValue.access_token);
});
}
exports.getAppToken = getAppToken;
Subscribe to a topic API
The API is used to subscribe to a topic for one or more users. A maximum of 1,000 tokens are allowed in each call request. Currently, this API only supports Android apps.
URL: https://push-api.cloud.huawei.com/v1/[appid]/topic:subscribe
API
subscribeToTopic.js
Code:
var request = require("request");
const subscribeToTopicNotify = (appLevelToken, tokenVal, topicSubscribeVal, callBack) => {
var options = {
method: 'POST',
url: 'https://push-api.cloud.huawei.com/v1/[appid]/topic:subscribe',
headers:
{
authorization: 'Bearer ' + appLevelToken,
host: 'oauth-login.cloud.huawei.com',
post: '/oauth2/v2/token HTTP/1.1',
'content-type': 'application/json'
},
body:
{
topic: topicSubscribeVal,
tokenArray: tokenVal
},
json: true
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
// console.log(body);
var notify = JSON.parse(JSON.stringify(body));
// console.log(">>>"+notify.msg);
callBack(notify.msg);
});
}
exports.subscribeToTopicNotify = subscribeToTopicNotify;
app.js
Code:
app.post('/subscribeToTopic', (req, res, next) => {
// topicSubscribeVal = req.body.topic;
let topic = {
topic: req.body.topic,
};
console.log(topic.topic);
// Fetching Token from mongodb ...
dbase.collection('token').find().toArray((err, results) => {
if (results) {
//clear the token list ...
arrToken = [];
// adding token in the list ...
for (var i = 0; i < results.length; i++) {
console.log("Token " + i + " " + results[i].token);
arrToken.push(results[i].token);
}
// if error code 401 returned it means access token becomes invalid
// and we need to obtain a new token. Token Validity is 60 mins...
// fetchin app level access token here ...
appToken.getAppToken((callBack) => {
console.log("TOKEN >>>" + callBack);
appLevelAccesToken = callBack;
subscribeToTopic.subscribeToTopicNotify(appLevelAccesToken, arrToken, topic.topic,callBackMessage => {
notifySucesssMessage = callBackMessage;
console.log(notifySucesssMessage);
if(callBackMessage!=null){
res.send(callBackMessage);
}
});
});
}
});
});
Unsubscribe to a topic API
The API is used to unsubscribe to a topic for one or more users. A maximum of 1,000 tokens are allowed in each call request. Currently, this API only supports Android apps.
URL: https://push-api.cloud.huawei.com/v1/[appid]/topic:unsubscribe
API
unSubscribeToTopic.js
Code:
var request = require("request");
const unSubscribeToTopicNotify = (appLevelToken, tokenVal, topicSubscribeVal, callBack) => {
var options = {
method: 'POST',
url: 'https://push-api.cloud.huawei.com/v1/[appid]/topic:unsubscribe',
headers:
{
authorization: 'Bearer ' + appLevelToken,
host: 'oauth-login.cloud.huawei.com',
post: '/oauth2/v2/token HTTP/1.1',
'content-type': 'application/json'
},
body:
{
topic: topicSubscribeVal,
tokenArray: tokenVal
},
json: true
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
// console.log(body);
var notify = JSON.parse(JSON.stringify(body));
// console.log(">>>"+notify.msg);
callBack(notify.msg);
});
}
exports.unSubscribeToTopicNotify = unSubscribeToTopicNotify;
app.js
Code:
// Push Kit unsubscribe to a topic ...
app.post('/unsubscribeToTopic', (req, res, next) => {
let topic = {
topic: req.body.topic,
};
// Fetching Token from mongodb ...
dbase.collection('token').find().toArray((err, results) => {
if (results) {
//clear the token list ...
arrToken = [];
// adding token in the list ...
for (var i = 0; i < results.length; i++) {
console.log("Token " + i + " " + results[i].token);
arrToken.push(results[i].token);
}
// if error code 401 returned it means access token becomes invalid
// and we need to obtain a new token. Token Validity is 60 mins...
// fetchin app level access token here ...
appToken.getAppToken((callBack) => {
console.log("TOKEN >>>" + callBack);
appLevelAccesToken = callBack;
unSubscribeToTopic.unSubscribeToTopicNotify(appLevelAccesToken, arrToken, topic.topic,callBackMessage => {
notifySucesssMessage = callBackMessage;
console.log(notifySucesssMessage);
if(callBackMessage!=null){
res.send(callBackMessage);
}
});
});
}
});
});
Querying the topic subscription list API
The API is used to query the topic subscription list of a token. Currently, this API only supports Android apps.
URL: https://push-api.cloud.huawei.com/v1/[appid]/topic:list
API
getTopicList.js
Code:
var request = require("request");
const getTopicList = (appLevelToken, tokenVal, callBack) => {
var options = {
method: 'POST',
url: 'https://push-api.cloud.huawei.com/v1/[appid]/topic:list',
headers:
{
authorization: 'Bearer ' + appLevelToken,
host: 'oauth-login.cloud.huawei.com',
post: '/oauth2/v2/token HTTP/1.1',
'content-type': 'application/json'
},
body:
{
token: tokenVal
},
json: true
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
// console.log(body);
var notify = JSON.parse(JSON.stringify(body));
console.log(">>>"+notify);
callBack(notify.topics);
});
}
exports.getTopicList = getTopicList;
app.js
Code:
app.post('/getTopicList', (req, res, next) => {
console.log("CALLLLLING")
// Fetching Token from mongodb ...
dbase.collection('token').find().toArray((err, results) => {
if (results) {
//clear the token list ...
arrToken = [];
// adding token in the list ...
for (var i = 0; i < results.length; i++) {
console.log("Token " + i + " " + results[i].token);
arrToken.push(results[i].token);
}
var uniqueItems = Array.from(new Set(arrToken));
// if error code 401 returned it means access token becomes invalid
// and we need to obtain a new token. Token Validity is 60 mins...
// fetchin app level access token here ...
appToken.getAppToken((callBack) => {
console.log("TOKEN VA L >>>" + uniqueItems);
appLevelAccesToken = callBack;
getTopicList.getTopicList(appLevelAccesToken, uniqueItems[0],callBackMessage => {
notifySucesssMessage = callBackMessage;
console.log("MSG >>>>>>>>>"+notifySucesssMessage);
res.send(notifySucesssMessage);
});
});
}
});
});
Sending notification using topic subscription API
The API is used to send notification using topic subscription, also an intent to send user to specific page. In this case we are sending URL as a data in an intent which will make user to view the website in android WebActivity page on taping the notification.
URL: https://push-api.cloud.huawei.com/v1/[appid]/messages:send
This is not the end. For full content, you can visit https://forums.developer.huawei.com/forumPortal/en/topicview?tid=0201327882870070045&fid=0101187876626530001
Related
More information like this, you can visit HUAWEI Developer Forum
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Co-author by Sanghati Mukherjee and Sujith.
The article is the continuity of our Huawei kit series. The series contain four parts. Below are the links:
1) It’s Show Time using Huawei Auth Service and Account kit.
2) It’s Show Time using Huawei Location and Site kit.
3) It’s Show Time using Huawei Wallet kit.
The fourth part is this article where we would learn how to implement the server side of Huawei wallet kit using local Node js server and how to call them in client side in order to create ticket and save the ticket on user wallet app for future use.
Why having your own server?
Using our own server to communicate with app is generally the best option for creating cards or tickets in our application. Because, our app will recognize and trust our own server, allowing us to control all transactions between our server and user devices. That is what we are going to learn today.
Setting up the server
If you are not familiar with setting up your own local server and connecting the server with the device, I would strongly recommend you to go through my previous article i.e. “Build your own server from scratch to send push notification“. My previous article will help you setting up local server from scratch also it is healthy to learn something new every day as our brain can store more information than a computer.
Prerequisite
1) We must have latest version of Node installed.
2) We must have latest version of Visual Studio Code installed.
3) We must have latest version of MongoDB database installed.
4) Laptop/desktop and Huawei mobile device must share same Wi-Fi connection.
5) We must have a working app integrate with HMS Wallet Kit.
We will divide this article into two parts
1) Server Side: The server side contains Node, Express, Request and JavaScript.
2) Client Side: The client side contains Android Native, Java, Retrofit and HMS Wallet Kit.
Demo
Server Side
Obtaining app-level access token API
The request header uses AccessToken for authentication, which is obtained using the service API provided by the open platform. The service API provides two modes to obtain AccessToken: authorization code mode and client password mode. This API uses the client password mode.
Do not apply for a new app-level access token each time before the server API is called. Frequent application requests may trigger rejection, leading to application failure within a specified period. Each access token has a validity period.
Within the validity period, it can be used repeatedly. You are advised to apply for an access token again only when the server API is accessed and HTTP result code 401 is returned.
Code:
var request = require("request");
const getAppToken = (callBack) => {
var options = {
method: 'POST',
url: 'https://oauth-login.cloud.huawei.com/oauth2/v3/token',
headers:
{
'content-type': 'application/x-www-form-urlencoded',
host: 'Login.cloud.huawei.com',
post: '/oauth2/v2/token HTTP/1.1'
},
form:
{
grant_type: "client_credentials",
client_secret: 'Put Your Client Secret Here...',
client_id: 'Put Your APP ID Here ...'
}
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
var tokenValue = JSON.parse(body);
callBack(tokenValue.access_token);
});
}
exports.getAppToken = getAppToken;
Wallet server address
Set the {url} variable based on the region where the server is located. For details, please refer to Wallet Server Address as shown below.
Creating a HwWalletObject
We need to set HwWalletObject as body parameter in order to call wallet event ticket APIs. To know more about HwWalletObject follow the link below:
https://developer.huawei.com/consumer/en/doc/HMSCore-References-V5/def-0000001050160319-V5
Creating an event ticket model API
We call this method to add an event ticket model to the Huawei server.
URL: https://passentrust-dra.wallet.hicloud.com/hmspass/v1/eventticket/model
API
Code:
var request = require("request");
const getEventTIcketModelObject = (authorizationVal, ticketBookingDate,callBack) => {
var options = {
method: 'POST',
url: 'https://passentrust-dra.wallet.hicloud.com/hmspass/v1/eventticket/model',
headers:
{
'cache-control': 'no-cache',
accept: 'application/json',
authorization: 'Bearer '+authorizationVal,
'content-type': 'application/json'
},
body:
{ passVersion: '1.0',
passTypeIdentifier: 'YOUR_SERVICE_ID',
passStyleIdentifier: 'YOUR_MODEL_ID',
organizationName: 'Huawei',
fields:
{ countryCode: 'zh',
locationList: [ { longitude: '114.0679603815', latitude: '22.6592051284' } ],
commonFields:
[
{ key: 'logo', value: 'https://contentcenter-drcn.dbankcdn.com/cch5/Wallet-WalletKit/picres/cloudRes/coupon_logo.png' },
{ key: 'name', value: 'Is Show Time Movie Ticket' },
{ key: 'merchantName',
value: 'Huawei',
localizedValue: 'merchantNameI18N' },
{ key: 'address', value: 'INOX Cinema' },
{ key: 'ticketType', value: 'Movie ticket' } ],
appendFields: [ { key: 'backgroundColor', value: '#3e454f' } ],
timeList:
[ { key: 'startTime', value: ticketBookingDate },
{ key: 'endTime', value: '2020-08-20T00:00:00.111Z' } ],
localized:
[ { key: 'merchantNameI18N', language: 'zh-cn', value: '华为' },
{ key: 'merchantNameI18N', language: 'en', value: 'Huawei' }
]
}
},
json: true };
request(options, function (error, response, body) {
if (error) {
callBack(error,error);
}
else{
callBack(body);
}
});
}
exports.getEventTIcketModelObject = getEventTIcketModelObject;
The value of passTypeIdentifier parameter is the Service ID, which can be obtained when we apply for HUAWEI Wallet Kit service in AGC and the value of passStyleIdentifier is the Model ID, which can also be obtained from wallet kit service in AGC. Both are mandatory.
Adding an event ticket instance API
We call this method to add the event ticket instance of a user to the Huawei server. After the instance is added using this API, a thin JWE should be used as well to link the instance to the user's HUAWEI ID. Alternatively, a JWE can be used, rather this API, to directly add the instance to the Huawei server and link it to the user's HUAWEI ID.
URL: https://passentrust-dra.wallet.hicloud.com/hmspass/v1/eventticket/instance
API
Code:
var request = require("request");
const getEventTIcketInstanceObject = (authorizationVal, serialNumber, ticketBookingDate,seatNumber,userName, movieName,callback) => {
var options = {
method: 'POST',
url: 'https://passentrust-dra.wallet.hicloud.com/hmspass/v1/eventticket/instance',
headers:
{
'cache-control': 'no-cache',
accept: 'application/json',
authorization: 'Bearer '+authorizationVal,
'content-type': 'application/json' },
body:
{
organizationPassId: 'YOUR_APP_ID',
passTypeIdentifier: 'YOUR_SERVICE_ID',
passStyleIdentifier: 'YOUR_MODEL_ID',
serialNumber: serialNumber,
fields:
{ status:
{ state: 'active',
effectTime: ticketBookingDate,
expireTime: '2020-08-20T00:00:00.111Z' },
barCode:
{ text: '562348969211212',
type: 'codabar',
value: '562348969211212',
encoding: 'UTF-8' },
commonFields:
[ { key: 'ticketNumber', value: serialNumber },
{ key: 'title', value: 'Its Show Time' },
{ key: 'name', value: movieName },
{key:'programImage', value:'https://contentcenter-drcn.dbankcdn.com/cch5/Wallet-WalletKit/picres/cloudRes/coupon_logo.png'}
],
appendFields:
[ { key: 'gate', value: 'Gate 2', label: 'Gate' },
{ key: 'seat', value: '24', label: 'Seat' },
{ key: 'userName', value: userName }
]
}
},
json: true };
request(options, function (error, response, body) {
if (error) throw new Error(error);
//console.log(body);
var data = JSON.parse(JSON.stringify(body));
// console.log(data)
callback(body);
});
}
exports.getEventTIcketInstanceObject = getEventTIcketInstanceObject;
The value of organizationPassId parameter is the APP ID, which can be obtained from our AGC.
Create ticket instance API for client
We need an API for client to call the above two APIs on server in order to fetch wallet instance and use it to create a movie ticket.
Code:
app.post('/createTicket', (req, res, next) => {
appTokenWallet.getAppToken((callBack) => {
let authorization = callBack
console.log(authorization);
let serialNumber = Math.floor(Math.random() * 50000) + 100000;
console.log(serialNumber);
let ticketBookingDate = convertDateToUTC();
createTicketModel.getEventTIcketModelObject(authorization,ticketBookingDate, callBackMessage => {
console.log(callBackMessage);
});
createTicketInstance.getEventTIcketInstanceObject(authorization,serialNumber,ticketBookingDate,req.body.seat,req.body.username,"Dil Bechara",callBackMsg => {
if(callBackMsg!=null){
res.send(""+serialNumber);
}
});
});
});
Client Side (Android Native)
We need Retrofit in order to call our restful Apis. Include the following dependencies in app build.gradle file.
Code:
implementation 'com.squareup.retrofit2:retrofit:2.8.1'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
Create retrofit interface
Create a new Interface class and name it GetDataService. Open the class, copy and paste below code:
Code:
public interface GetDataService {
@POST("/createTicket")
Call<Object> createMovieTicket(@Body HashMap<String, String> map);
}
Create retrofit instance
Create a new class and name it RetrofitClientInstance. Open the class, copy and paste below code:
Code:
public class RetrofitClientInstance {
private static Retrofit retrofit;
public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(Constant.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
Note: The BASE_URL is important here. We will put our IPv4 Address of our server machine instead localhost. To find our machine IPv4 Address, we will go to command prompt and type ipconfig. Also make sure that the device is connected to the same Wi-Fi the server machine is connected too.
Use retrofit instance in the activity
Create an object of retrofit instance in onCreate() method of the activity as show below:
Code:
service = RetrofitClientInstance.getRetrofitInstance().create(GetDataService.class);
After that use this retrofit instance object to call the create ticket instance API of server in order to fetch instance Id and use it to create thin JWE which will be used to create a ticket.
Code:
public void proceedToPay(View view) {
progressBar.setVisibility(View.VISIBLE);
HashMap<String, String> ticket = new HashMap<>();
ticket.put("seat", seats);
ticket.put("username","Sanghati Mukherjee");
Call<Object> call = service.createMovieTicket(ticket);
call.enqueue(new Callback<Object>() {
@Override
public void onResponse(Call<Object> call, Response<Object> response) {
passObject = "{\"instanceIds\": [\""+response.body().toString().replace(".0","").trim()+"\"]}";
progressBar.setVisibility(View.GONE);
generateJWEStr();
}
@Override
public void onFailure(Call<Object> call, Throwable t) {
Toast.makeText(PaymentActivity.this, t.getMessage(), Toast.LENGTH_LONG).show();
System.out.println("RESPONSE >>> " + t.getMessage());
progressBar.setVisibility(View.GONE);
}
});
}
When JWE is created the Huawei server will call browser to create ticket as shown below:
Now we need to add the ticket in our wallet app for future use which in this case is a movie ticket for INOX cinemas.
GitHub Links
Client Side: https://github.com/DTSE-India-Community/ItsShowTime
Server Side: https://github.com/DTSE-India-Community/Huawei-In-App-Purchase-Push-Kit-Server_Side-And-Wallet-Kit-Server-Side-Implementation
For More Information
https://developer.huawei.com/consumer/en/doc/development/HMSCore-References-V5/create-model-0000001050158460-V5
https://developer.huawei.com/consumer/en/doc/development/HMSCore-References-V5/add-instance-0000001050158466-V5
More information like this, you can visit HUAWEI Developer Forum
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
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
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.
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.
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.
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.
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.
4. Open the build.gradle file in the root directory of your Android Studio project.
5. Configure the following information in the build.gradle file:
Code:
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'
}
}
6. Open the build.gradle file in the app directory.
7. Configure the HUAWEI Account Kit service address, Auth Service SDK address, and HUAWEI Game Service address.
Code:
// 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'
}
8. Click Sync Now to synchronize the configuration.
9. Add into your Manifest.
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="Your package name"/>
</intent-filter>
</activity>
10. Add your app client Id from Google Developer Console for Google Sign In.
Code:
<?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
Code:
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([email protected], Observer {
if (it != null) {
if (it.error.toString().contentEquals("invalid_token")) {
Toast.makeText([email protected], "${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([email protected], "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(
[email protected],
"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
Reference URL:
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
Hi, how do I verify an anonymous account?
On an Enterprise environment maybe you want to perform some console operations from a custom plaform, for example, to manage your app information, to automatize the download of the app finance report, or to automatically release an app allocated in your own server. If this is your case, yo may be interested on App Gallery Connect API.
Previous requirements
A developer account
At least one project on AGC
What will we do in this article?
We will generate our client credentials to obtain an app level access token which will give us access to the App Gallery Connect API. After that we wiil use the Connect API to perform the next operations.
Obtaining the App Id related to an app package name
Obtaining the basic app information
Obtaining the upload URL to upload a new version of our app
Obtaining the URL to download your App Report
All the API requests on this article will be performed by using Kotlin, so you can use it to develop your own management platform for mobile, desktop or web.
Generating the client credentials
Sign into AppGallery Connect and then go to Users and permissionfrr4qwq
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
From the left side panel, select Connect API and click on the Create button to generate your client credentials.
In the pop up dialog, choose a name, project and applicable roles for your API client. In this example, I will use Administrator.
Note: Some APIs require project-level authorization. Please specify the projects you want to access through these APIs. Select N/A if the APIs only require team-level authorization.
Once you have confirmed your settings, your client ID and secret key will appear on the Connect API screen.
Copy your Client ID and Key. Make sure to keep them safe.
Request Helper
I wrote a helper class to perform the REST calls to the Connect API
Code:
data class ResponseObject(val code:Int,val message: String,var responseBody:JSONObject?)
class RequestHelper {
companion object{
fun sendRequest(host:String, headers: HashMap<String,String>?, body:JSONObject?, requestType:String="GET"):ResponseObject{
try {
val conn = URL(host)
.openConnection() as HttpURLConnection
conn.apply {
requestMethod = requestType
headers?.apply {
for(key in keys)
setRequestProperty(key, get(key))
}
doOutput = requestType == "POST"
doInput = true
}
if(requestType!="GET"){
conn.outputStream.let{
body?.apply { it.write(toString().toByteArray()) }
}
}
val result = when (conn.responseCode) {
in 0..300 -> convertStreamToString(conn.inputStream)
else -> convertStreamToString(conn.errorStream)
}
//Returns the access token, or an empty String if something fails
return ResponseObject(conn.responseCode,conn.responseMessage, JSONObject(result)).also { conn.disconnect() }
} catch (e: Exception) {
return ResponseObject(400,e.toString(),null)
}
}
private fun convertStreamToString(input: InputStream): String {
return BufferedReader(InputStreamReader(input)).use {
val response = StringBuffer()
var inputLine = it.readLine()
while (inputLine != null) {
response.append(inputLine)
inputLine = it.readLine()
}
it.close()
response.toString()
}
}
}
}
Obtaining the App Level Access Token
This is a mandatory step, the access token contains information about the scope level provided to your client credentials.
Perform the following POST request:
Hostname: connect-api.cloud.huawei.com
Path: /api/oauth2/v1/token
Headers:
Content-Type: application/json
Body:
Code:
{
"grant_type":"client_credentials",
"client_id":"YOUR_CLIENT ID",
"client_secret":"YOUR_CLIENT_KEY"
}
If everything goes fine, the request will return an access token and the validity period of the token. You can use the same access token for any of the following operations until the expiration time. If the token expires, you must apply for a new one.
Example:
Code:
data class AGCredential(val clientId: String, val key: String)
class AGConnectAPI(credential: AGCredential) {
companion object {
@JvmField
val HOST = "https://connect-api.cloud.huawei.com/api"
private val MISSING_CREDENTIALS = "must setup the client credentials first"
val MISSING_CREDENTIALS_RESPONSE=ResponseObject(403, MISSING_CREDENTIALS, null)
}
var token: String? = null
var credential: AGCredential= credential
set(value) {
field = value
getToken()
}
init {
getToken()
}
private fun getToken(): Int {
val host = "$HOST/oauth2/v1/token"
val headers = HashMap<String, String>().apply {
put("Content-Type", "application/json")
}
val body = JSONObject().apply {
put("client_id", credential.clientId)
put("client_secret", credential.key)
put("grant_type", "client_credentials")
}
val response = RequestHelper.sendRequest(host, headers, body, "POST")
val token = response.responseBody?.let {
if (it.has("access_token")) {
it.getString("access_token")
} else null
}
return if (token != null) {
this.token = token
200
} else response.code
}
}
Obtaining the App Id for a given package name
The App Id is required as a unique identifier for app management operations.
Perform the following GET request:
Hostname: connect-api.cloud.huawei.com
Path: /api/publish/v2/appid-list?packageName=$packageName
Headers:
Authorization: Bearer $token
client_id: clientId
Example
Code:
fun queryAppId(packageName: String): ResponseObject {
return if (!token.isNullOrEmpty()) {
val url = "$HOST/publish/v2/appid-list?packageName=$packageName"
RequestHelper.sendRequest(url, getClientHeaders(), null)
} else MISSING_CREDENTIALS_RESPONSE
}
private fun getClientHeaders(): HashMap<String, String> {
return HashMap<String, String>().apply {
put("Authorization", "Bearer $token")
put("client_id", credential.clientId)
}
}
Obtaining the upload URL to upload a new version of our app
The Connect API provides URLs to upload different files of your app configuration in AppGallery. For this example we will get the URL to upload the APK. Currently, files with the following extensions can be uploaded:
apk/rpk/pdf/jpg/jpeg/png/bmp/mp4/mov/aab.
Perform the following GET request:
Hostname: connect-api.cloud.huawei.com
Path: /api//publish/v2/upload-url?appId=$appId&suffix=$(apk/rpk/pdf/jpg/jpeg/png/bmp/mp4/mov/aab)
Headers:
Content-Type: application/json
Authorization: Bearer $token
client_id: clientId
Example:
Code:
fun getUploadURL(appId: String,suffix: String):ResponseObject{
return if (!token.isNullOrEmpty()){
val url="$HOST/publish/v2/upload-url?appId=$appId&suffix=$suffix"
RequestHelper.sendRequest(url,getClientHeaders(),null)
} else MISSING_CREDENTIALS_RESPONSE
}
Obtaining the URL to download your App Report
You can download detailed reports about your app downloads and installations, purchases made in your app and if your app is a paid app, you can get the Paid Download report.
For the Paid Download report the hostname is different for every region
Hostname: https://{domain}
Domain name for China: connect-api.cloud.huawei.com
Domain name for Europe: connect-api-dre.cloud.huawei.com
Domain name for Asia Pacific: connect-api-dra.cloud.huawei.com
Domain name for Russia: connect-api-drru.cloud.huawei.com
Path: /api/report/distribution-operation-quality/v1/orderDetailExport/$appId
Headers:
Authorization: Bearer $token
client_id: clientId
This API require some querys to generate the report, to se the query details, refer to the official documentation
Example:
Code:
fun getReportUrl(
appId: String,
lang: String,
startTime: String,
endTime: String,
filterConditions:HashMap<String,String>
):ResponseObject {
return if (!token.isNullOrEmpty()){
val fc = StringBuilder().apply {
for (key in filterConditions.keys) {
append("&filterCondition=")
append(key)
append("&filterConditionValue=")
append(filterConditions[key])
}
}
val url =
"$HOST/report/distribution-operation-quality/v1/orderDetailExport/$appId?language=$lang&startTime=$startTime&endTime=$endTime${fc}"
RequestHelper.sendRequest(url,getClientHeaders(),null)
} else MISSING_CREDENTIALS_RESPONSE
}
Conclusion
Now you know what is the Connect API and how you can use it to automatize some management operations or to develop yur own custom console. There are also a lot of things what you can do with the Connect API. For more information you can refer to the official documentation.
Check this and other demos: https://github.com/danms07/dummyapp
Using this API can we customize Roles and permissions user basis?
Recently, it is found that some services of AppGallery Connect start to support Flutter. Let’s have a try on Auth Service with an email address.If you want to quickly experience this service, see the demo on GitHub.
Integration Procedure
1. Install the Flutter environment.
a) Download the Flutter SDK package from https://flutter.dev/docs/get-started/install/windows.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Decompress the package to any directory, for example, D:\Flutter.
b) Add the Flutter command file as an environment variable. Here, the path is D:\Flutter\flutter_windows_1.22.2-stable\flutter\bin.
c) In Android Studio, go to File > Settings > Plugins, download the Flutter and Dart plug-ins, and restart Android Studio for the plug-ins to take effect.
2. Create a project and enable Auth Service.
a) Create an Android app and enable Auth Service for it in AppGallery Connect.
b) Enable the mobile number, email address, and anonymous account as the authentication modes.
c) In Android Studio, create a Flutter project.
d) Add the agconnect-services.json file to the android/app directory.
e) Configure the Maven repository address and AppGallery Connect plug-in address.
a. Open the build.gradle file in the android directory of the Flutter project.
b. Go to allprojects > repositories and configure the Maven repository address.
c. Go to buildscript > repositories and configure the Maven repository address.
d. Go to buildscript > dependencies and configure the AppGallery Connect plug-in address.
1. Add build dependencies and the AppGallery Connect plug-in address.
a. Open the build.gradle file in the android/app directory of the Flutter project.
b. Add the following content to the file.
3. Integrate the AppGallery Connect SDK.
Add the dependency to the pubspec.yaml file of the Flutter project.
XML:
dependencies:
flutter:
sdk: flutter
# Add the following line:
agconnect_auth: ^1.1.0
Click Pub get to synchronize the data.
4. Integrate functions.
1. Anonymous account sign-in
Call the signInAnonymously API.
JavaScript:
_signIn() async {
AGCAuth.instance.signInAnonymously().then((value) {
setState(() {
_log =
‘signInAnonymously = ${value.user.uid} , ${value.user.providerId}’;
});
});
}
You can obtain user information from the returned value. In this example, the UID is returned.
2. Sign-in with a mobile number or an email address
A verification code needs to be sent.
For a mobile number, call the requestVerifyCode method, and pass the mobile number, country/region code, and settings.
JavaScript:
_requestPhoneVerifyCode(VerifyCodeAction action) {
String countryCode = _countryCodeController.text;
String phoneNumber = _phoneNumberController.text;
VerifyCodeSettings settings = VerifyCodeSettings(action, sendInterval: 30);
PhoneAuthProvider.requestVerifyCode(countryCode, phoneNumber, settings).then((value) => print(value.validityPeriod));
}
For an email address, call the requestVerifyCode method, and pass the email address and settings.
JavaScript:
_requestEmailVerifyCode(VerifyCodeAction action) {
String email = _emailController.text;
VerifyCodeSettings settings = VerifyCodeSettings(action, sendInterval: 30);
EmailAuthProvider.requestVerifyCode(email, settings)
.then((value) => print(value.validityPeriod));
}
Then, create a user.
To create a mobile phone user, call the createPhoneUser method, and pass the PhoneUser object.
JavaScript:
_createPhoneUser() async {
bool result = await _showPhoneDialog(VerifyCodeAction.registerLogin);
if (result == null) {
print("cancel");
return;
}
String countryCode = _countryCodeController.text;
String phoneNumber = _phoneNumberController.text;
String verifyCode = _verifyCodeController.text;
String password = _passwordController.text;
AGCAuth.instance.createPhoneUser(PhoneUser(countryCode, phoneNumber, verifyCode, password: password)) .then((value) {
setState(() {
_log = 'createPhoneUser = ${value.user.uid} , ${value.user.providerId}';
});
}).catchError((error)=>print(error));
}
To create an email address user, call the createEmailUse method, and pass the EmailUser object.
JavaScript:
_createEmailUser() async {
bool result = await _showEmailDialog(VerifyCodeAction.registerLogin);
if (result == null) {
print("cancel");
return;
}
String email = _emailController.text;
String verifyCode = _verifyCodeController.text;
String password = _passwordController.text;
AGCAuth.instance
.createEmailUser(EmailUser(email, verifyCode, password: password))
.then((value) {
setState(() {
_log = 'createEmailUser = ${value.user.uid} , ${value.user.providerId}';
});
}).catchError((error) => print(error));
}
Then, configure the sign-in mode. If a user signs in with a password:
· For a mobile number, call the signIn method, and pass the user token generated using the mobile number.
JavaScript:
_signInWithPassword() async {
bool result = await _showPhoneDialog(VerifyCodeAction.registerLogin);
if (result == null) {
print("cancel");
return;
}
String countryCode = _countryCodeController.text;
String phoneNumber = _phoneNumberController.text;
String password = _passwordController.text;
AGCAuthCredential credential = PhoneAuthProvider.credentialWithPassword(countryCode, phoneNumber, password);
AGCAuth.instance.signIn(credential).then((value) {
setState(() {
_log = 'signInWithPassword = ${value.user.uid} , ${value.user.providerId}';
});
});
}
· For an email address, call the signIn method, and pass the user token generated using the email address and its password.
JavaScript:
_signInWithPassword() async {
bool result = await _showEmailDialog(VerifyCodeAction.registerLogin);
if (result == null) {
print("cancel");
return;
}
String email = _emailController.text;
String password = _passwordController.text;
AGCAuthCredential credential =
EmailAuthProvider.credentialWithPassword(email, password);
AGCAuth.instance.signIn(credential).then((value) {
setState(() {
_log =
'signInWithPassword = ${value.user.uid} , ${value.user.providerId}';
});
});
}
If a user signs in with a verification code:
· For a mobile number, call the signIn method, and pass the user token generated using the mobile number, password, and verification code.
JavaScript:
_signInWithVerifyCode() async {
bool result = await _showPhoneDialog(VerifyCodeAction.registerLogin);
if (result == null) {
print("cancel");
return;
}
String countryCode = _countryCodeController.text;
String phoneNumber = _phoneNumberController.text;
String verifyCode = _verifyCodeController.text;
String password = _passwordController.text;
AGCAuthCredential credential = PhoneAuthProvider.credentialWithVerifyCode(countryCode, phoneNumber, verifyCode, password: password);
AGCAuth.instance.signIn(credential).then((value) {
setState(() {
_log = 'signInWithVerifyCode = ${value.user.uid} , ${value.user.providerId}';
});
});
}
· For an email address, call the signIn method, and pass the user token generated using the email address, password, and verification code.
JavaScript:
_signInWithVerifyCode() async {
bool result = await _showEmailDialog(VerifyCodeAction.registerLogin);
if (result == null) {
print("cancel");
return;
}
String email = _emailController.text;
String verifyCode = _verifyCodeController.text;
String password = _passwordController.text;
AGCAuthCredential credential = EmailAuthProvider.credentialWithVerifyCode(
email, verifyCode,
password: password);
AGCAuth.instance.signIn(credential).then((value) {
setState(() {
_log =
'signInWithVerifyCode = ${value.user.uid} , ${value.user.providerId}';
});
});
}
3. Self-owned account
You need to create a JWT and obtain the user token for a self-owned account on your server. In your app, you need to use the token for sign-in only.
JavaScript:
_signIn() async {
bool result = await _showSelfBuildDialog(VerifyCodeAction.registerLogin);
if (result == null) {
print("cancel");
return;
}
String token = _selfBuildController.text;
AGCAuthCredential credential = SelfBuildAuthProvider.credentialWithToken(token);
AGCAuth.instance.signIn(credential).then((value) {
setState(() {
_log = 'signIn = ${value.user.uid} , ${value.user.providerId}';
});
});
}
5. Package the APK file.
Similar to Android, you only need to run your project in Android Studio.
For more details, please check:
Auth Service development guide:
https://developer.huawei.com/consum...Guides/agc-auth-introduction-0000001053732605
Auth Service codelab for Android:
https://developer.huawei.com/consumer/en/codelab/AuthenticationService/index.html#0
Auth Demo:https://github.com/AppGalleryConnect/agc-flutter-plugin/tree/master/agc-authservice-flutter
Introduction
This article. we will learn how to implement the Card Ability — Huawei Account binding — solution 1 step by step.
Account Binding
Account binding refers to the process of binding a user’s HUAWEI ID and a developer’s account when developing card ability. Huawei provides three solutions to developers for account binding. We will work on solution-1 in this article.
if everybody is ready, let’s get started
Implementation
The account binding process consists of six phases in total. The picture below will help you better understand the process of account binding.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
1.Enables Custom Sign Page — Account Binding Solution-1
Firstly, the developer server is mandatory for using solution-1. Huawei Ability Gallery sends the bind request to the developer’s server when custom sign mode is enabled from the Huawei phone.
2. Sends the Request to Obtain a Deep Link URL
Code:
@PostMapping("/api/v1/users/bind")
public AccountBindingResponseModel requestBindPage(@RequestBody AccountBindingRequestModel bindingRequest) {
AccountBindingResponseModel bindingResponse = new AccountBindingResponseModel();
try {
if (bindingRequest.getHeader().getNamespace().equals(BindingMode.AUTHORIZATION.getMode())) {
return mAccountBindingClient.doAccountBinding(bindingRequest);
} else {
return mAccountBindingClient.doAccountUnBinding(bindingRequest);
}
} catch (Exception ex) {
bindingResponse.setErrorMessage(ex.toString());
return bindingResponse;
}
}
/api/v1/users/bind is the address to which the binding or unbinding request from a Huawei ability gallery is sent. This address needs to be configured on the Fulfillment page in HUAWEI Ability Gallery. The request body of this service is in the following format.
Code:
{
"version":"1.0",
"header":{
"type":"Directive",
"timestamp":"15869741258",
"name":"AcceptGrant",
"namespace":"Authorization"
},
"inquire":{
"inquireId":"efd1f6ca-8fdf-11e8-9eb6-529269fb1459",
"payload":{
"grant":{
"type":"OAuth2.Authorization",
"openId":"ZU5uc3v045B6gkNSIXmf0414BQJQ5O37",
"sign":"TZ5uc3v0BQJQ5SIXmf0414O3745B6gkNXYZQPR",
"abilityId":"77fd5f3fc18243b58f2fe3e843991100"
}
}
}
}
OpenId: It is generated by Huawei Ability Gallery, using an app ID and a Huawei user ID.
Note: OpenId is unique for the same app and user.
3. Returns the Deep Link Of The Login Page
Deeplink needs to be created client-side has a specific syntax. This syntax should end with OpenID.
native app deep link example : app://com.huawei.hag/openId=xxxx
Click to expand...
Click to collapse
Code:
@Override
public AccountBindingResponseModel doAccountBinding(AccountBindingRequestModel bindingRequest) {
logger.debug("starts doAccountBinding");
AccountBindingResponseModel bindingResponse = new AccountBindingResponseModel();
bindingResponse.setErrorCode(SUCCESS);
bindingResponse.setErrorMessage("OK");
String openId = bindingRequest.getInquire().getPayload().getGrant().getOpenId();
try {
ValidatorUtils.checkArgument(!StringUtils.isEmpty(openId));
} catch (IllegalArgumentException e) {
bindingResponse.setErrorCode(INVALID_PARAMETER);
bindingResponse.setErrorMessage("param openid is invalid");
}
AccountDirectiveInteraction interaction = new AccountDirectiveInteraction();
NativeAppLinkInteraction deepLink = new NativeAppLinkInteraction();
String appName = ResourceBundle.getBundle("url").getString("appname");
String packagename = ResourceBundle.getBundle("url").getString("packagename");
deepLink.setAppName(appName);
deepLink.setAppPackage(packagename);
deepLink.setMinVersion(1L);
deepLink.setUrl("app://open.sigIn.page/openId=" + openId);
interaction.setDeepLink(deepLink);
Reply reply = new Reply();
reply.setAccountLoginAddr(interaction);
bindingResponse.setReply(reply);
bindingResponse.setVersion("1.0");
logger.debug("doAccountBinding ended successfully");
return bindingResponse;
}
5. Authentication / Login with developer’s server
A deep link is created in the desired format. Afterward, the user redirects to the app sign-in page via the mentioned link.
The user enters the authorization credentials on the sign-in page. The sign-in page sends the credentials and OpenID to the developer’s server. OpenId is obtained from account binding response.
Once the developer’s server has verified the credentials, it will bind the developer’s account to the user’s OpenID.
Code:
if(loginRequestModel.getOpenId() != null) {
mAccountBindingClient.sendBindRequest(loginRequestModel.getOpenId(), false);
foundUser.setOpenId(loginRequestModel.getOpenId());
userRepository.saveAndFlush(foundUser);
}
6. Binding Operation
We need to “accessToken” for binding operation. We can obtain “accessToken” while creating an HTTP post request
huawei.hag.bindUrl=
https://hag-eu.cloud.huawei.com/open-ability/v1/open-account-events/bind
Click to expand...
Click to collapse
Code:
@Override
public BindResponse sendBindRequest(String openId, Boolean unBind) throws Exception {
CloseableHttpClient httpClient = HttpClients.createDefault();
logger.debug("starts sendBindRequest with openId is {}", openId);
String serverUrl = "";
try {
OpenAccountBindReq bindReq = new OpenAccountBindReq();
bindReq.setOpenId(openId);
bindReq.setRequestTime(TimeUtil.toTimeString(new Date()));
StringEntity entity = new StringEntity(JSON.toJSONString(bindReq), "UTF-8");
if(unBind) {
serverUrl = ResourceBundle.getBundle("url").getString("huawei.hag.unBindUrl");
} else {
serverUrl = ResourceBundle.getBundle("url").getString("huawei.hag.bindUrl");
}
HttpResponse response = httpClient.execute(getHttpPost(serverUrl, entity));
String reponseContent = EntityUtils.toString(response.getEntity());
Integer status = response.getStatusLine().getStatusCode();
BindResponse bindResponse = new BindResponse();
JSONObject jsonObject = JSONObject.parseObject(reponseContent);
String code = jsonObject.getString("code");
String desc = jsonObject.getString("desc");
if (status == 200) {
bindResponse.setCode(code);
bindResponse.setDesc(desc);
logger.debug("sendBindRequest process ended successfully");
return bindResponse;
}
logger.debug("sendBindRequest process error, error code :{}, error desc : {}", code, desc);
throw new Exception("Send Request to " + serverUrl + " status is " + status);
} catch (Exception e) {
throw new Exception(e.toString());
} finally {
httpClient.close();
}
}
Code:
private HttpPost getHttpPost(String postUrl, StringEntity entity) throws Exception {
HttpPost httpPost = new HttpPost(postUrl);
try {
executeAccessToken();
httpPost.setHeader("Authorization", "Bearer " + accessToken);
httpPost.setHeader("Content-Type", "application/json");
httpPost.setHeader("Accept", "application/json");
httpPost.setHeader("x-appid", appId);
httpPost.setEntity(entity);
return httpPost;
} catch (IOException ex) {
throw new IOException(ex.toString());
}
}
Code:
private void executeAccessToken() throws Exception {
String requestBody = createRequestBody(appId, appSecret);
HttpPost httpPost = new HttpPost(TOKEN_AT_URL);
CloseableHttpClient httpClient = HttpClients.createDefault();
try {
StringEntity entity = new StringEntity(requestBody);
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");
httpPost.setEntity(entity);
HttpResponse response = httpClient.execute(httpPost);
String jsonStr = EntityUtils.toString(response.getEntity());
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != 200) {
throw new Exception("Send Request to " + TOKEN_AT_URL + " failed with httpcode is " + statusCode);
}
JSONObject jsonObject = JSONObject.parseObject(jsonStr);
accessToken = jsonObject.getString("access_token");
} catch (IOException ex) {
throw new IOException(ex.toString());
} finally {
httpClient.close();
}
}
Account Unbinding
If the user taps the account unbinding button, Huawei Ability Gallery sends the request to the developer server (/api/v1/users/bind).
The request body of unbinding operation is the same as the binding operation’s body. There is only one difference and that is the namespace.
The namespace must be “Deauthorization” for unbinding operations.
Code:
{
"version":"1.0",
"header":{
"type":"Directive",
"timestamp":"15869741258",
"name":"AcceptGrant",
"namespace":"Deauthorization"
},
"inquire":{
"inquireId":"efd1f6ca-8fdf-11e8-9eb6-529269fb1459",
"payload":{
"grant":{
"type":"OAuth2.Authorization",
"openId":"ZU5uc3v045B6gkNSIXmf0414BQJQ5O37",
"sign":"TZ5uc3v0BQJQ5SIXmf0414O3745B6gkNXYZQPR",
"abilityId":"7a0af511a91f4591b4efbbaacd8bee60"
}
}
}
}
huawei.hag.unBindUrl=
https://hag-eu.cloud.huawei.com/open-ability/v1/open-account-events/unbind
Click to expand...
Click to collapse
Code:
@Override
public AccountBindingResponseModel doAccountUnBinding(AccountBindingRequestModel bindingRequest) throws Exception {
logger.debug("starts doAccountUnBinding");
try {
String openId = bindingRequest.getInquire().getPayload().getGrant().getOpenId();
BindResponse response = sendBindRequest(openId, true);
AccountBindingResponseModel bindingResponse = new AccountBindingResponseModel();
bindingResponse.setErrorCode(response.getCode());
bindingResponse.setErrorMessage(response.getDesc());
User foundUser = userRepository.findByOpenId(openId).orElse(null);
if (foundUser == null) {
logger.debug("user not found by open id, openId is {}", openId);
} else {
foundUser.setOpenId(null);
userRepository.saveAndFlush(foundUser);
}
logger.debug("doAccountUnBinding process ended successfully");
return bindingResponse;
} catch (Exception e) {
throw new Exception(e.toString());
}
}
Conclusion
I hope this article helps you better understand Huawei Ability and account binding/unbinding operations.
Thanks for reading. If you have a question, don’t hesitate to contact me.
References:
Document
developer.huawei.com
Document
developer.huawei.com
Document
developer.huawei.com
Provided clear info. thank you
yagmur.kilic said:
Introduction
This article. we will learn how to implement the Card Ability — Huawei Account binding — solution 1 step by step.
Account Binding
Account binding refers to the process of binding a user’s HUAWEI ID and a developer’s account when developing card ability. Huawei provides three solutions to developers for account binding. We will work on solution-1 in this article.
if everybody is ready, let’s get started
Implementation
The account binding process consists of six phases in total. The picture below will help you better understand the process of account binding.
View attachment 5340343
1.Enables Custom Sign Page — Account Binding Solution-1
Firstly, the developer server is mandatory for using solution-1. Huawei Ability Gallery sends the bind request to the developer’s server when custom sign mode is enabled from the Huawei phone.
2. Sends the Request to Obtain a Deep Link URL
Code:
@PostMapping("/api/v1/users/bind")
public AccountBindingResponseModel requestBindPage(@RequestBody AccountBindingRequestModel bindingRequest) {
AccountBindingResponseModel bindingResponse = new AccountBindingResponseModel();
try {
if (bindingRequest.getHeader().getNamespace().equals(BindingMode.AUTHORIZATION.getMode())) {
return mAccountBindingClient.doAccountBinding(bindingRequest);
} else {
return mAccountBindingClient.doAccountUnBinding(bindingRequest);
}
} catch (Exception ex) {
bindingResponse.setErrorMessage(ex.toString());
return bindingResponse;
}
}
/api/v1/users/bind is the address to which the binding or unbinding request from a Huawei ability gallery is sent. This address needs to be configured on the Fulfillment page in HUAWEI Ability Gallery. The request body of this service is in the following format.
Code:
{
"version":"1.0",
"header":{
"type":"Directive",
"timestamp":"15869741258",
"name":"AcceptGrant",
"namespace":"Authorization"
},
"inquire":{
"inquireId":"efd1f6ca-8fdf-11e8-9eb6-529269fb1459",
"payload":{
"grant":{
"type":"OAuth2.Authorization",
"openId":"ZU5uc3v045B6gkNSIXmf0414BQJQ5O37",
"sign":"TZ5uc3v0BQJQ5SIXmf0414O3745B6gkNXYZQPR",
"abilityId":"77fd5f3fc18243b58f2fe3e843991100"
}
}
}
}
OpenId: It is generated by Huawei Ability Gallery, using an app ID and a Huawei user ID.
Note: OpenId is unique for the same app and user.
3. Returns the Deep Link Of The Login Page
Deeplink needs to be created client-side has a specific syntax. This syntax should end with OpenID.
Code:
@Override
public AccountBindingResponseModel doAccountBinding(AccountBindingRequestModel bindingRequest) {
logger.debug("starts doAccountBinding");
AccountBindingResponseModel bindingResponse = new AccountBindingResponseModel();
bindingResponse.setErrorCode(SUCCESS);
bindingResponse.setErrorMessage("OK");
String openId = bindingRequest.getInquire().getPayload().getGrant().getOpenId();
try {
ValidatorUtils.checkArgument(!StringUtils.isEmpty(openId));
} catch (IllegalArgumentException e) {
bindingResponse.setErrorCode(INVALID_PARAMETER);
bindingResponse.setErrorMessage("param openid is invalid");
}
AccountDirectiveInteraction interaction = new AccountDirectiveInteraction();
NativeAppLinkInteraction deepLink = new NativeAppLinkInteraction();
String appName = ResourceBundle.getBundle("url").getString("appname");
String packagename = ResourceBundle.getBundle("url").getString("packagename");
deepLink.setAppName(appName);
deepLink.setAppPackage(packagename);
deepLink.setMinVersion(1L);
deepLink.setUrl("app://open.sigIn.page/openId=" + openId);
interaction.setDeepLink(deepLink);
Reply reply = new Reply();
reply.setAccountLoginAddr(interaction);
bindingResponse.setReply(reply);
bindingResponse.setVersion("1.0");
logger.debug("doAccountBinding ended successfully");
return bindingResponse;
}
5. Authentication / Login with developer’s server
A deep link is created in the desired format. Afterward, the user redirects to the app sign-in page via the mentioned link.
The user enters the authorization credentials on the sign-in page. The sign-in page sends the credentials and OpenID to the developer’s server. OpenId is obtained from account binding response.
Once the developer’s server has verified the credentials, it will bind the developer’s account to the user’s OpenID.
Code:
if(loginRequestModel.getOpenId() != null) {
mAccountBindingClient.sendBindRequest(loginRequestModel.getOpenId(), false);
foundUser.setOpenId(loginRequestModel.getOpenId());
userRepository.saveAndFlush(foundUser);
}
6. Binding Operation
We need to “accessToken” for binding operation. We can obtain “accessToken” while creating an HTTP post request
Code:
@Override
public BindResponse sendBindRequest(String openId, Boolean unBind) throws Exception {
CloseableHttpClient httpClient = HttpClients.createDefault();
logger.debug("starts sendBindRequest with openId is {}", openId);
String serverUrl = "";
try {
OpenAccountBindReq bindReq = new OpenAccountBindReq();
bindReq.setOpenId(openId);
bindReq.setRequestTime(TimeUtil.toTimeString(new Date()));
StringEntity entity = new StringEntity(JSON.toJSONString(bindReq), "UTF-8");
if(unBind) {
serverUrl = ResourceBundle.getBundle("url").getString("huawei.hag.unBindUrl");
} else {
serverUrl = ResourceBundle.getBundle("url").getString("huawei.hag.bindUrl");
}
HttpResponse response = httpClient.execute(getHttpPost(serverUrl, entity));
String reponseContent = EntityUtils.toString(response.getEntity());
Integer status = response.getStatusLine().getStatusCode();
BindResponse bindResponse = new BindResponse();
JSONObject jsonObject = JSONObject.parseObject(reponseContent);
String code = jsonObject.getString("code");
String desc = jsonObject.getString("desc");
if (status == 200) {
bindResponse.setCode(code);
bindResponse.setDesc(desc);
logger.debug("sendBindRequest process ended successfully");
return bindResponse;
}
logger.debug("sendBindRequest process error, error code :{}, error desc : {}", code, desc);
throw new Exception("Send Request to " + serverUrl + " status is " + status);
} catch (Exception e) {
throw new Exception(e.toString());
} finally {
httpClient.close();
}
}
Code:
private HttpPost getHttpPost(String postUrl, StringEntity entity) throws Exception {
HttpPost httpPost = new HttpPost(postUrl);
try {
executeAccessToken();
httpPost.setHeader("Authorization", "Bearer " + accessToken);
httpPost.setHeader("Content-Type", "application/json");
httpPost.setHeader("Accept", "application/json");
httpPost.setHeader("x-appid", appId);
httpPost.setEntity(entity);
return httpPost;
} catch (IOException ex) {
throw new IOException(ex.toString());
}
}
Code:
private void executeAccessToken() throws Exception {
String requestBody = createRequestBody(appId, appSecret);
HttpPost httpPost = new HttpPost(TOKEN_AT_URL);
CloseableHttpClient httpClient = HttpClients.createDefault();
try {
StringEntity entity = new StringEntity(requestBody);
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");
httpPost.setEntity(entity);
HttpResponse response = httpClient.execute(httpPost);
String jsonStr = EntityUtils.toString(response.getEntity());
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != 200) {
throw new Exception("Send Request to " + TOKEN_AT_URL + " failed with httpcode is " + statusCode);
}
JSONObject jsonObject = JSONObject.parseObject(jsonStr);
accessToken = jsonObject.getString("access_token");
} catch (IOException ex) {
throw new IOException(ex.toString());
} finally {
httpClient.close();
}
}
Account Unbinding
If the user taps the account unbinding button, Huawei Ability Gallery sends the request to the developer server (/api/v1/users/bind).
The request body of unbinding operation is the same as the binding operation’s body. There is only one difference and that is the namespace.
The namespace must be “Deauthorization” for unbinding operations.
Code:
{
"version":"1.0",
"header":{
"type":"Directive",
"timestamp":"15869741258",
"name":"AcceptGrant",
"namespace":"Deauthorization"
},
"inquire":{
"inquireId":"efd1f6ca-8fdf-11e8-9eb6-529269fb1459",
"payload":{
"grant":{
"type":"OAuth2.Authorization",
"openId":"ZU5uc3v045B6gkNSIXmf0414BQJQ5O37",
"sign":"TZ5uc3v0BQJQ5SIXmf0414O3745B6gkNXYZQPR",
"abilityId":"7a0af511a91f4591b4efbbaacd8bee60"
}
}
}
}
Code:
@Override
public AccountBindingResponseModel doAccountUnBinding(AccountBindingRequestModel bindingRequest) throws Exception {
logger.debug("starts doAccountUnBinding");
try {
String openId = bindingRequest.getInquire().getPayload().getGrant().getOpenId();
BindResponse response = sendBindRequest(openId, true);
AccountBindingResponseModel bindingResponse = new AccountBindingResponseModel();
bindingResponse.setErrorCode(response.getCode());
bindingResponse.setErrorMessage(response.getDesc());
User foundUser = userRepository.findByOpenId(openId).orElse(null);
if (foundUser == null) {
logger.debug("user not found by open id, openId is {}", openId);
} else {
foundUser.setOpenId(null);
userRepository.saveAndFlush(foundUser);
}
logger.debug("doAccountUnBinding process ended successfully");
return bindingResponse;
} catch (Exception e) {
throw new Exception(e.toString());
}
}
Conclusion
I hope this article helps you better understand Huawei Ability and account binding/unbinding operations.
Thanks for reading. If you have a question, don’t hesitate to contact me.
References:
Document
developer.huawei.com
Document
developer.huawei.com
Document
developer.huawei.com
Click to expand...
Click to collapse
Its informative and clear