-
Notifications
You must be signed in to change notification settings - Fork 366
Testing an app using MSAL
Starting with MSAL 3.x, the API uses the builder pattern heavily. Builders are difficult / tedious to mock. Instead, we recommend that you wrap all your authentication logic behind an interface and mock that in your app.
For end to end testing, you can setup test accounts, test applications or even separate directories. Username and passwords can be deployed via the Continuous Integration pipeline (e.g. secret build variables in Azure DevOps). Another strategy is to keep test credentials in KeyVault and configure the machine that runs the tests to access KeyVault, for example by installing a certificate. Feel free to use MSAL's strategy for accessing KeyVault.
Note that once token acquisition occurs, both an Access Token and a Refresh Token are cached. The first has a lifetime of 1h, the latter of several months. When the Access Token expires, MSAL will automatically use the Refresh Token to acquire a new one, without user interaction. You can rely on this behaviour to provision your tests.
If you have Conditional Access configured, automating around it will be difficult. It will be easier to have a manual step that deals with Conditional Access (e.g. MFA), which will add tokens to the MSAL cache and then rely on silent token acquisitions, i.e. rely on a pre-logged in user.
Strategy 1: Use Selenium or an equivalent technology to automate your web app. Fetch usernames and password from KeyVault.
Pros: end to end testing with real tokens
Cons: UI automation is flaky. It's tedious to automate the login screens. Live accounts and "Work and School" have slightly different UI flows.
Strategy 2: Use ROPC (Username/Password flow) to get tokens and test only your controllers
Pros: no ui automation
Cons: Does not work for Live accounts, where ROPC is not supported.
Strategy 3: Login manually to prepopulate the token cache. Call AcquireTokenSilent
to get a fresh access token based on the refresh token silently. Refresh tokens are valid for 90 days, but they are also refreshed.
Pros: no ui automation; works for both "Live" and "Work and School" accounts;
Cons: some Conditional Access policies will not work cross machine; some manual setup at first;
Sample showcasing token cache sharing between apps: https://github.com/Azure-Samples/ms-identity-dotnet-advanced-token-cache
Daemon apps use pre-deployed secrets (passwords or certificates) to talk to AAD. You can deploy a secret to your test environment or use the token caching technique to provision your tests. Note that the Client Credential Grant, used by daemon apps, does NOT fetch refresh tokens, just access tokens, which expire in 1h.
For native clients, there are several approaches to testing:
-
Use the Username / Password grant to fetch a token in a non-interactive way. This flow is not recommended in production, but it is reasonable to use it for testing.
-
Use a framework, like Appium or Xamarin.Test, that provides an automation interface for both your app and the MSAL created browser.
-
MSAL exposes an extensibility point that allows developers to inject their own browser experience. The MSAL team uses this internally to test interactive auth scenarios. Have a look at this test project to see how to inject a Selenium powered browser that can handle authentication.
The MSAL team are currently running tests on a Xamarin app that uses MSAL.net; We are using App Center to manage devices, test runs etc. The test framework is Xamarin.UITest. A limitation that we have found is that we are unable to test system browsers, only embedded browsers.
When evaluating a test framework, it is worth also having a look at the Appium and other test frameworks, as well as other CI / CD providers in the mobile space.
No matter what platform you use, MSAL stores tokens in a token cache. On some platforms, you tell MSAL how to serialize this cache. On others - the mobile platforms - MSAL does it for you. From an application perspective, the token cache is responsible for 3 things:
- storing tokens in the cache after they were acquired (e.g. via AcquireTokenInteractive)
- fetching tokens from the cache when doing AcquireTokenSilent()
- fetching account metadata from the cache when doing GetAccount()
So if you want to test cache scenarios, consider writing a scenario that would:
- acquire one or more tokens (e.g. using ROPC / Username-password flow, which is the simplest for testing)
- verify that GetAccounts works
- verify that AcquireTokenSilent works
You might want to also test that:
- restarting the app does not blow away the cache
- AcquireTokenSilent does not refresh the RT (i.e. network call to AAD), but serves the AT if it has not expired. You can achieve this and other HTTP related scenarios by taking control of the HttpClient via IHttpClientFactory, see:
Please log issues or ask questions related to testing. Providing a good test experience is one of the goals of the team.
- Home
- Why use MSAL.NET
- Is MSAL.NET right for me
- Scenarios
- Register your app with AAD
- Client applications
- Acquiring tokens
- MSAL samples
- Known Issues
- Acquiring a token for the app
- Acquiring a token on behalf of a user in Web APIs
- Acquiring a token by authorization code in Web Apps
- AcquireTokenInteractive
- WAM - the Windows broker
- .NET Core
- Maui Docs
- Custom Browser
- Applying an AAD B2C policy
- Integrated Windows Authentication for domain or AAD joined machines
- Username / Password
- Device Code Flow for devices without a Web browser
- ADFS support
- High Availability
- Regional
- Token cache serialization
- Logging
- Exceptions in MSAL
- Provide your own Httpclient and proxy
- Extensibility Points
- Clearing the cache
- Client Credentials Multi-Tenant guidance
- Performance perspectives
- Differences between ADAL.NET and MSAL.NET Apps
- PowerShell support
- Testing apps that use MSAL
- Experimental Features
- Proof of Possession (PoP) tokens
- Using in Azure functions
- Extract info from WWW-Authenticate headers
- SPA Authorization Code