1. Guides & Tools
  2. App switching

Native apps

If you are building a native app, and would like to get the smoothest possible UX, you can use Criipto Verify's app-switching capabilities (for e-ID's that support it).

This will require that you take on a bit more work to orchestrate the login process in comparison with just going with the browser-based flow, but the net result is a much better UX.

You should always start by detecting the presence of the native e-ID app on the users device. See below for app-detection examples.

If the desired app is present on the users device, you must augment the authorize request you send to Criipto Verify so it contains one of the following values (also OS dependent)

  • login_hint=appswitch:ios
  • login_hint=appswitch:android

The value must be sent in the login_hint query parameter. Further details on this (and other) parameters in an authorize request can be found here

Your app is responsible for sending the appropriate value for the platform it is deployed on.

App detection

The following code and configuration snippets illustrates how you can detect the presence of both the Danish MitID and the Swedish BankID apps.


public boolean deviceHasDKMitApp() {
  try {
    getPackageManager().getPackageInfo("", 0);
    return true;
  } catch (PackageManager.NameNotFoundException e) {
    return false;

public boolean deviceHasSEBankIdApp() {
  try {
    getPackageManager().getPackageInfo("com.bankid.bus", 0);
    return true;
  } catch (PackageManager.NameNotFoundException e) {
    return false;

From Android 11, your applications Manifest.xml must contain the following to be able to perform these queries:

<manifest ...>
    <package android:name="" />
    <package android:name="com.bankid.bus" />
  <application .... />


func canOpenDKMitIDApp() -> Bool {
  guard let url = URL(string: "") else {
    return false
  return UIApplication.shared.canOpenUrl(url)

func canOpenSEBankIDApp() -> Bool {
  guard let url = URL(string: "") else {
    return false
  return UIApplication.shared.canOpenUrl(url)

Swedish BankID

App switch is supported for the same-device login flow (acr_values=urn:grn:authn:se:bankid:same-device).

We recommend that you use response_mode=json or response_mode=query for this flow.

For the redirect_uri in the authorize request, you can experiment with using either a universal link for your app, or a custom file handler. Which one is more robust depends on the version of the OS you are on. Newer OS versions typically work best with universal links.

The response to the authorize request will change to 200 OK with a JSON payload of format along the lines of:

  "launchLinks": {
    "universalLink": "",
    "customFileHandlerUrl": "bankid:///?autostarttoken=...&redirect=..."
  "cancelUrl": "...",
  "completeUrl": "..."

The value of the redirect parameter depends on the supplied platform in the login_hint=appswitch:... parameter. If the app sends the wrong parameter, the flow cannot be expected to work correctly.

Your app must use one of the launchLinks values to open the native BankID app. The universalLink is recommended, but some older platforms may only support the customFileHandlerUrl syntax. We provide both so you can target the widest possible range of OS versions.

The cancelUrl can be used to to stop an active authentication request. This can be useful in case

  • You are pre-filling the SSN
  • The user does not complete the authentication in the BankID application
  • The user navigates manually back to your app When that happens, the BankID login for the specified SSN is blocked for a few minutes, unless you cancel the login.

When control returns to your app, issue an HTTP GET to the completeUrl (without any modifications). Expect an OAuth2-formatted response. Depending on the value of the response_mode you use in your authorize request, the response will be either a 200 OK, a 302 Redirect, or a 400 Bad Request.

  • 200 OK: (when sending response_mode=json) The response payload will be a JSON literal with a string-valued location property. The value will be a URL where the relevant response parameters are in the query section. You should start by checking for errors (an error query parameter will be present in the response). If not an error, pick out the code and state parameters and use your OIDC library to exhange the code to an id_token.

  • 302 Redirect: (when sending response_mode=query) The Location response header value value will be a URL where the relevant response parameters are in the query section. You should perform the same OAuth2-response processing as described for the 200 OK case.

  • 400 Bad Request: Can have several causes, we recommend that you invoke the cancelUrl and present the user with the option to login again.

Danish MitID

You must (still) use a either a Custom Tab (on Android) or an ASWebAuthenticationSession (on iOS) to run the login flow in your app, but an app-switch button will show up in the MitID Core Client, allowing the user to launch the MitID app from the browser.

On iOS, you can also use SFAuthenticationSession if the ASWebAuthenticationSession is unavailable for the version you are targeting. If you must target very old OS versions, SFSafariViewController can be used as a fallback, but some features might not work properly.

Once the flow completes, the MitID app will perform an app-switch back to your app, which makes the Custom Tab / ASWebAuthenticationSession resume its operation, thus completing the login process by issuing an OAuth2 formatted response to your native app.

Android notes

It is not entirely intuitive how the switchback / resume flow can be handled on this OS. The following setup has been observed to work:

  1. Set a dedicated activity for the e-ID login UI flow, and set the following properties in your applications Manifest.xml.

Remember to replace the values of the and android.pathPattern properties in the data node. These values must correspond to the App Link for your app. You can read more about how App Links work here

           <intent-filter android:autoVerify="true">
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                        android:scheme="https" />

After the app links are configured properly, register the app link as a Callback URL in Criipto, and set it as the redirect_uri query parameter in the authorize request towards Criipto Verify. You can read more about the anatomy of the authorize request in our authorize URL builder.

Show the custom tab in the mitIDLoginActivity, and the user will be taken to MitID app during the login process. After Login, the context switches back to your app automatically because of the app link used in the redirect_uri. When the custom tab tries to load your app link, you can access the returned code value in an override of the onNewIntent function of the mitIDLoginActivity:

override fun onNewIntent(intent: Intent?) {
    val data = intent?.data
    if (data != null && data.isHierarchical) {
        if (data.getQueryParameter("error") != null) {
            val error = data.getQueryParameter("error")
            if (data.getQueryParameter("error_description") != null) {
              val error_description = data.getQueryParameter("error_description")
            // Handle error in a way that matches your UX requirements
        if (data.getQueryParameter("code") != null) {
            val code = data.getQueryParameter("code")
            // Exchange the `code` to a token using the standard OAuth2/OIDC protocols.
            // Exactly how to do this depends on your choice of flow.
            // If you are using the PKCE flow, you can run the exchange directly from your app.
            // If you are using the traditional authorization code flow, send the code to your server backend so it can run the exchange.

Web apps

Danish MitID

The MitID login UX to web apps can also benefit from app-switching. When using this flow, a "Start the MitID app" button will be presented to the user after they have entered their username, just as in the case for native app targets. Clicking this button will launch the MitID app - so the user does not have to find it and open it manually.

You can activate this behavior by adding a target:web token in the login_hint query parameter.

You must also add the platform-specific appswitch:... token as described above for native apps.

Your web application is responsible for detecting if the user is browsing your site on a mobile device. You will need that information to determine whether to send appswitch:ios or appswitch:android.

In contrast to native apps, there is no way that your web app can determine if the native MitID app is installed on the device. Only the end user knows this - so you need to let them choose between the 2 flow options in your web app.

Working with the test-version(s) of the MitID app

The test version(s) can be found here: Note that on Android, the test version cannot co-exist with the production version, so you need a dedicated Android device for testing. On iOS, the 2 app versions can co-exist, but doing so can lead to surprising behaviour at runtime. The app-switching is (sometimes) done to the "latest active" version of the app, so you might end up in your production app while testing. We recommend that you always close the production version of the MitID app before running any tests.

Android caveat

The login flow pauses in the MitID app after the user has authenticated, and the user must manually switch back to the browser. The login process will automatically complete after the manual switch-back. This is the same behavior that end-users are familiar with from NemID app-based logins.