Shielding apps with the App Shielding API

App Shielding and the OneSpan Mobile Portal also allow you to shield your apps via the App Shielding API.

Initial setup

The steps described below need to be performed for the initial setup only.

App Shielding API—initial setup

To set up the App Shielding API

  1. Generate the client ID and client secret from the App Shielding user interface on the OneSpan Mobile Portal.

    You only need to generate these once as they apply to all your projects, except if either of the two is compromised. In that case, you must generate a new ID or secret.

  2. Generate the configuration key from the App Shielding user interface on the OneSpan Mobile Portal.

    Generate a separate key for each project as well as for each App Shielding version!

  3. Copy the client ID, client secret, and configuration key from the App Shielding user interface and set it in your automation script(s), CI/CD pipeline etc.

Shielding apps

Once you have set up the App Shielding API, you can shield your apps with it.

Sample scripts

OneSpan provides sample scripts for shielding an app with the App Shielding API. You can use the scripts provided in shieldingapps.zip to start shielding the app, obtain the shielding progress, and download the shielded app.

These scripts are only examples to demonstrate how to shield an app with the public APIs!

Workflows to shield apps

The steps described below are performed every time you shield your app(s) with the API.

App Shielding API—shielding flow

To shield apps with the API

  1. Obtain the access token via the authentication server with the client ID and client secret you created during initial setup with the POST /oauth2/token authorization endpoint.

    Endpoint signature:

    • Host: https://auth.mobile.onespan.com/oauth2/token
    • Header: Authorization: Basic YOUR_CLIENT_CREDENTIALS
    • Header: Content-Type: application/x-www-form-urlencoded
    • Request body: grant_type=client_credentials
    • Response body sample (JSON):

      {
          "access_token": "AUTHORIZATION_TOKEN",
          "expires_in": 3600,
          "token_type": "Bearer"
      }

      where access_token is the authorization token which must be added to the header when invoking all shielding REST APIs.

    • Status response: 200
    1. Implement the following in your pipeline:

      client_id = <CLIENT_ID> # Replace with your Client ID obtained/copied during initial setup.
      client_secret = <CLIENT_SECRET> # Replace with your Client Secret obtained/copied during initial setup.
      auth_server_url = "https://auth.mobile.onespan.com/oauth2/token" # The Auth server URL
      payload = 'grant_type=client_credentials'
      headers = {'Content-Type': 'application/x-www-form-urlencoded'}
      response = requests.request("POST", auth_server_url, headers=headers, data=payload, auth=HTTPBasicAuth(client_id, client_secret)) # client_id and client_secret obtained during initial setup.
      access_token = response.json().get('access_token') # JWT token used in REST API requests for authorization
  2. Obtain the upload URL for uploading the mobile app to the storage server via the REST API with the POST /v1/shieldingapps public shielding endpoint. This creates a signed upload URL to upload the mobile app to be shielded.

    Endpoint signature:

    • Host: https://api-appshielding.mobile.onespan.com/v1/shieldingapps
    • Header: Authorization: BearerAUTHORIZATION_TOKEN
    • Request body sample (JSON):

      {
          "configurationKey": "0ef41a59-1eb9-1234-abcd-7368f5149a8b",
          "shielderPlatform": "ANDROID",
          "fileNameWithExt": "some_android_mobile.apk"
      }

      where:

      • configurationKey is the key obtained from the user interface
      • shielderPlatform is the mobile platform, with two possible values, ANDROID or IOS)
      • fileNameWithExt is the full file name with file extension of the app to be uploaded.
    • Status response: 200
    • Response body sample (JSON):

      {
          "url": "https://<long-pre-signed-url>/<to-upload>",
          "expiredAt": "2024-06-19T17:09:16.995169312Z",
          "uuid": "512729d2-dbf0-9876-dcba-491c441a1122"
      }

      where:

      • url is the pre-signed URL to use for uploading the mobile app to the storage server.
      • uuid is the unique identifier used to map the individual mobile app shielding process. You will need this uuid to check the shielding progress or to download the shielded mobile app via the shielding REST APIs.
      1. Implement the following in your pipeline:

        base_shieldingapps_api_url = "https://api-radium.mobile.eu.onespan.com/v1/shieldingapps"  # The API service URL
        configuration_key_value = <CONFIGURATION_KEY> # Replace with your Configuration Key obtained/copied during initial setup.
        shielder_platform_value = <PLATFORM> # Replace with the corresponding platform: ANDROID or IOS
        file_name_with_ext_value = <APP_FILE_NAME> # Replace with your mobile application file name and extension e.g: app.apk app.ipa

        headers = {
            'Content-Type': 'application/json',
            'Authorization': f"Bearer {access_token}" # Access token obtained via the authentication server with the Client ID and Client Secret.
        }
        payload = json.dumps({
            "configurationKey": configuration_key_value,
            "shielderPlatform": shielder_platform_value,
            "fileNameWithExt": file_name_with_ext_value
        })
        response = requests.request("POST", base_shieldingapps_api_url, headers=headers, data=payload)

        if response.status_code == 201:
            print(f"Upload URL successfully created")
            pre_signed_url = response.json().get('url') # The URL for uploading the mobile app to be shielded.
            shielding_uuid = response.json().get('uuid') # The UUID reference to the shielding process.
        else:
            print("Something went wrong when trying to obtained Upload URL!!")
            print(response.text)
  3. Upload the mobile app to the storage server with the pre-signed upload URL from the public shielding endpoint.

    Upload request signature:

    • PUT https//long-pre-signed-url/to-upload
    • data: file_content
    • Status response: 200
    1. Implement the following in your pipeline:

      file_path = <PATH_FILE_NAME> #Replace with the path to your local file e.g: /home/apps/app.apk
      try:
          with open(file_path, 'rb') as file:
              payload_file_content = file.read()
      except FileNotFoundError:
          print(f"File not found: {file_path}")
          exit(1)

      headers = {'Content-Type': 'application/octet-stream'}
      response = requests.request("PUT", pre_signed_url, headers=headers, data=payload_file_content)
      response.raise_for_status()
      print("File uploaded successfully!!")
  4. Once the app is completely uploaded to the storage server, invoke the Start Shielding REST API with POST /v1/shieldingapps?uuid=SHIELDING-UUID. This starts the shielding process of a mobile app previously uploaded, using the SHIELDING_UUID value obtained with the upload URL.

    Endpoint signature:

    • Host: https://api-appshielding.mobile.onespan.com/v1/shieldingapps
    • Query parameter required: uuid=SHIELDING_UUID
    • Header: Authorization: BearerAUTHORIZATION_TOKEN
    • Provide your value attributes: Request body sample (JSON).

      {
          "configurationKey": "0ef41a59-1eb9-1234-abcd-7368f5149a8b",
          "fileNameWithExt": "some_android_mobile.apk"
      }

      where:

      • configurationKey is the key obtained from the user interface
      • fileNameWithExt is the full file name with file extension of the app to be uploaded.
    • Status response: 201
    • Response body sample (JSON):

      {
        "uuid": "512729d2-dbf0-9876-dcba-491c441a1122"  
      }

      where uuid is the same as that provided as query parameter.

    1. Implement the following in your pipeline:

      start_shieldingapps_api_url = f"{base_shieldingapps_api_url}?uuid={shielding_uuid}" # Same shielding_uuid obtained with the upload URL.
      payload = json.dumps({
          "configurationKey": configuration_key_value, # Your Configuration Key obtained during initial setup.
           "fileNameWithExt": file_name_with_ext_value # Same as provided with the upload URL.
      })
      headers = {
          'Content-Type': 'application/json',
           'Authorization': f"Bearer {access_token}" # Access token obtained via the authentication server with the Client ID and Client Secret.
      }
      response = requests.request("POST", start_shieldingapps_api_url, headers=headers, data=payload)

      if response.status_code == 201:
          print(f"App Shielding successfully started : {response.json().get('uuid')}")
      else:
          print("Something went wrong when trying to start App shielding!!")
  5. After the shielding process has started, your pipeline can query the API at regular intervals to monitor the shielding progress with the Shielding Progress REST API. Get log details of the shielding process with GET /v1/shieldingapps?uuid=512729d2-dbf0-9876-dcba-491c441a1122&action=progress, using the SHIELDING_UUID value obtained with the upload URL.

    The Get Shielding Progress REST API can be invoked multiple times to obtain the progress, until the shielding status changes to FAILED or SUCCESS.

    Endpoint signature:

    • Host: https://api-appshielding.mobile.onespan.com/v1/shieldingapps
    • Query parameter required: uuid=SHIELDING_UUID, action=progress
    • Header: Authorization: BearerAUTHORIZATION_TOKEN
    • Response body sample (JSON):

      • In progress:

        {
            "uuid": "512729d2-dbf0-9876-dcba-491c441a1122",
            "shieldingStatus": "IN_PROGRESS",
            "progressInPercent": 14,
            "progressTimestamp": "2024-06-27T17:51:39.432Z",
            "shieldingLogs": "Cleaning workspace...\nLoading data...\nWriting temporary configuration files...\nRunning Shielder...\n* Shielding app: /tmp/...\nShielder for Android, version 6.5.3 (id: OneSpan, Jigsaw: 1.15.0)\n* Decoding application\n..."
        }

        where:

        • shieldingStatus is the status of the shielding process. When it is in progress, log details are available.
        • shieldingLogs are the log details of the shielding process.
        • progressInPercent is the progress in percent of the shielding process.

      • Status response: 200
      • Completed:

        {
            "uuid": "512729d2-dbf0-9876-dcba-491c441a1122",
            "shieldingStatus": "SUCCESS"
        }

        where shieldingStatus is the status of the shielding process. When it is in progress, log details are available.

    1. Implement the following in your pipeline:

      progress_shieldingapps_api_url = f"{base_shieldingapps_api_url}?uuid={shielding_uuid}&action=progress" # Same shielding_uuid obtained with the Upload URL.

      headers = {'Authorization': f"Bearer {access_token}"} # Access token obtained during initial setup.
      response_progress = requests.request("GET", progress_shieldingapps_api_url, headers=headers)

      if response_progress.status_code == 200:
          print(f"Response: {response_progress.json()}") # Shielding progress details
      else:
          print("Something went wrong when trying to get App shielding progress!!")
  6. Once shielding has completed, obtain the download URL via the REST API with GET /v1/shieldingapps?uuid=512729d2-dbf0-9876-dcba-491c441a1122&action=download, using the SHIELDING_UUID value obtained with the upload URL.

    Endpoint signature:

    • Host: https://api-appshielding.mobile.onespan.com/v1/shieldingapps
    • Query parameter required: uuid=SHIELDING_UUID, action=download
    • Header: Authorization: BearerAUTHORIZATION_TOKEN
    • Response body sample (JSON):

      {
          "url": "https://long-pre-signed-url/to-download",
          "expiredAt": "2024-06-19T18:10:16.995169312Z",
      }

      url is the pre-signed URL to use for downloading the mobile app from the storage server

    • Status response: 200
    1. Implement the following in your pipeline:

      download_shieldingapps_api_url = f"{base_shieldingapps_api_url}?uuid={shielding_uuid}&action=download"
      headers = {'Authorization': f"Bearer {access_token}"}
      response_download = requests.request("GET", download_shieldingapps_api_url, headers=headers)

      if response_download.status_code == 200:
        print(f"Response download: {response_download.json()}")
        response_download_url = response_download.json().get('url')
        print(f"Shielded App Download URL: {response_download_url}")
      else:
        print("Something went wrong when trying to get App shielding download URL!!")
  7. (Optional) Download the shielded mobile app via your pipeline from the storage server with the pre-signed URL obtained in the previous step. The shielded app and other files will be downloaded as a .zip file.

    Download request signature:

    • URL: DOWNLOAD_URL obtained from the public shielding endpoint.
    • GET https//long-pre-signed-url/to-download
    • Status response: 200
    1. Implement the following in your pipeline:

      shielded_app_download_url = response_download_url # Same URL obtained as Upload URL.
      response_shielded_download = requests.request("GET", shielded_app_download_url)

      if response_shielded_download.status_code == 200:
        print(f"File successfully downloaded")
        shielded_filename = os.path.basename(urlparse(response_shielded_download.url).path)
        output_shielded_file = os.path.join(shielded_filename) # Stored locally. Update it to your output path
        print(f"Filename: {shielded_filename}")
        with open(output_shielded_file, "wb") as file:
          file.write(response_shielded_download.content)
        print(f"File successfully stored")
      else:
        print("Something went wrong when trying to download the shielded App!!")