DEV Community

Cover image for Unlocking the Future: Passwordless Authentication(Passkey) With Flutter and Node.js
Md. Mobin
Md. Mobin

Posted on

Unlocking the Future: Passwordless Authentication(Passkey) With Flutter and Node.js

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

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

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

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.


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

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

  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


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
      ]
    }
  }
]
Enter fullscreen mode Exit fullscreen mode
  • Host the Digital Assets Link JSON file at the following location on the sign-in domain:
https://domain[:optional_port]/.well-known/assetlinks.json
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode

You can validate digital assets link json from here.

  • 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');
});
Enter fullscreen mode Exit fullscreen mode

Setup Node server

  • Installing following packages
npm install @simplewebauthn/server base64url dotenv express jsonwebtoken mongodb uuid

Enter fullscreen mode Exit fullscreen mode
  • Connect to MongoDB in src/utils/db.ts

  • Util for managing user in src/utils/userManager.ts

  • 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

  • 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

  • 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;

Enter fullscreen mode Exit fullscreen mode
  • Generate Android hash key either
keytool -printcert -jarfile app.apk

Enter fullscreen mode Exit fullscreen mode

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

keytool -printcert -file CERT.RSA 

Enter fullscreen mode Exit fullscreen mode
  • Create middleware for verify jwt token:

src/api/middleware.ts

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 :

We are done with server side integration.


Integrate into Flutter

  • Add Following packages into flutter app:

    • Credential Manager : 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
- 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:
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  if (AuthService.credentialManager.isSupportedPlatform) { //check if platform is supported
    await AuthService.credentialManager.init(
      preferImmediatelyAvailableCredentials: true,
    );
  }
  runApp(const MyApp());
}
Enter fullscreen mode Exit fullscreen mode
  • Create userModel in dart
  • Create Authentication Service for calling APIs:

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

Registration

  • Get required payload for /register/start endpoint:
  final res = await AuthService.passKeyRegisterInit(username: username!);
Enter fullscreen mode Exit fullscreen mode
  • Send the payload to Credential Manager to start the user approval flow for registration:
  final credResponse = await AuthService.credentialManager.savePasskeyCredentials(request: res);
Enter fullscreen mode Exit fullscreen mode
  • After user approval, send the data to /register/complete API for verification and user creation:
  final user = await AuthService.passKeyRegisterFinish(
      username: username!,
      challenge: res.challenge,
      request: credResponse,
  );
Enter fullscreen mode Exit fullscreen mode

Sign-In

  • Make a GET request to /login/start endpoint to retrieve challenge and other payload:
  final res = await AuthService.passKeyLoginInit();
Enter fullscreen mode Exit fullscreen mode
  • Initiate the user approval flow for sign-in by sending the request data from /login/start to CredentialManager:
  final credResponse = await AuthService.credentialManager.getPasswordCredentials(
      passKeyOption: CredentialLoginOptions(
          challenge: res.challenge,
          rpId: res.rpId,
          userVerification: res.userVerification,
      ),
  );
Enter fullscreen mode Exit fullscreen mode
  • Verify the user by sending the data returned from getPasswordCredentials method to /login/complete/ endpoint to retrieve the user object:
  final user = await AuthService.passKeyLoginFinish(
      challenge: res.challenge,
      request: credResponse.publicKeyCredential!,
  );
Enter fullscreen mode Exit fullscreen mode

Outputs

Passkey registration 1

Passkey Registation 2

Passkey Login

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

Source code :

Follow me on


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.

Top comments (1)

Collapse
 
vdelitz profile image
vdelitz

Great tutorial!