Unlocking the Future: Passwordless Authentication(Passkey) With Flutter and Node.js
Tags: flutter, android, passworldless, tutorial
Published: 2024-06-16 14:45:31

> Welcome Back, Fellow Coders!

Hey everyone! It’s been a hot minute since our last deep dive, but I’m thrilled to be back and diving headfirst into some seriously cool tech. Today, we’re tackling a topic that’s bound to make your life (and your users’ lives) a whole lot easier: passwordless authentication. Yes, you heard that right – we’re talking about a future where you’ll never have to remember another password again.


In this post, we’ll explore how to implement passwordless authentication in your Flutter app with the help of Node.js on the server side. It’s a powerful combo that promises to enhance security and improve user experience. So, grab your favorite coding beverage, get comfy, and let’s embark on this exciting journey together. Welcome back, and let’s get coding!




## Table of Contents

- [Introduction](#introduction)
- [Prerequisites](#prerequisites)
- [Setup Your Own WebAuthn Server](#setup-your-own-webauthn-server)
- [Integrate into Flutter](#integrate-into-flutter)
- [Outputs](#outputs)
- [Conclusion](#conclusion)

## Introduction

### Passwordless Authentication: A New Era of Security

Passwordless authentication is an innovative approach to securing user accounts and sensitive data without relying on traditional passwords. Instead of requiring users to create and remember complex passwords, passwordless authentication leverages advanced technologies like biometrics, security tokens, and public key cryptography. This method not only enhances security but also improves user experience by simplifying the login process.

![passwordless-meme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d46nizve4bccx6np7iwv.jpeg)



#### Benefits of Passwordless Authentication
- **Enhanced Security**: Traditional passwords are prone to being weak, reused across multiple sites, and vulnerable to phishing attacks and data breaches. Passwordless methods, such as biometrics or hardware tokens, are significantly harder for attackers to compromise.

- **Improved User Experience**: Users no longer need to remember complex passwords or go through cumbersome password recovery processes. This leads to a smoother and more pleasant authentication experience.



![passwordless-meme-usage](https://i.imgflip.com/8u0xl7.jpg)

### What is passkey ?

A passkey is a form of authentication credential used in passwordless authentication systems, typically based on the WebAuthn standard. It serves as a secure, user-friendly alternative to traditional passwords. Here are some key points about passkeys:

1. **Digital Credential**: A passkey is a digital credential stored securely on a user's device.
  
2. **Public Key Cryptography**: It utilizes public key cryptography to authenticate users. During registration, a key pair is generated: a private key stored securely on the user's device and a public key shared with the server.

3. **Authentication Process**: To authenticate, the user proves possession of the private key associated with their passkey. This can be done through biometric verification (e.g., fingerprint scan, facial recognition) or by unlocking the device.

4. **Enhanced Security**: Passkeys are more secure than traditional passwords because they are not susceptible to phishing attacks or credential stuffing. They also eliminate the risk of password reuse and theft.

5. **User Convenience**: Passkeys improve user experience by simplifying the authentication process. Users do not need to remember complex passwords or go through lengthy password reset procedures.


{% youtube 2xdV-xut7EQ %}


---

## Prerequisites

- **Flutter** : Basic knowledge
- **Node-js** : Node js. is used server, you can use any backend language for framework. 
- **MongoDB**: we are using **MongoDB** for database,you can use whichever you find best.
- **Active Brain**



## Setup Your Own WebAuthn Server

Before starting setup **webauthn** server, lets talk about what is **webAuthn** and **passkey**.


**Passkeys** and **WebAuthn** complement each other rather than compete. **WebAuthn** serves as the framework that facilitates the functionality of passkeys.
### Key difference 

- Passkeys act as digital credentials used for authentication, while WebAuthn is a set of rules enabling communication between browsers and websites for passkey-based authentication. 


Once this communication setup is complete, WebAuthn instructs the browser on how to communicate with the relying party (the website where passkey authentication is used) for the authentication process.

Read more about pass key and WebAuthn [here](https://teampassword.com/blog/passkey-vs-webauthn)


### How WebAuthn Works

WebAuthn operates through several essential phases to establish secure and passwordless authentication:

1. **Registration Initiation**: When a user initiates registration, the server generates an encoded challenge and sends it, along with other necessary data, to the client.
![passkey-registration](https://images.ctfassets.net/23aumh6u8s0i/2wFESoN5p5JJNnCM8gzIIA/8359b4717f89f2bd6ad0036961b4e841/registartion-flow.jpg)

2. **User Approval**: During this phase, the user selects and registers a credential. This can be a passkey, biometric (like fingerprint or face-lock), USB key, Bluetooth device, or device security code. The user's approval is crucial to proceed.

3. **Verification Process**: Once the user approves the registration, the data returned by the approved credential is sent back to the server. This data includes the public key, the type of device used, and a unique device identifier.

4. **Completion of Registration**: After verification, the user's credentials are securely stored in the database. Key pieces of information such as the `userId` and `publicCredentialId` are recorded, enabling future authentication without the need for traditional passwords.

This process ensures a robust and user-friendly authentication method, leveraging modern security practices to safeguard user accounts effectively.

### Understanding Relay Servers (rpId)


In the realm of WebAuthn (Web Authentication), the Relay Server, known as rpId, serves as a pivotal intermediary between the client device and the relying party (e.g., a website or service). Here’s a concise breakdown using two focal points:

- **Authentication Endpoint**: The Relay Server functions as the designated endpoint for authentication requests originating from the client device. It facilitates a secure channel for communication between the client and the relying party.

- **Secure Challenge Management**: Upon receiving an authentication request, the Relay Server generates and manages a cryptographic challenge. This challenge ensures the integrity of the authentication process, guarding against replay attacks and unauthorized access attempts.

> Login using **WebAuthn**

![web-authn-web-login](https://images.ctfassets.net/23aumh6u8s0i/6sQPhaAjnYdPl2L1Ruu0gU/0b866c672109b02ba5a856b324d719a0/login-flow.jpg)

---

**Enough with theory, let's implement passkey.***

## Setup Server with node JS

### Add support for Digital Asset Links
To enable passkey support for your flutter Android app, associate your app with a website that your app owns. You can declare this association by completing the following steps:

- Create a Digital Asset Links JSON file. For example, to declare that the website `https://signin.example.com` and an Android app with the package name com.example can share sign-in credentials, create a file named `assetlinks.json` with the following content:

```
[
  {
    "relation" : [
      "delegate_permission/common.handle_all_urls",
      "delegate_permission/common.get_login_creds"
    ],
    "target" : {
      "namespace" : "android_app",
      "package_name" : "com.example.android", //packageId
      "sha256_cert_fingerprints" : [
        SHA_HEX_VALUE
      ]
    }
  }
]
  ```
- Host the Digital Assets Link JSON file at the following location on the sign-in domain:
```
https://domain[:optional_port]/.well-known/assetlinks.json
```

- on Sending `GET` request following data should be return 

```
> GET /.well-known/assetlinks.json HTTP/1.1
> User-Agent: curl/7.35.0
> Host: signin.example.com

< HTTP/1.1 200 OK
< Content-Type: application/json
```
> You can validate digital assets link json from [here](https://developers.google.com/digital-asset-links/tools/generator).

- `server.js` for hosting digital assetslink

```
import express from 'express';
const app = express();

app.use('/.well-known', express.static('public'));

app.listen(3000, () => {
  console.log('Server started on port 3000');
});
```

### Setup Node server


-  Installing following packages

```
npm install @simplewebauthn/server base64url dotenv express jsonwebtoken mongodb uuid

```

- Connect to MongoDB in `src/utils/db.ts`
{% gist https://gist.github.com/Djsmk123/a5ee75c39b671f3f0fb30b9e0ae92874  %}

- Util for managing user in `src/utils/userManager.ts`

{% gist https://gist.github.com/Djsmk123/77eb17c8099088217450c1a487a89a2d  %}

- We need to store every `challenge` string generated by the backend during registration or login. This stored data will allow us to verify whether a given `challenge` is valid or not. Additional data such as `expiryTime` can be included with each challenge, but for simplicity, we are only storing strings.

 `src/utils/challengerManagers.ts`

{% gist https://gist.github.com/Djsmk123/9e8d1d9d1b6a6752384e947fae4d58e0  %}
- When registration is completed, we receive the following in the response: `credentialID`, `credentialPublicKey`, `rpID`, `origin`, and other parameters. We need to store these values alongside the `userId` for the purpose of verifying sign-ins.

`src/utils/passkey.ts`

{% gist https://gist.github.com/Djsmk123/74f4c4f21d4d4f4ec23380a4db6ba7c0  %}

- Create basic http server using node

```
import express, { Request, Response } from 'express';
import { connectToMongoDB } from './utils/db';

// Constants
const rpID = "<domain>.com"; // Replace with your actual rpID
const androidHashKey="<hashKey">; //android release signing app hashapp 

const origin = [
    rpID,
androidHashKey
    
];

// Initialize Express app
const app = express();
const port = process.env.PORT || 8080;
app.use(express.json());

// Connect to MongoDB
connectToMongoDB();

// GET endpoint for /
app.get('/', (req: Request, res: Response) => {
    res.send('Hello World!');
});

// Start server
const localIp = process.env.LOCAL_IP || 'localhost';
app.listen(port, () => {
    console.log(`Express is listening at http://${localIp}:${port}`);
});

export default app;

```

- Generate Android hash key either

```
keytool -printcert -jarfile app.apk

```

------------------  OR ---------------------------------------

```
keytool -printcert -file CERT.RSA 

```

- Create middleware for verify jwt token:

`src/api/middleware.ts`

{% gist https://gist.github.com/Djsmk123/a2ae203f1a09f46f1fbd4f3ec23b3b03  %}

Now we will create following endpoints:

- **`/register/start`:** Initiates the registration process.
- **`/register/complete`:** Verifies passkey data, stores the user in the database, deletes the challenge once sign-in or user creation is completed, and sends requested data with a JWT token.
- **`/login/start`:** Returns the payload needed to start the login flow on the client side.
- **`/login/complete`:** Verifies data returned by the client with passkey data from MongoDB and returns requested data with a JWT token.
- **`/me`:** An auth-protected route that requires a JWT token to return the user object.

**`app.ts`** :

{% gist https://gist.github.com/Djsmk123/3ba410c7c4d0696a43ea89e49ac534f7  %}


We are done with server side integration.

---

## Integrate into Flutter

- Add Following packages into flutter app:

   - **Credential Manager** : [Credential Manager](https://pub.dev/packages/credential_manager) is a Jetpack API that supports multiple sign-in methods, such as username and password, passkeys, and federated sign-in solutions (such as Sign-in with Google) in a single API, thus simplifying the integration for developers.

`flutter pub add credential_manager`

   > Note: I am author of this package and it is currently support `Android` platform for flutter.

 More about Compose Credential Manager:
[https://developer.android.com/identity/sign-in/credential-manager](https://developer.android.com/identity/sign-in/credential-manager)
    - **HTTP**: For implementing server APIs
 ` flutter pub add http`


> Note: I am going talk about screens and UI component we will integrate direct.

- Initialise Credential Manager:

```dart
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  if (AuthService.credentialManager.isSupportedPlatform) { //check if platform is supported
    await AuthService.credentialManager.init(
      preferImmediatelyAvailableCredentials: true,
    );
  }
  runApp(const MyApp());
}
```

- Create `userModel` in dart

{% gist https://gist.github.com/Djsmk123/db344b81e180e7369160955ad55dd5d7  %}

- Create Authentication Service for calling APIs:

{% gist https://gist.github.com/Djsmk123/b54f9244f128dad474443e9d52ba4dea  %}

Certainly! Here's the markdown file with the corrected content:

## Registration

- **Get required payload for `/register/start` endpoint:**
  
  ```dart
  final res = await AuthService.passKeyRegisterInit(username: username!);
  ```

- **Send the payload to Credential Manager to start the user approval flow for registration:**
  
  ```dart
  final credResponse = await AuthService.credentialManager.savePasskeyCredentials(request: res);
  ```

- **After user approval, send the data to `/register/complete` API for verification and user creation:**
  
  ```dart
  final user = await AuthService.passKeyRegisterFinish(
      username: username!,
      challenge: res.challenge,
      request: credResponse,
  );
  ```

## Sign-In

- **Make a GET request to `/login/start` endpoint to retrieve `challenge` and other payload:**
  
  ```dart
  final res = await AuthService.passKeyLoginInit();
  ```

- **Initiate the user approval flow for sign-in by sending the request data from `/login/start` to `CredentialManager`:**
  
  ```dart
  final credResponse = await AuthService.credentialManager.getPasswordCredentials(
      passKeyOption: CredentialLoginOptions(
          challenge: res.challenge,
          rpId: res.rpId,
          userVerification: res.userVerification,
      ),
  );
  ```

- **Verify the user by sending the data returned from `getPasswordCredentials` method to `/login/complete/` endpoint to retrieve the user object:**
  
  ```dart
  final user = await AuthService.passKeyLoginFinish(
      challenge: res.challenge,
      request: credResponse.publicKeyCredential!,
  );
  ```

## Outputs


![Passkey registration 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lzpe920ht5vxfgjfsn4o.jpeg)

![Passkey Registation 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v51tvdvh29zq6y2kubjm.jpeg)

![Passkey Login](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lijiue09i7sf8u5qi85v.jpeg)







### Conclusion

In conclusion, exploring passkey authentication with WebAuthn integration for a seamless login experience in Flutter has been insightful. This technology offers a secure and user-friendly alternative to traditional password-based authentication methods. By leveraging cryptographic authentication mechanisms and user verification processes, passkey authentication enhances security while simplifying user interactions.

Throughout this article, we've delved into the foundational concepts of passkey authentication, including its integration with WebAuthn for server-side operations using Node.js. We've explored essential endpoints for user registration and login, demonstrating how to initiate and complete these processes securely.

Moreover, the content of this article has been refined using AI-powered tools to ensure clarity and grammatical accuracy. This approach underscores the importance of leveraging technology to enhance content quality and readability.

For those interested in implementing passkey authentication in their projects, the provided code snippets and explanations serve as a practical guide. Each step, from initiating registration to verifying credentials during login, has been detailed to facilitate smooth integration into Flutter applications.

More info about credential manager read following blog:

[Bringing seamless authentication to your apps with passkeys using Credential Manager API](https://medium.com/androiddevelopers/bringing-seamless-authentication-to-your-apps-using-credential-manager-api-b3f0d09e0093)




## **Source code** :

- [Flutter App](https://github.com/Djsmk123/passkey_example_flutter)
- [Backend Server](https://github.com/Djsmk123/pass_key_backend)
- [React App which has digital Assets link](https://github.com/Djsmk123/flutter_deeplinking_example/tree/main/reactjs_blogs_deeplink_example)
- [Credential Manager Package](https://pub.dev/packages/credential_manager)

## Follow me on 

- [Twitter](https://twitter.com/smk_winner)

- [Instagram](https://www.instagram.com/smkwinner/)

- [Github](https://www.github.com/djsmk123)

- [linkedin](https://www.linkedin.com/in/md-mobin-bb928820b/)

- [dev.to](https://dev.to/djsmk123)

- [Medium](https://medium.com/@djsmk123)





---
By leveraging AI for grammar correction and clarity enhancement, this article aims to provide a polished and informative guide to passkey authentication in Flutter. All credits for the code snippets and technical insights go to the authors and contributors of the referenced resources.