[Huawei Developers] yagmur.kilic:
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

@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.

{
"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.

@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.

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

@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();
}

}

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());
}
}

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.

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

@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