HomeBlogUncategorizedBuilding a Full Authentication Flow with Dart Frog

Building a Full Authentication Flow with Dart Frog

When it comes to building modern backend applications, one of the most critical features is authentication. In this post, I’ll walk you through how I built a complete authentication flow using Dart Frog, a fast, minimalistic backend framework powered by Dart.

This is part of the backend powering [SpeakUp], a multi-author content platform currently in development.

🧱 Features Covered

Here’s what we implemented:

  1. User Registration & Login
  2. JWT Authentication with Middleware
  3. Password Reset Flow
    • OTP generation and validation
    • Email notification
    • Temporary token for added security
    • Final password update
  4. Clean Architecture using Services, Repositories, and Middleware
  5. PostgreSQL as our database
  6. Cron Jobs for background tasks (like cleaning up expired OTPs)

🔐 JWT Authentication

We use JWT (JSON Web Token) to handle secure sessions. Upon successful login, a token is generated and returned:

static String generateToken(Map<String, dynamic> claims) {
  final jwt = JWT(claims, issuer: _jwtIssuer);
  return jwt.sign(SecretKey(_secretKey), expiresIn: Duration(minutes: 120));
}


A middleware verifyHeaderTokenAndReturnUser is used to decode the token and attach the user to the context:

Middleware verifyHeaderTokenAndReturnUser() {
  return (handler) => (context) async {
    final authHeader = context.request.headers['Authorization'];
    final token = authHeader?.replaceFirst('Bearer ', '');
    final decoded = JWTUtilis.verifyToken(token);
    return handler(context.provide(() => User(...)));
  };
}

🔁 Password Reset Flow with OTP

Step 1: Request OTP

We generate a 6-digit OTP, store it in the database, and send it to the user’s email:

CREATE TABLE speakup_password_reset_table (
  email VARCHAR NOT NULL,
  otp VARCHAR(6) NOT NULL,
  expires_at TIMESTAMP NOT NULL,
  is_used BOOLEAN DEFAULT FALSE
);


Rate limiting was handled by checking recent OTPs sent in the last minute:

if (lastOtpCreatedAt.difference(now).inSeconds < 60) {
  return ApiResponse.tooManyRequests('Try again later.');
}

Step 2: OTP Verification

We validate that the OTP is correct and not expired. If valid, we generate a temporary token that can be used only to reset the password.

final tempToken = JWTUtilis.generateToken({
  'email': email,
  'purpose': 'reset_password',
  'exp': DateTime.now().add(Duration(minutes: 10)).millisecondsSinceEpoch
});

Step 3: Set New Password

With the temporary token, users can now securely reset their password. We verify the token’s claims and hash the new password using bcrypt before updating the DB.

🛠 Cron Job for Cleanup

We used the cron package to run a cleanup job every 10 minutes to delete expired or used OTPs:

cron.schedule(Schedule.parse('*/10 * * * *'), () async {
  await connection.execute('''
    DELETE FROM speakup_password_reset_table
    WHERE expires_at < NOW() OR is_used = TRUE;
  ''');
});

💡 Key Takeaways

  • Dart Frog provides a flexible and clean way to build robust backend APIs.
  • PostgreSQL is powerful enough for relational needs including rate limiting and OTP tracking.
  • Middleware in Dart Frog makes token verification seamless.
  • Cron jobs help manage background cleanup tasks in a Dart-native way.

🔚 Final Thoughts

Dart Frog has proved itself as a reliable option for building a full-featured bac


If you’re a Dart developer or a startup looking for a modern, cost-effective backend stack—Dart Frog is worth your attention.

Leave a Reply

Your email address will not be published. Required fields are marked *