Skip to content

Commit 81d96be

Browse files
committed
Merge branch 'main' of github.com-corbado:corbado/example-passkeys-flutter
2 parents a3b49c4 + 43d2ef6 commit 81d96be

9 files changed

+272
-49
lines changed

README.md

Lines changed: 224 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,227 @@
1-
# Corbado Auth for Flutter: Example (custom backend)
1+
# Flutter Passkeys Example Application
2+
3+
This is a sample implementation of the [Corbado passkeys-first authentication solution](https://www.corbado.com) using Flutter. The following package is being used:
4+
5+
- [Corbado Auth Flutter](https://github.com/corbado/flutter-passkeys/tree/main/packages/corbado_auth)
6+
7+
[![integration-guides](https://github.com/user-attachments/assets/7859201b-a345-4b68-b336-6e2edcc6577b)](https://app.corbado.com/integration-guides/flutter)
28

39
## Getting Started
4-
This example demonstrates how you can use the Corbado Auth package to setup Passkey based
10+
11+
This example demonstrates how you can use the Corbado Auth package to setup Passkey based
512
authentication in a matter of minutes.
13+
14+
## How to run the example
15+
16+
To get started with the `corbado_auth` it's nice to see a running version of an example application
17+
that uses this package.
18+
You can start the example like this:
19+
20+
1. Run `flutter run lib/main.dart` to start the example (if you want to run on Android or iOS, start
21+
a Simulator/Emulator beforehand)
22+
23+
## How to integrate the package into your own app
24+
25+
To use `corbado_auth` in your own app, you need to create a free project at
26+
the [Corbado developer panel](https://app.corbado.com).
27+
28+
Integrating the package is not just about running `flutter pub add corbado_auth`.
29+
You need to configure and customize the package, which requires about one hour.
30+
31+
For that we have provided an integration guide that is
32+
available [here](https://app.corbado.com/integration-guides/flutter).
33+
34+
**Note:** Please make sure to also add your project frontend api link to the relying party Id,
35+
Navigate to your project in the developer panel then open Settings > General > URLs and input your
36+
link.
37+
It should be something like `pro-{projectID}.frontendapi.cloud.corbado.io`
38+
39+
## A closer look at the example code
40+
41+
In addition to going through the integration guide it can be helpful to see the `corbado_auth`
42+
package in an example application.
43+
For that, let's take a closer look at this example.
44+
45+
The example is a typical flutter application that makes use of the following libraries:
46+
47+
- go_router (routing)
48+
- riverpod (data binding and state management)
49+
- corbado_auth (authentication)
50+
51+
It consists of 3 pages:
52+
53+
- loading (shown while the app is loading initially)
54+
- authentication (shown when a user signs up or logs in)
55+
- profile (shown for users after signup/log in)
56+
57+
To understand how the `corbado_auth` package is integrated into this example application,
58+
we have to take a look at these files/directories:
59+
60+
- **router.dart**: Here we handle routing that is based on the authentication state (e.g. a user
61+
that is logged in should not see the authentication screen => that user must be navigated to the
62+
profile screen).
63+
- **auth_provider.dart**: Here we set up the riverpod providers and thus make authentication state (
64+
e.g. the user object) and functionality (e.g. the logout function) available throughout the app.
65+
- **pages/auth_page.dart**: Here we define the page that loads the Corbado auth screens. We
66+
configure it with a set of custom Flutter components (see the next bullet point).
67+
- **screens/\*.dart**: Here we define a set of custom Flutter components. Each of them must
68+
implement `CorbadoScreen<T>` where `T` is one of 5 currently supported Corbado blocks. Find
69+
details about this in the `Corbado blocks` section.
70+
71+
### A quick note about Flutter web
72+
73+
Flutter web relies on some JavaScript code that we provide
74+
on [Github](https://github.com/corbado/example-passkeys-flutter/releases/download/2.4.0/bundle.js).
75+
If you want to use `corbado_auth` in a Flutter web application you have to include this JavaScript
76+
bundle as part of your index.html.
77+
You can do this by adding the following `<script>` tag to your `<head>` section.
78+
79+
```
80+
<script src="https://github.com/corbado/example-passkeys-flutter/releases/download/2.4.0/bundle.js" type="application/javascript"></script>
81+
```
82+
83+
For an example how the `index.html` can look check
84+
out [index.html](https://github.com/corbado/example-passkeys-flutter/blob/main/web/index.html).
85+
86+
## Corbado blocks
87+
88+
If you introduce passkeys into your application you will have to define a number of screens in your
89+
flutter app.
90+
A user wants to signup and log in with a passkey. We also have to provide fallbacks for situations
91+
where a user can not use a passkey.
92+
Finally we want to ask users that don't have a passkey yet if they want to create one.
93+
94+
Triggering these screens in the right moments requires quite a bit of logic.
95+
At Corbado we provide this as a service for developers and we make it configurable in our developer
96+
panel.
97+
As a developer you have the freedom of defining your own UI implementation for these screens.
98+
99+
To give you guidance about what data and what functionalities are available for a screen we
100+
introduced the concept of **Corbado blocks**.
101+
One Corbado block (e.g. `SignupInitBlock`) defines the data and functionalities that you can use on
102+
one of these screens.
103+
Adding `corbado_auth` to your app means defining these screens.
104+
105+
### Overview of existing Corbado blocks
106+
107+
Currently, there are 5 Corbado blocks available in the `corbado_auth` package.
108+
Over time we will add additional blocks that you can implement (they are optional).
109+
These 5 blocks are needed by most developers.
110+
111+
#### SignupInitBlock
112+
113+
This block is used to initiate a sign up.
114+
The goal of this block is to ask the user for a unique identifier (currently only email is
115+
supported)
116+
and an optional nice name (we call this a fullname at Corbado).
117+
The corresponding screen will thus be shown when a user starts a new signup in your app.
118+
119+
If the identifier is not available or in the wrong format the block will return an error.
120+
121+
When you implement the screen for this block, you usually want to define one or multiple input
122+
fields
123+
and two buttons ("submit" and "switch to login").
124+
You should also show the errors (ideally close to the input fields).
125+
126+
Check
127+
out [signup_init.dart](https://github.com/corbado/example-passkeys-flutter/blob/main/lib/screens/signup_init.dart)
128+
to see an example implementation for a screen that uses this block.
129+
130+
#### PasskeyAppendBlock
131+
132+
This block is used to create a new passkey.
133+
The corresponding screen will thus be shown during a signup (after the user has provided the
134+
identifier)
135+
or potentially at the end of a login (if no passkey is existing for the user).
136+
137+
If setting up the passkey fails (e.g. because the user has cancelled the operation)
138+
the block allows either a retry or switching to a fallback authentication method (currently
139+
email-otp is supported).
140+
141+
When you implement the screen for this block, you usually want to define two buttons.
142+
143+
Check
144+
out [passkey_append.dart](https://github.com/corbado/example-passkeys-flutter/blob/main/lib/screens/passkey_append.dart)
145+
to see an example implementation for a screen that uses this block.
146+
147+
#### EmailVerifyBlock
148+
149+
This block is used to verify that a user has access to an email address.
150+
The corresponding screen will thus be shown during a signup (e.g. if passkeys are not supported or
151+
if you have configured your Corbado project to verify each email address during signup)
152+
or during a login (if no passkey has been used).
153+
154+
When the screen for this block is rendered, Corbado has already sent out an email to the user
155+
containing a 6-digit code.
156+
The user should provide this code through the screen.
157+
If the code is wrong the block will indicate this through an error.
158+
It then allows to enter the code again or to resend the email (after 30s have passed).
159+
160+
When you implement the screen for this block, you usually want to define one or two buttons (submit
161+
and resend).
162+
Also you need an input field (for the 6-digit code).
163+
164+
Check
165+
out [email_verify_otp.dart](https://github.com/corbado/example-passkeys-flutter/blob/main/lib/screens/email_verify_otp.dart)
166+
to see an example implementation for a screen that uses this block.
167+
168+
#### LoginInitBlock
169+
170+
This block is used to initiate a login process.
171+
The goal of this block is to ask the user for her unique identifier (the email address).
172+
The corresponding screen will thus be shown at the beginning of a login.
173+
174+
When the screen for this block is rendered, conditional UI will be started.
175+
If the user has a passkey available he can log in without typing his identifier.
176+
It that's not the case he has to provide the email address to the block through an input field.
177+
178+
When you implement the screen for this block, you usually want to define one input field (for the
179+
email address).
180+
Also usually you need two buttons ("submit" and "switch to signup").
181+
182+
Check
183+
out [login_init.dart](https://github.com/corbado/example-passkeys-flutter/blob/main/lib/screens/login_init.dart)
184+
to see an example implementation for a screen that uses this block.
185+
186+
#### PasskeyVerifyBlock
187+
188+
This block is used to verify that a user has access to a passkey.
189+
The corresponding screen will thus be shown during a login process.
190+
191+
When the screen for this block is rendered, a passkey authentication is started.
192+
If the user completes it he will move on to the next block (most of the time he directly is logged
193+
in).
194+
If something goes wrong the user can either retry the passkey operation or use a fallback method.
195+
196+
When you implement the screen for this block, you usually want to define two buttons
197+
(one to retry the passkey operation and another one to initiate the fallback).
198+
199+
Check
200+
out [passkey_verify.dart](https://github.com/corbado/example-passkeys-flutter/blob/main/lib/screens/passkey_verify.dart)
201+
to see an example implementation for a screen that uses this block.
202+
203+
### Deep dive: why do we use the concept of Corbado blocks?
204+
205+
We understand that this concept of `blocks` and `screens` might be challenging at the beginning.
206+
From personal experience, integrating authentication (no matter what solution you use) is always a
207+
bit challenging at first.
208+
While passkeys are a great feature for end-users for us developers they tend to make life harder
209+
(at least at the beginning).
210+
211+
With `corbado_auth` we want to reduce complexity as much as possible for you.
212+
We do this on the one hand by implementing parts of the system for you (e.g. the relying party
213+
server and session management)
214+
On the other hand we try to give clear guidance what authentication related functionalities you can
215+
use on which screen.
216+
This means that on each screen just by looking at the corresponding block you quickly get an idea
217+
how you should build the screen.
218+
You still have full control over the UI though.
219+
Most importantly we also render these screens in the right order for you so you don't need to bother
220+
with complex routing during authentication.
221+
222+
## Troubleshooting
223+
224+
| Type | Issue | Note |
225+
| ------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
226+
| iOS Sim | Localized string not found | For passkeys to work in the iOS simulator, you need to enable faceID first in the top menu bar under features > faceID > enrolled |
227+
| iOS physical device | Passkeys not working | Passkeys do not work when running your flutter application on your physical iOS device. This has to do with the signing of your app. Rather test on a virtual iOS device or virtual/ physical Android device. |

ios/Runner/Runner.entitlements

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
<key>com.apple.developer.associated-domains</key>
66
<array>
77
<string>webcredentials:pro-4268394291597054564.frontendapi.cloud.corbado.io?mode=developer</string>
8-
<string>webcredentials:pro-4268394291597054564.frontendapi.cloud.corbado.io</string>
98
</array>
109
</dict>
1110
</plist>

lib/config.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import 'package:flutter/foundation.dart';
2+
3+
// if you want to override the used projectID change this
4+
const PROJECT_ID = "<Your Project ID>";
5+
6+
String getProjectID() {
7+
if (PROJECT_ID.isNotEmpty && PROJECT_ID.startsWith("pro-")) {
8+
return PROJECT_ID;
9+
}
10+
11+
// These are default values
12+
if (kIsWeb) {
13+
return 'pro-8751299119685489253';
14+
} else {
15+
return 'pro-4268394291597054564';
16+
}
17+
}

lib/main.dart

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,11 @@ import 'package:corbado_auth/corbado_auth.dart';
22
import 'package:corbado_auth_example/auth_provider.dart';
33
import 'package:corbado_auth_example/pages/loading_page.dart';
44
import 'package:corbado_auth_example/router.dart';
5-
import 'package:flutter/foundation.dart';
65
import 'package:flutter/material.dart';
76
import 'package:flutter_riverpod/flutter_riverpod.dart';
87
import 'package:overlay_support/overlay_support.dart';
98

10-
// In your own project you should get this from an ENV variable or from a Flutter flavour.
11-
String calculateProjectID() {
12-
if (kIsWeb) {
13-
return 'pro-8751299119685489253';
14-
} else {
15-
return 'pro-4268394291597054564';
16-
}
17-
}
18-
19-
const String DEFAULT_VALUE = 'none';
20-
21-
const String envProjectId = String.fromEnvironment(
22-
'CORBADO_PROJECT_ID',
23-
defaultValue: DEFAULT_VALUE,
24-
);
9+
import 'config.dart';
2510

2611
void main() async {
2712
WidgetsFlutterBinding.ensureInitialized();
@@ -33,8 +18,7 @@ void main() async {
3318
runApp(const LoadingPage());
3419

3520
// Now we do the initialization.
36-
final projectId =
37-
envProjectId == 'none' ? calculateProjectID() : envProjectId;
21+
final projectId = getProjectID();
3822

3923
final corbadoAuth = CorbadoAuth();
4024
await corbadoAuth.init(projectId: projectId);

lib/pages/auth_page.dart

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'package:corbado_auth_example/screens/signup_init.dart';
99
import 'package:flutter/material.dart';
1010
import 'package:hooks_riverpod/hooks_riverpod.dart';
1111
import '../widgets/debug_info.dart';
12+
1213
class AuthPage extends HookConsumerWidget {
1314
AuthPage({super.key}) {}
1415

@@ -18,30 +19,31 @@ class AuthPage extends HookConsumerWidget {
1819

1920
return Scaffold(
2021
appBar: AppBar(title: const Text('Corbado authentication')),
21-
body: Stack(children: [
22-
DebugInfo(),
23-
Center(
24-
child: Container(
25-
constraints: const BoxConstraints(maxWidth: 500),
26-
child: Padding(
27-
padding: const EdgeInsets.symmetric(horizontal: 10),
28-
29-
child:
30-
CorbadoAuthComponent(
31-
corbadoAuth: corbadoAuth,
32-
components: CorbadoScreens(
33-
signupInit: SignupInitScreen.new,
34-
loginInit: LoginInitScreen.new,
35-
emailVerifyOtp: EmailVerifyOtpScreen.new,
36-
passkeyAppend: PasskeyAppendScreen.new,
37-
passkeyVerify: PasskeyVerifyScreen.new,
38-
emailEdit: EmailEditScreen.new,
22+
body: Stack(
23+
children: [
24+
DebugInfo(),
25+
Center(
26+
child: Container(
27+
constraints: const BoxConstraints(maxWidth: 500),
28+
child: Padding(
29+
padding: const EdgeInsets.symmetric(horizontal: 10),
30+
child:
31+
CorbadoAuthComponent(
32+
corbadoAuth: corbadoAuth,
33+
components: CorbadoScreens(
34+
signupInit: SignupInitScreen.new,
35+
loginInit: LoginInitScreen.new,
36+
emailVerifyOtp: EmailVerifyOtpScreen.new,
37+
passkeyAppend: PasskeyAppendScreen.new,
38+
passkeyVerify: PasskeyVerifyScreen.new,
39+
emailEdit: EmailEditScreen.new,
40+
),
3941
),
4042
),
4143
),
4244
),
43-
),
44-
]),
45+
],
46+
),
4547
);
4648
}
4749
}

lib/pages/edit_profile_page.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import 'package:corbado_auth/corbado_auth.dart';
22
import 'package:corbado_auth_example/auth_provider.dart';
33
import 'package:corbado_auth_example/screens/helper.dart';
4-
import 'package:corbado_auth_example/widgets/debug_info.dart';
54
import 'package:corbado_auth_example/widgets/filled_text_button.dart';
65
import 'package:corbado_auth_example/widgets/outlined_text_button.dart';
76
import 'package:http/http.dart' as http;
@@ -10,6 +9,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
109
import 'package:go_router/go_router.dart';
1110
import 'package:hooks_riverpod/hooks_riverpod.dart';
1211
import 'package:overlay_support/overlay_support.dart';
12+
import '../widgets/debug_info.dart';
1313

1414
class EditProfilePage extends HookConsumerWidget {
1515
EditProfilePage({super.key});

lib/pages/passkey_list_page.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import 'package:corbado_auth/corbado_auth.dart';
22
import 'package:corbado_auth_example/auth_provider.dart';
33
import 'package:corbado_auth_example/screens/helper.dart';
4-
import 'package:corbado_auth_example/widgets/debug_info.dart';
54
import 'package:corbado_auth_example/widgets/filled_text_button.dart';
65
import 'package:corbado_auth_example/widgets/outlined_text_button.dart';
76
import 'package:corbado_auth_example/widgets/passkey_card.dart';
@@ -10,6 +9,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
109
import 'package:go_router/go_router.dart';
1110
import 'package:hooks_riverpod/hooks_riverpod.dart';
1211
import 'package:overlay_support/overlay_support.dart';
12+
import 'package:corbado_auth_example/widgets/debug_info.dart';
1313

1414
class PasskeyListPage extends HookConsumerWidget {
1515
PasskeyListPage({super.key});

0 commit comments

Comments
 (0)