DEV Community

HarmonyOS
HarmonyOS

Posted on

Get an access token after the authorization for the Health Kit

Read the original article:Get an access token after the authorization for the Health Kit

Requirement Description

User wants to get health data, but needs an access token to send a request health endpoint. How can the user get an access token?

Background Knowledge

1) Generate an authorization code.

  • The code is obtained by redirecting the user's browser to the address https://oauth-login.cloud.huawei.com/oauth2/v3/authorize. If an error occurs, please refer to Errors and Troubleshooting.

2) Generate an access token based on the authorization code.

  • After obtaining the authorization code in the previous step, it can be used to generate an access token by sending a request using the POST method from the app's server to the Huawei OAuth 2.0 authorization service address https://oauth-login.cloud.huawei.com/oauth2/v3/token with some input parameters:

3) After the access token expires, use the refresh token to obtain a new access token.

  • A refresh token can be used to obtain a new access token by sending a request using the POST method from the app's server to the Huawei OAuth 2.0 authorization service address https://oauth-login.cloud.huawei.com/oauth2/v3/token with some input parameters.

Implementation Steps

1) Define the client_id, client_secret, auth_url, redirect_url to get access token.

2) Send a token request with the authorization code received from the user during the OAuth authentication process, store the received tokens, and provide helper methods for storing the access token.

To obtain an authorization code, the user is redirected to a URL. After the user grants permission, an authorization code is returned.

3) After receiving the authorization code from the user, an access token is obtained using this code. If the token is already stored, the user is redirected directly to the application without needing to authenticate again.

Code Snippet / Configuration

1) We need to define the client_id, client_secret, auth_url, redirect_url to get access token.

export class APIConstants {
  static readonly AUTH_URL: string = 'YOUR_URL'
  static readonly CLIENT_ID: string = 'YOUR_CLIENT_ID'
  static readonly CLIENT_SECRET: string = 'YOUR_CLIENT_SECRET'
  static readonly REDIRECT_URL: string = 'YOUR_REDIRECT_URL'
}
Enter fullscreen mode Exit fullscreen mode

2) This class sends a token request with the authorization code received from the user during the OAuth authentication process, stores the received tokens, and provides helper methods for storing the access token.

To obtain an authorization_code, the user is redirected to a URL. After the user grants permission, an authorization code is returned.

import Preferences from '../common/Preferences';
import { APIConstants } from '../common/Constants';
import { http } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { TokenResponse } from '../model/TokenResponse';
import taskpool from '@ohos.taskpool';
import { processRespTask } from '../common/processRespTask';


export class AuthenticationViewModel {
  accessToken: string = '';

  getAccessTokenFromStorage(): string {
    const token = Preferences.instance.getPreference('accessToken');
    this.accessToken = token;
    return token;
  }

  saveTokens(accessToken: string, refreshToken: string) {
    Preferences.instance.putPreference('accessToken', accessToken);
    Preferences.instance.putPreference('refreshToken', refreshToken);
    this.accessToken = accessToken;
  }

  getQueryParameter(url: string, paramName: string): string | null {
    const queryStartIndex = url.indexOf('?');
    if (queryStartIndex === -1) {
      return null;
    }

    const queryString = url.substring(queryStartIndex + 1);
    const params = queryString.split('&');

    for (const param of params) {
      const parts = param.split('=');
      if (parts.length === 2) {
        const key = decodeURIComponent(parts[0]);
        const value = decodeURIComponent(parts[1]);
        if (key === paramName) {
          return value;
        }
      }
    }

    return null;
  }

  async postOAuthToken(code: string): Promise<string | null> {
    const httpRequest = http.createHttp();

    const body =
      'grant_type=authorization_code' +
        `&code=${encodeURIComponent(code)}` +
        `&client_id=${encodeURIComponent(APIConstants.CLIENT_ID)}` +
        `&client_secret=${encodeURIComponent(APIConstants.CLIENT_SECRET)}` +
        `&redirect_uri=${encodeURIComponent(APIConstants.REDIRECT_URL)}`;

    return new Promise((resolve, reject) => {
      httpRequest.request(
        APIConstants.AUTH_URL,
        {
          method: http.RequestMethod.POST,
          header: {
            'Content-Type': 'application/x-www-form-urlencoded',
          },
          extraData: body,
          expectDataType: http.HttpDataType.STRING,
        },
        async (err: BusinessError, data) => {
          let task = new taskpool.Task(processRespTask, [err, data]);
          await taskpool.execute(task);

          let parsed: TokenResponse | null = null;

          if (typeof data.result === 'string') {
            try {
              parsed = JSON.parse(data.result) as TokenResponse;
            } catch (e) {
              console.error('JSON parse failed:', e);
              resolve(null);
              return;
            }
          } else if (typeof data.result === 'object') {
            parsed = data.result as TokenResponse;
          }

          if (parsed && parsed.access_token && parsed.refresh_token) {
            this.saveTokens(parsed.access_token, parsed.refresh_token);
            resolve(parsed.access_token);
          } else {
            console.error('Token parse error or missing fields:', parsed);
            resolve(null);
          }

        }
      );
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

3) After receiving the authorization code from the user, an access token is obtained using this code. If the token is already stored, the user is redirected directly to the application without needing to authenticate again.

import { AuthenticationViewModel } from '../viewmodel/AuthenticationViewModel';
import { APIConstants } from '../common/Constants';
import { webview } from '@kit.ArkWeb';
import { router } from '@kit.ArkUI';
import { ButtonComponent } from '../component/ButtonComponent';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
export struct Authentication {
  @State shouldShowWebView: boolean = true;
  @State accessToken: string = '';
  @State activationKey: string = '';
  controller: webview.WebviewController = new webview.WebviewController();
  private viewModel = new AuthenticationViewModel();

  aboutToAppear(): void {
    const storedToken = this.viewModel.getAccessTokenFromStorage();
    if (storedToken.length > 0) {
      this.accessToken = storedToken;
      this.shouldShowWebView = false;
    }
  }

  handleOAuthCode(code: string) {
    this.activationKey = code;
    this.shouldShowWebView = false;

    this.viewModel.postOAuthToken(code).then((token) => {
      if (token) {
        this.accessToken = token;
      }
    }).catch((e: BusinessError) => {
      console.error('Token exchange failed:', e);
    });
  }

  build() {
    Column() {
      if (this.shouldShowWebView) {
        Web({ src: APIConstants.AUTH_URL, controller: this.controller })
          .width('100%')
          .height('100%')
          .onLoadIntercept((event) => {
            if (event) {
              const url = event.data.getRequestUrl();
              console.log('URL:' + url);
              const code = this.viewModel.getQueryParameter(url, 'code');
              if (code) {
                console.info('Code received from redirect: ' + code);
                this.handleOAuthCode(code);
              }
            }
            return false;
          });
      } else {
        Column() {
          Text($r('app.string.access_token'))
            .fontSize(12)
            .height('20%')
            .width('100%')
            .fontWeight(FontWeight.Bold)
            .textAlign(TextAlign.Center);

          Text(this.accessToken)
            .fontSize(12)
            .height('30%')
            .width('100%')
            .fontColor(Color.Red);

          ButtonComponent({ text: $r('app.string.continue') })
            .margin({ bottom: 5 })
            .onClick(() => {
              router.pushUrl({ url: 'pages/HomePage' });
            });

          ButtonComponent({ text: $r('app.string.steps') })
            .margin({ bottom: 5 })
            .onClick(() => {
              router.pushUrl({
                url: 'pages/StepsPage',
                params: { access_token: this.accessToken }
              });
            });
        }
        .height('100%')
        .width('100%');
      }
    }
    .height('100%')
    .width('100%');
  }
}
Enter fullscreen mode Exit fullscreen mode

Test Results

cke_1160.png

Limitations or Considerations

You need to apply for the Huawei Health application. After approval, you can get data from the Huawei Health.

Related Documents or Links

https://developer.huawei.com/consumer/en/doc/HMSCore-Guides/description-0000001558389985

Written by Merve Yonetci

Top comments (0)