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 eIDs 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 eID 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

From June 6, 2023 (MitID Release 11) and onwards, app-detection for MitID is no longer relevant. After that date, end users will be presented with "Open MitID app" buttons in the MitID Core Client, regardless of any effort by the SP's app to detect the presence of the MitID app. We have therefore removed the previous guidance for MitID app detection from our documentation.

The following code and configuration snippets illustrate how you can detect the presence of the Swedish BankID app.


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 this query:

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


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 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, this initial resuming of your app will not include any OAuth2 parameters, however in resuming your app the MitID login request will continue in the web component, completing the login process which will issue an OAuth2 formatted response.

Assuming your redirect_uri is also pointing to your native app, you will need to handle two sets of deep links, one initial from MitID with no parameters, and a second one from Criipto with parameters.

You can define a seperate URL for the MitID resume operation by using a appswitch:resumeUrl:{UNIVERSAL LINK} login_hint. MitID will only resume to a universal link, but if you define a separate resumeUrl your redirect_uri can be a custom scheme.

The resumeUrl login_hint must be combined with your other appswitch login_hints, for example appswitch:android%20appswitch:resumeUrl:{UNIVERSAL LINK}

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 eID 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 also benefits from app-switching, though only in the switch from the browser to the MitID app. The user must still manually switch back to the browser once they have approved the login in their MitID app.

This has changed on June 6, 2023 (with MitID Release 11). Previously, automated switchback to the browser could be achieved on iOS, but that is no longer the case.

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.