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.
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.
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:
Digital Credential: A passkey is a digital credential stored securely on a user's device.
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.
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.
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.
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:
Registration Initiation: When a user initiates registration, the server generates an encoded challenge and sends it, along with other necessary data, to the client.
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.
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.
Completion of Registration: After verification, the user's credentials are securely stored in the database. Key pieces of information such as the
userId
andpublicCredentialId
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
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 namedassetlinks.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.
-
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
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 givenchallenge
is valid or not. Additional data such asexpiryTime
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 theuserId
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;
- 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
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());
}
- 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!);
- Send the payload to Credential Manager to start the user approval flow for registration:
final credResponse = await AuthService.credentialManager.savePasskeyCredentials(request: res);
- 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,
);
Sign-In
- Make a GET request to
/login/start
endpoint to retrievechallenge
and other payload:
final res = await AuthService.passKeyLoginInit();
- Initiate the user approval flow for sign-in by sending the request data from
/login/start
toCredentialManager
:
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:
final user = await AuthService.passKeyLoginFinish(
challenge: res.challenge,
request: credResponse.publicKeyCredential!,
);
Outputs
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)
Great tutorial!