This article shows how to use the open-source Flutter passkeys package to add passkeys to Flutter apps, either with your own or a hosted passkey backend.
Vincent
Created: July 26, 2023
Updated: October 1, 2024
We aim to make the Internet a safer place using passkeys. That's why we want to support developers with tutorials on how to implement passkeys.
Hello Flutter developers,
With this tutorial, we want to introduce you to our new open-source Flutter passkeys package. The Flutter passkeys package offers passkeys / WebAuthn implementations for Android, iOS and web (more platforms to come; this post focuses on Android and iOS) with emphasis on maximum flexibility and ease of use for developers. We want to help make the internet a safer place and are happy if you help by starting to ditch passwords and join the passkey- era.
Recent Articles
⚙️
Passkey Tutorial: How to Implement Passkeys in Web Apps
⚙️
Supabase Passkeys: Add Passkeys for Your Supabase Users
⚙️
Vue.js Passkeys: How to Implement Passkeys in Vue.js Apps
⚙️
Flask Passkeys: How to Implement Passkeys with Python Flask
📖
WebAuthn Conditional UI (Passkeys Autofill) Technical Explanation
Regarding passkeys, currently available Flutter packages are either complicated to set up or require an external passkey backend, that you have to set up and maintain on your own. This can be quite challenging and tedious (especially when you want to quickly prototype with passkeys).
Therefore, the main goals of the Flutter passkeys package are:
Note: For realizing these main goals, no Corbado services are needed. You can simply use the passkeys package together with your own passkey backend.
This tutorial is structured as follows:
1.2 Flutter Passkeys Package Architecture
2.2 Perform Passkey Authentication
4. (Optional) Configure Your Relying Party Server
5.1 Bind Passkeys to Specific Domain
5.2 Integrate Your Own Passkeys Backend
6.1 Weak SHA-1 Algorithm for Android Keystore
6.2 Android Screen Lock Not Set Up On Virtual Android Device
7.1 Use case: Prototype with Passkeys
7.2 Use case: Use Passkeys for Your App with Existing Relying Party Server
7.3 Use case: Use Passkeys for Your App But You Don't Want to Build Your Own Relying Party Server
7.4 Use case: Build an App that Supports More Advanced Use Cases with Passkeys
The code of this tutorial is taken from the Flutter passkeys package example, which you can find on GitHub and on pub.dev.
Heres a preview of the running example app in Android:
To better understand the architecture of the Flutter passkeys package, we first look at passkeys and how they work.
In a traditional authentication system (e.g., a password-based one), a developer sets up a backend including a database where user credentials (e.g., email + passwords) will be stored after the sign up. The password must only be known by the user and developers must take precautions to protect these passwords in the backend (e.g. hashing, salting).
Now, lets come to passkeys. For a passkey authentication system, there is also a sign up and a login flow. Moreover, there is also a backend involved which is called relying party server. During the sign up flow, the user creates a public / private key pair (by using e.g. Face ID or Touch ID). The public key is shared with the relying party server and is permanently stored there. The private key is kept securely on the users device.
During every login, the relying party server is asked for a cryptographic challenge by the client (the native iOS / Android app or the web browser). The challenge can only be solved with the private key. Access to that private key is protected with Face ID or Touch ID. As soon as the user proves his identity using his biometrics, the device can sign the challenge and sends it back to the relying party server.
Ben Gould
Head of Engineering
I’ve built hundreds of integrations in my time, including quite a few with identity providers and I’ve never been so impressed with a developer experience as I have been with Corbado.
10,000+ devs trust Corbado & make the Internet safer with passkeys. Got questions? We’ve written 150+ blog posts on passkeys.
Join Passkeys CommunityNext, well explain some key concepts of the Flutter passkeys package (non-exhaustive architecture). In a nutshell, the package supports by abstracting the OS-specific authenticator implementations. The authenticator provides OS functionalities that can create a passkey and sign passkey challenges by asking the user for their biometrics. It's written in OS-specific languages (e.g. Swift, Kotlin, JavaScript).
The major benefit of this architecture is that the Flutter passkeys package abstracts away the complexities of handling OS -specific implementations of the authenticator. This simplification greatly eases the development process, offering developers straightforward usability while retaining the flexibility to select their own relying party server and the corresponding adapter in Flutter.
The relying party server operates on a remote server, with the Flutter application acting as a client. Integration is facilitated through a Flutter adapter tailored to the specific relying party server, allowing for the use of a customized backend. For added convenience, Corbado provides both an adapter (see the Flutter corbado_auth package) and a hosted relying party server that are optimized for the Flutter passkeys package, enabling rapid testing. Nevertheless, you have the option to employ your own relying party server if preferred.
A more detailed view at the architecture:
1. Native plugins
1.1. passkeys_android: The native implementation for Android that is responsible for receiving the challenge, verifying the user and sending a response to the challenge back afterwards. All user interaction on Android is handled by the passkeys_android plugin.
1.2. passkeys_ios: The native implementation for iOS that is
responsible for receiving the challenge, verifying the user and sending a
response to the challenge back afterwards. All user interaction on iOS is
handled by the passkeys_ios plugin.
1.3. passkeys_web: The implementation for web apps that is
responsible for receiving the challenge, verifying the user and sending a
response to the challenge back afterwards. All user interaction on the web is
handled by the passkeys_web plugin.
2. Flutter (Dart)
2.1. Passkey authenticator: The authenticator is the core of the passkeys package , handles the platform dependent parts and coordinates the native plugins (1.1, 1.2 & 1.3). On the other side, it communicates with the Relying party server adapter (2.2).
2.2. Relying party server adapter: The Relying party server adapter provides a specific implementation that is needed to relay the communication between the Passkey authenticator (2.1) and the Relying party server (3). It is written inside your Flutter app.
3. Relying party server: The Relying party server is responsible for managing passkeys. It generates the challenge, verifies the validity of the frontends response, and manages the users and passkeys.
As depicted in the chart, its possible to add more native plugins. Moreover, you can use any specific implementation and relying party server. For reference, in the example folder, we provide a specific implementation for Corbado and use Corbado as relying party server.
The great benefit of this modular architecture is that it allows to directly use passkeys out-of-the-box (e.g. with Corbado as passkey backend provider), while also giving the flexibility to integrate your own passkey backend instead.
Become part of our Passkeys Community for updates and support.
JoinFor this tutorial, we just make use of the example project in /examples , that you can clone to your machine with the following command.
Go to the example directory:
In the following, we explain selected important parts of the code.
For simplicity, a single stateful widget is created which represents a login, signup and logged-in page dependent on the state of the application.
Upon initialization a shared relying party server by Corbado is started and used in the following sections. In step 5.2, we will show how to replace the current implementation with your own passkey infrastructure.
That's it concerning the UI of the Flutter application.
When the user clicks on the login button, the login is performed, and the widget is updated once the result is available:
When the user clicks on the sign up button, the sign up is performed, and the widget is updated once the result is available:
When you cloned the GitHub repo, make sure that you are now in the root directory to run the example app. Also, make sure that you have a running virtual device or a physical device connected to your machine.
Now, you are fully set and you can start signing up with your first passkey in the example by running the following command:
Note that you share a relying party server with other Flutter developers for this example. Its user table is flushed every day. We have built the example this way to make it very simple to set up. For an example, this works totally fine, but if you want to build your own app, you need your own relying party server. Moreover, you cannot run the example on physical iOS devices but only on a simulator (for Android, physical devices + emulators work).
Note: If you just want to quickly try out passkeys in your Flutter app, you don't need to modify anything, because the previous example comes with a pre- configured relying party server that can serve passkeys without any additional setup.
The following parts only describe the settings you need to make if you have your own relying party server up and running and want to let your Flutter apps (with the passkeys package) communicate with your relying party server. Setting up your own relying party server from scratch is something that goes beyond the scope of this tutorial. There are several other resources that you can use. Moreover, we plan to publish a separate blog post for setting up a relying party server.
To make your relying party server ready for communication with your Flutter app, you need to obtain some platform-specific information that you need later and also need to modify some platform-specific settings.
You will need your package name (e.g. com.passkeys.example) and your SHA-256 fingerprint (e.g.54:4C:94:2C:E9:...).
You can obtain the SHA-256 fingerprint of your debug signing certificate by executing the following command:
macOS / Linux:
Windows:
This information is needed for step 5.1
If you encounter any issues, please have a look at Troubleshooting.
Subscribe to our Passkeys Substack for the latest news, insights and strategies.
SubscribeYou need to establish trust between your iOS app and the relying party server. Your app will be identified through your Application Identifier Prefix (e.g. 9RF9KY77B2) and your Bundle Identifier(e.g. com.passkeys.example). You need an Apple developer account to set up both. If you haven't got one yet, set up a new account.
Note: When creating your Bundle Identifier, make sure that the "Associated Domains " capability is enabled.
The Application Identifier Prefix can be obtained by going to your Apple Developer Certificates, Identifier & Profiles associated with your Apple Developer account.
Open your app's code in Xcode now. In "Runner -> Signing & Capabilities" enter your Application Identifier Prefix and your Bundle Identifier.
To set up your iOS app, you will need your Application Identifier Prefix and your Bundle Identifier that we set up in step 4.2.1.
Afterwards, you need to host an apple-app-site-association file on your
relying party server on the given relying party ID https://{your-relying-party-ID}/.well-known/apple-app-site-association
. This file will be
downloaded by iOS when you install your app. To tell iOS where to look for the
file, we need the next step in our setup.
In your Xcode workspace, you need to configure the following settings: In
"Signing & Capabilities" tab, add the "Associated Domains " capability and
add the following domain: webcredentials:{your-relying-party-ID}
. Now, iOS
knows where to download the apple-app-site-association file from.
If you forget about this step, the example will show you an error message like Your app is not associated with your relying party server. You have to add....
Your configuration inside Xcode should look similar to what is shown in the screenshot below (you will have a different Bundle Identifier and Associated Domain).
Note, it's important to add the domain without the protocol (https://) and without a path.
Next, we will explore the options for customizing your passkey experience and show you how to switch to your own passkey infrastructure provider.
Each passkey is bound to a certain domain, the so-called relying party ID. If you use the example, passkeys are bound to a shared Corbado subdomain at https://pro-6244024196016258271.frontendapi.corbado.io.
Of course, you can also use your own domain for passkey binding. To do so, you need to verify that both the native app and domain belong to you. Therefore, you need to host the configuration file on your domain:
To create these files on your own, you can copy the file structure from the shared Corbado subomain above. Then, you need to update the respective relying party ID / specific domain in the file.
In the case of Android, for the assetlinks.json file, you can also use Android Studios Digital Asset Links File Generator to generate the file. You find it under Tools > App Links Assistant > Open Digital Asset Links File Generator in Android Studio.
If you want to build an application that is entirely hosted and maintained by you, you can do so by bringing your own passkey backend infrastructure, while still using the Flutter passkeys package for easy integration.
Therefore, you need to create a specific implementation in Flutter for the Relying party server adapter (2.2). We recommend to take a look at the sample implementation of the corbado_auth adapter (custom_corbado_auth.dart) and provide something similar for your own backend.
Originally, we provided a general interface, but due to the heterogeneous nature of Relying party servers and the varied preferences of developers, we decided to discontinue it. Additionally, the maintenance and support for different Relying party servers became too cumbersome and ultimately unsustainable.
If youve executed one of the commands in step 2.1.1 to view your SHA-256 fingerprint and you see an error message like the following SHA1withRSA (weak):
Then, you need to create a new Android keystore with a stronger algorithm. You can do so with one of the following commands:
If you run the application in an emulator and it says that you can't create a passkey, you have to log into your Google account and properly set up a screen lock or biometrics on the device.
First, to log into your Google account, open settings, click on the icon in the top right and then on "Sign in to your Google Account".
Secondly, to set up the screen lock, open the settings, search for security settings and add a PIN as well as a fingerprint as shown below (PIN is required for fingerprint):
So far, we've seen how to set up passkey authentication in a Flutter app with the passkeys package.
We now want to quickly go through the most common use cases for Flutter developers when using passkeys. We will try to show how this package can help with each use case. While use case 1 is a very basic one (exploration only), use case 2 and use case 3 are show cases in which real apps are built. Use case 4 and use case 5 are more advanced solutions that try to help you with building more complicated apps.
You just want to see passkeys in action in a Flutter app? Then the example of this package is the right point for you to start. There is no configuration required and you can go through sign up and login flows on your emulator.
Note:
If you already have a relying party server, you just need to tell this package how to interact with it. This is done by implementing a Relying party server adapter (2.2). The code below shows a sample implementation of the adapter for corbado_auth that you can use as a reference:
To use passkeys in your app, you need a relying party server. If you don't want to build one, you can use already existing solutions. To allow this package to integrate with a relying party server, the Relying party server adapter must be implemented for that particular relying party server.
Corbado lets you set up a relying party server. To save time and effort, you can use Corbado as a relying party server. Find an example how to do this including a step-by-step guide here.
You can use every other SaaS provider that allows you to set up a relying party server though. All you need to do is implement the Relying party server adapter.
While this package allows you to use passkeys for authentication, a few challenges remain unsolved, e.g.:
Solving these challenges goes beyond the scope of this package.
As a solution, you can create your own authentication SDK that builds on top of the passkeys package. This can make sense if you want to build your own authentication backend.
Alternatively, you can save time and use our solution for that problem: corbado_auth. This is a separate flutter package that builds on top of the passkeys package to provide solutions for the above challenges. For more information, check out its documentation and examples.
Firebase is a great platform that helps you with building your app. We offer a "Passkeys for Firebase" extension that you can install in the Firebase console to use the passkeys with Firebase Auth.
Add passkeys to your Flutter app.
Start For FreeIn this tutorial, we showed how to add passkey authentication to a Flutter app using the passkeys package. The package was designed with flexibility in mind, so you can get started fast with Corbado, but also have the option to host everything yourself and only make use of the simple passkey integration.
While the Flutter passkeys package will always remain a clean modular package just for passkey signup and login, the corbado_auth package will add features that can help during the development of more complex apps. Examples of these feature are:
In an upcoming tutorial, well explain in detail how to set up these features with the corbado_auth package in your Flutter app.
Regarding the passkeys package itself, it's planned to
Table of Contents
Enjoyed this read?
🤝 Join our Passkeys Community
Share passkeys implementation tips and get support to free the world from passwords.
🚀 Subscribe to Substack
Get the latest news, strategies, and insights about passkeys sent straight to your inbox.
We provide UI components, SDKs and guides to help you add passkeys to your app in <1 hour
Start for free
Recent Articles
Supabase Passkeys: Add Passkeys for Your Supabase Users
Nicolai - July 13, 2023
Tutorial: How to Get Full Flutter Passkey Auth SDK in <1h
Lukas - September 5, 2023