avatarsimbu

Summary

This article provides a step-by-step guide on how to authenticate a Flutter application on Android devices using Microsoft Authentication Library (MSAL) with Azure Active Directory (AAD) for secure access to Microsoft APIs.

Abstract

The article is the fifth in a series focusing on securing Flutter applications using Microsoft technologies. It details the process of integrating MSAL into an Android Flutter app to authenticate users via Azure AD. The guide covers adding MSAL as a dependency, configuring a signing certificate, registering the app in Azure AD, setting up necessary permissions and redirects in the AndroidManifest.xml file, creating an MSAL configuration file, and handling specific issues like null exceptions. The author also includes screenshots of the login process and provides commands for managing the Android Keystore. The article aims to help developers save time by providing a comprehensive setup for Android app authentication with Azure AD.

Opinions

  • The author emphasizes the importance of using a signing certificate for the debug build to match the redirect URI configured in Azure AD.
  • There is an acknowledgment of the time-consuming nature of troubleshooting certain issues, such as the authorizationInCurrentTask boolean null exception, and the author provides a link to a solution that helped them.
  • The author suggests that hardcoding secret values is not a best practice and recommends injecting configuration values to enhance security.
  • There is a warning about the security risk associated with using the SHA1withRSA signature algorithm, hinting at future updates that may disable its use.
  • The author indicates a need for further improvements in app signing and authentication redirection for different environments beyond the quick and dirty debug key setup.
  • The article concludes with a list of resources and packages that can be used for further reading and implementation of Azure AD authentication in Android applications.
Join Medium to view all my articles.

Flutter — Microsoft API Management secured with AD — MSAL for Android

This is the fifth part of a mini series to show how to secure your Flutter applications using Microsoft technologies:

  1. Web API — Setting up your Web API with Visual Studio.
  2. APIM Service — Creating an API management service in Azure.
  3. Secure Gateway — Securing requests through the API gateway.
  4. MSAL — Authenticating your Flutter app on iOS.
  5. MSAL — Authenticating your Flutter app on Android.
  6. MSAL — Authenticating your Flutter app on the Web.
  7. Hosted API — Hosting your Web API’s in Azure.
  8. Secure Hosted API — Secure requests with the APIM Gateway

In this article we show how to get an access token by authenticating against an Azure AD on an Android phone or tablet.

Step 1: Declare MSAL as a dependency

Add the following entries to your app gradle android/app/src/build.gradle:

dependencies {
    implementation 'com.microsoft.identity.client:msal:3.0.+'
}
allprojects {
    repositories {
        mavenCentral()
        maven { 
            url 'https://pkgs.dev.azure.com/MicrosoftDeviceSDK/DuoSDK-Public/_packaging/Duo-SDK-Feed/maven/v1' 
        }
    }
}

Step 2: Add a signing certificate to the debug Keystore

Azure AD requires the app’s package name and the Base64-encoded representation of your app’s signature to setup a redirect for authentication.

The first step is to create and add a new certificate to our debug keystore:

keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64

Password prompt and output:

Enter keystore password:  <KeyPassword>
<New KeyHash>

Step 3: Configure debug signing

Use the androiddebugkey we created in the previous step to sign our debug build in the app gradle android/app/src/build.gradle:

signingConfigs {
        debug {
            storeFile file("~/.android/debug.keystore")
            storePassword '<YourPassword>'
            keyAlias 'androiddebugkey'
            keyPassword '<YourPassword>'
        }
    }

    buildTypes {
        release {
            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            //signingConfig signingConfigs.release
            signingConfig signingConfigs.debug
        }
        debug {
            signingConfig signingConfigs.debug
        }
    }

Step 4: Register the redirect URL in Azure AD

Login into your Azure Portal and select Azure Active Directory..App registrations from the menu and open the DigestableMe registration we created in the last article, select Authentication from the sub menu and add the Android platform:

Use the signature hash created in the previous step:

Step 5: Configure permissions and redirect in AndroidManifest.xml

Request the following permissions in your Android Manifest file(android/app/src/main/AndroidManifest.xml):

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

And configure redirect:

<activity android:name="com.microsoft.identity.client.BrowserTabActivity"
    android:launchMode="singleTask">
    <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:host="com.simbu.digestableprologue"
            android:path="<YourSigHashKey from prev step>"
            android:scheme="msauth" />
    </intent-filter>
</activity>

Step 6: Create MSAL config file

Create the file:

Add the config:

{
  "client_id": "<ClientId Find it in Azure App Registration>",
  "redirect_uri": "msauth://com.simbu.digestableprologue/<YourSigHashKey from prev step>",
  "authorities": [
    {
      "type": "AAD",
      "audience": {
        "type": "AzureADMyOrg",
        "tenant_id": "<TennantId Find it in Azure App Registration>"
      },
      "default": true
    }
  ],
  "authorization_user_agent": "DEFAULT",
  "minimum_required_broker_protocol_version": "3.0",
  "multiple_clouds_supported": false,
  "broker_redirect_uri_registered": true,
  "web_view_zoom_controls_enabled": true,
  "web_view_zoom_enabled": true,
  "environment": "Production",
  "power_opt_check_for_network_req_enabled": true,
  "handle_null_taskaffinity": false,
  "authorization_in_current_task": false,
  "http": {
    "connect_timeout": 10000,
    "read_timeout": 30000
  },
  "logging": {
    "pii_enabled": false,
    "log_level": "WARNING",
    "logcat_enabled": true
  },
  "account_mode": "MULTIPLE",
  "browser_safelist": [
    {
      "browser_package_name": "com.android.chrome",
      "browser_signature_hashes": [
        "7fmduHKTdHHrlMvldlEqAIlSfii1tl35bxj1OXN5Ve8c4lU6URVu4xtSHc3BVZxS6WWJnxMDhIfQN0N0K2NDJg=="
      ]
    },
    {
      "browser_package_name": "org.mozilla.firefox",
      "browser_signature_hashes": [
        "2gCe6pR_AO_Q2Vu8Iep-4AsiKNnUHQxu0FaDHO_qa178GByKybdT_BuE8_dYk99G5Uvx_gdONXAOO2EaXidpVQ=="
      ]
    },
    {
      "browser_package_name": "com.sec.android.app.sbrowser",
      "browser_signature_hashes": [
        "ABi2fbt8vkzj7SJ8aD5jc4xJFTDFntdkMrYXL3itsvqY1QIw-dZozdop5rgKNxjbrQAd5nntAGpgh9w84O1Xgg=="
      ]
    },
    {
      "browser_package_name": "com.cloudmosa.puffinFree",
      "browser_signature_hashes": [
        "1WqG8SoK2WvE4NTYgr2550TRhjhxT-7DWxu6C_o6GrOLK6xzG67Hq7GCGDjkAFRCOChlo2XUUglLRAYu3Mn8Ag=="
      ]
    },
    {
      "browser_package_name": "com.duckduckgo.mobile.android",
      "browser_signature_hashes": [
        "S5Av4cfEycCvIvKPpKGjyCuAE5gZ8y60-knFfGkAEIZWPr9lU5kA7iOAlSZxaJei08s0ruDvuEzFYlmH-jAi4Q=="
      ]
    },
    {
      "browser_package_name": "com.explore.web.browser",
      "browser_signature_hashes": [
        "BzDzBVSAwah8f_A0MYJCPOkt0eb7WcIEw6Udn7VLcizjoU3wxAzVisCm6bW7uTs4WpMfBEJYf0nDgzTYvYHCag=="
      ]
    },
    {
      "browser_package_name": "com.ksmobile.cb",
      "browser_signature_hashes": [
        "lFDYx1Rwc7_XUn4KlfQk2klXLufRyuGHLa3a7rNjqQMkMaxZueQfxukVTvA7yKKp3Md3XUeeDSWGIZcRy7nouw=="
      ]
    },
    {
      "browser_package_name": "com.microsoft.emmx",
      "browser_signature_hashes": [
        "Ivy-Rk6ztai_IudfbyUrSHugzRqAtHWslFvHT0PTvLMsEKLUIgv7ZZbVxygWy_M5mOPpfjZrd3vOx3t-cA6fVQ=="
      ]
    },
    {
      "browser_package_name": "com.opera.browser",
      "browser_signature_hashes": [
        "FIJ3IIeqB7V0qHpRNEpYNkhEGA_eJaf7ntca-Oa_6Feev3UkgnpguTNV31JdAmpEFPGNPo0RHqdlU0k-3jWJWw=="
      ]
    },
    {
      "browser_package_name": "com.opera.mini.native",
      "browser_signature_hashes": [
        "TOTyHs086iGIEdxrX_24aAewTZxV7Wbi6niS2ZrpPhLkjuZPAh1c3NQ_U4Lx1KdgyhQE4BiS36MIfP6LbmmUYQ=="
      ]
    },
    {
      "browser_package_name": "mobi.mgeek.TunnyBrowser",
      "browser_signature_hashes": [
        "RMVoXuK1sfJZuGZ8onG1yhMc-sKiAV2NiB_GZfdNlN8XJ78XEE2wPM6LnQiyltF25GkHiPN2iKQiGwaO2bkyyQ=="
      ]
    },
    {
      "browser_package_name": "org.mozilla.focus",
      "browser_signature_hashes": [
        "L72dT-stFqomSY7sYySrgBJ3VYKbipMZapmUXfTZNqOzN_dekT5wdBACJkpz0C6P0yx5EmZ5IciI93Q0hq0oYA=="
      ]
    },
    {
      "browser_package_name": "com.cake.browser",
      "browser_signature_hashes": [
        "442kvSdZT1fEAewzSi8Wre73x4mWmHBhOFtQ-9T9N6ExZzUdsELUmaaS0edsI7ur2nY-bjbWX7IpluFOyvKkOA=="
      ]
    },
    {
      "browser_package_name": "com.brave.browser",
      "browser_signature_hashes": [
        "wIwX1v_1TfPxHm5qn-_jdGoH3Pa9VVMR5dtVz0Y0xqPkyM_KlavjWPSgOolrVH05AVO1cHWoLPqMzCH04Pw8LQ=="
      ]
    },
    {
      "browser_package_name": "com.kiwibrowser.browser",
      "browser_signature_hashes": [
        "kmPeixKA04JcDuWNBMUPu_6WaODr6a9ofROUIHIGxiiFGvH8Y92MonrDQmsNqEJO2DQkpEQc425WmAYB4NlD3Q=="
      ]
    },
    {
      "browser_package_name": "com.mi.globalbrowser.mini",
      "browser_signature_hashes": [
        "6FEWlPfWn-omfES2ZYDj5bZUIR5au_nfyRr-o_1R3fesjfoV1JptBMumtvVIo0q37abcMRWQt9RUSNXpzKpNdA=="
      ]
    },
    {
      "browser_package_name": "mark.via.gp",
      "browser_signature_hashes": [
        "oTJf5e5nB1NinkdBpkkmhPnwbVRmDlHZ-s_QhvyuGKM5nq5XtjA439O31wxrkL6ReHyyKfDHFUHpQnoXoj--Ig=="
      ]
    }
  ]
}

I lost quite a bit of time to a bool null exception around authorizationInCurrentTask, but eventually found the answer here, thanks Low Chen Thye.

Ta Da

Login screen when the app runs
Signing In
Redirected to home page when the access token is received

XP

Keystore Commands

List Keystore contents:

keytool -list -keystore ~/.android/debug.keystore

Change the Keystore password:

keytool -storepasswd -keystore ~/.android/debug.keystore

Change a key’s password:

keytool -keypasswd  -alias androiddebugkey -keystore ~/.android/debug.keystore

Remove alias (key):

keytool -delete -alias androiddebugkey  -keystore ~/.android/debug.keystore
# Silent
keytool -delete -noprompt -alias ${cert.alias}  -keystore ${keystore.file} -storepass password

Create a new Keystore

keytool -genkey -v -keystore debug.keystore -alias debugkeys -keyalg RSA -keysize 2048 -validity 10000

MSAL Config Authority Types

// Example AzureAD and Personal Microsoft Account
{
    "type": "AAD",
    "audience": {
        "type": "AzureADandPersonalMicrosoftAccount"
    },
    "default": true // Indicates that this is the default to use if not provided as part of the acquireToken call
},
// Example AzureAD My Organization
{
    "type": "AAD",
    "audience": {
        "type": "AzureADMyOrg",
        "tenant_id": "contoso.com" // Provide your specific tenant ID here
    }
},
// Example AzureAD Multiple Organizations
{
    "type": "AAD",
    "audience": {
        "type": "AzureADMultipleOrgs"
    }
},
//Example PersonalMicrosoftAccount
{
    "type": "AAD",
    "audience": {
        "type": "PersonalMicrosoftAccount"
    }
}

BackBurner

App Signing and Auth Redirection Improvements

This is just a quick and dirty debug key and key store to allow the Azure AD redirects to work.

We need to enable/create keys and keystrokes for other environments and pass in the secret values from a config file as opposed to hard coding them.

And harden encryption to avoid the the SHA1withRSA vulnerabilities whilst remaining compatible with the Azure AD redirect rules.

Warning:
<androiddebugkey> uses the SHA1withRSA signature algorithm which is considered a security risk. This algorithm will be disabled in a future update.

MSAL Config values

Inject the tenant_id, client_id, redirect_url into MSAL config file(msal_default_config.json) to protect the secrets.

Packages

Links

Flutter
Mobile
Mobile App Development
Programming
API
Recommended from ReadMedium