Guides & Tools
How to use Criipto Verify in app-switch mode
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 eID'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 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.
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("dk.mitid.app.android", 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 ...>
<queries>
<package android:name="dk.mitid.app.android" />
<package android:name="com.bankid.bus" />
</queries>
<application .... />
</manifest>
func canOpenDKMitIDApp() -> Bool {
guard let url = URL(string: "https://appswitch.mitid.nets.eu/.well-known/apple-app-site-association") else {
return false
}
return UIApplication.shared.canOpenUrl(url)
}
func canOpenSEBankIDApp() -> Bool {
guard let url = URL(string: "https://app.bankid.com/.well-known/apple-app-site-association") else {
return false
}
return UIApplication.shared.canOpenUrl(url)
}
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": "https://app.bankid.com/?autostarttoken=...&redirect=...",
"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
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.
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.
It is not entirely intuitive how the switchback / resume flow can be handled on this OS. The following setup has been observed to work:
Manifest.xml
.Remember to replace the values of the android.host
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
<activity
android:name=".activities.mitIDLoginActivity"
android:launchMode="singleTop">
<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" />
<data
android:host="yourwebdomain.com"
android:pathPattern="/yourRedirectUrlPath"
android:scheme="https" />
</intent-filter>
</activity>
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?) {
super.onNewIntent(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.
}
}
}
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.
The test version(s) can be found here: https://pp.mitid.dk/mitid-app/index.html. 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.
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.