aim_server_basic_auth 0.0.1 copy "aim_server_basic_auth: ^0.0.1" to clipboard
aim_server_basic_auth: ^0.0.1 copied to clipboard

Basic authentication middleware for Aim framework. Provides RFC 7617 compliant HTTP Basic Authentication with customizable user verification, realm support, and path exclusion.

aim_server_basic_auth #

HTTP Basic Authentication middleware for the Aim framework.

Overview #

aim_server_basic_auth provides RFC 7617 compliant HTTP Basic Authentication middleware for the Aim framework. This package enables simple username/password authentication with full support for custom user verification, realm configuration, and path exclusions.

Features #

  • 🔐 Basic Authentication - RFC 7617 compliant HTTP Basic Auth middleware
  • 🎫 Custom Verification - Flexible async user verification function
  • 🔒 Secure - Automatic WWW-Authenticate header response
  • 📋 Realm Support - Configurable protection realm for authentication dialogs
  • Path Exclusion - Skip authentication for specific routes (e.g., /login, /public)
  • 🛡️ Flexible - Supports passwords with special characters, including colons
  • 🌐 Unicode Support - Full support for non-ASCII characters in credentials
  • 🎯 Type-safe - Leverages Dart's type system for compile-time safety

Installation #

Add aim_server_basic_auth to your pubspec.yaml:

dependencies:
  aim_server: ^0.0.6
  aim_server_basic_auth: ^0.0.1

Then run:

dart pub get

Usage #

Basic Authentication #

import 'dart:io';
import 'package:aim_server/aim_server.dart';
import 'package:aim_server_basic_auth/aim_server_basic_auth.dart';

void main() {
  final app = Aim<BasicAuthEnv>(
    envFactory: () => BasicAuthEnv(
      options: BasicAuthOptions(
        realm: 'Admin Area',
        verify: (username, password) async {
          // Simple credential check (use password hashing in production!)
          return username == 'admin' && password == 'secret123';
        },
      ),
    ),
  );

  // Apply Basic Auth middleware
  app.use(basicAuth());

  // Protected endpoint
  app.get('/admin', (c) async {
    final username = c.variables.username;
    return c.json({'message': 'Welcome, $username!'});
  });

  app.serve(host: InternetAddress.anyIPv4, port: 8080);
  print('Server running on http://localhost:8080');
}

With Database Verification #

import 'package:crypto/crypto.dart';
import 'dart:convert';

final app = Aim<BasicAuthEnv>(
  envFactory: () => BasicAuthEnv(
    options: BasicAuthOptions(
      realm: 'My Application',
      verify: (username, password) async {
        // Fetch user from database
        final user = await database.findUserByUsername(username);
        if (user == null) return false;

        // Verify password hash (using bcrypt, argon2, etc.)
        final passwordHash = sha256.convert(utf8.encode(password)).toString();
        return passwordHash == user.passwordHash;
      },
    ),
  ),
);

app.use(basicAuth());

Path Exclusion #

Exclude specific paths from authentication:

final app = Aim<BasicAuthEnv>(
  envFactory: () => BasicAuthEnv(
    options: BasicAuthOptions(
      realm: 'Protected Area',
      verify: (username, password) async {
        return username == 'admin' && password == 'secret';
      },
      excludedPaths: ['/login', '/register', '/public', '/health'],
    ),
  ),
);

app.use(basicAuth());

// Public endpoint (no authentication required)
app.get('/login', (c) async {
  return c.html('<form>...</form>');
});

// Protected endpoint (authentication required)
app.get('/dashboard', (c) async {
  final username = c.variables.username;
  return c.json({'user': username});
});

Custom Realm #

The realm identifier appears in the browser's authentication dialog:

final options = BasicAuthOptions(
  realm: 'Staff Only Area',  // Shown to users
  verify: (username, password) async {
    return await authService.verify(username, password);
  },
);

Complete Example #

import 'dart:io';
import 'package:aim_server/aim_server.dart';
import 'package:aim_server_basic_auth/aim_server_basic_auth.dart';

void main() async {
  // Configuration
  final options = BasicAuthOptions(
    realm: 'Admin Dashboard',
    verify: (username, password) async {
      // In production, verify against a database with hashed passwords
      final validUsers = {
        'admin': 'admin_password_hash',
        'user': 'user_password_hash',
      };

      return validUsers[username] == password; // Simplified for demo
    },
    excludedPaths: ['/login', '/health', '/public'],
  );

  final app = Aim<BasicAuthEnv>(
    envFactory: () => BasicAuthEnv(options: options),
  );

  app.use(basicAuth());

  // Health check (excluded from auth)
  app.get('/health', (c) async => c.json({'status': 'ok'}));

  // Login page (excluded from auth)
  app.get('/login', (c) async {
    return c.html('''
      <h1>Login</h1>
      <p>Use HTTP Basic Authentication to access protected resources.</p>
    ''');
  });

  // Protected admin routes
  app.get('/admin/dashboard', (c) async {
    final username = c.variables.username;
    return c.json({
      'page': 'dashboard',
      'user': username,
      'timestamp': DateTime.now().toIso8601String(),
    });
  });

  app.get('/admin/users', (c) async {
    final username = c.variables.username;
    return c.json({
      'page': 'users',
      'admin': username,
      'users': ['alice', 'bob', 'charlie'],
    });
  });

  // Protected API endpoint
  app.post('/api/data', (c) async {
    final username = c.variables.username;
    final body = await c.req.json();

    return c.json({
      'message': 'Data received',
      'user': username,
      'data': body,
    });
  });

  await app.serve(host: InternetAddress.anyIPv4, port: 8080);
  print('Server running on http://localhost:8080');
  print('Try accessing: http://localhost:8080/admin/dashboard');
  print('Credentials: admin / admin_password_hash');
}

How It Works #

  1. Client Request: User attempts to access a protected resource
  2. Missing Credentials: Server returns 401 with WWW-Authenticate: Basic realm="..."
  3. Browser Dialog: Browser displays authentication dialog with the realm name
  4. Credential Submission: Client sends Authorization: Basic base64(username:password)
  5. Verification: Server decodes and verifies credentials using the verify function
  6. Access Granted/Denied:
    • Valid credentials → sets c.variables.username and proceeds
    • Invalid credentials → returns 401 with WWW-Authenticate header
Client                          Server
  |                               |
  |------ GET /admin ------------>|
  |                               | (no auth header)
  |<----- 401 Unauthorized -------|
  |   WWW-Authenticate: Basic     |
  |   realm="Admin Area"          |
  |                               |
  | [Browser shows dialog]        |
  |                               |
  |------ GET /admin ------------>|
  |   Authorization: Basic        |
  |   YWRtaW46c2VjcmV0           |
  |                               | (verify credentials)
  |<----- 200 OK -----------------|
  |   {"user": "admin"}           |

Security Best Practices #

1. Always Use HTTPS in Production #

Basic Auth sends credentials in Base64 encoding (not encryption). Always use HTTPS to prevent credentials from being intercepted.

// ✅ Production
final server = await app.serve(
  host: InternetAddress.anyIPv4,
  port: 443,
  securityContext: SecurityContext()
    ..useCertificateChain('server_chain.pem')
    ..usePrivateKey('server_key.pem'),
);

2. Use Strong Password Hashing #

Never store or compare plaintext passwords. Use bcrypt, argon2, or similar:

import 'package:bcrypt/bcrypt.dart';

verify: (username, password) async {
  final user = await db.findUser(username);
  if (user == null) return false;

  // Verify hashed password
  return BCrypt.checkpw(password, user.passwordHash);
}

3. Implement Rate Limiting #

Protect against brute force attacks:

// Use aim_server_ratelimit (when available) or implement custom logic
final loginAttempts = <String, int>{};

verify: (username, password) async {
  // Check rate limit
  final attempts = loginAttempts[username] ?? 0;
  if (attempts >= 5) {
    return false; // Locked out
  }

  final isValid = await authService.verify(username, password);

  if (!isValid) {
    loginAttempts[username] = attempts + 1;
  } else {
    loginAttempts.remove(username);
  }

  return isValid;
}

4. Never Log Passwords #

// ❌ Bad
print('Login attempt: $username:$password');

// ✅ Good
print('Login attempt for user: $username');

5. Use Environment Variables for Secrets #

final secret = Platform.environment['ADMIN_PASSWORD'] ??
               throw Exception('ADMIN_PASSWORD not set');

Error Handling #

The middleware returns 401 Unauthorized in these cases:

  • Missing Authorization header
  • Invalid Authorization header format (not "Basic ...")
  • Invalid Base64 encoding
  • Missing colon separator in credentials
  • Verification function returns false

All 401 responses include the WWW-Authenticate header with the configured realm.

// Example 401 response
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Admin Area"
Content-Type: application/json

{"error": "Unauthorized"}

API Reference #

For detailed API documentation, see the API reference on pub.dev.

Comparison with JWT #

Feature Basic Auth JWT
Stateless ✅ Yes ✅ Yes
Sends credentials Every request Only during login
Token expiration ❌ No ✅ Yes (exp claim)
Complexity Very simple More complex
Best for Internal tools, admin panels Public APIs, mobile apps
HTTPS required ✅ Critical ✅ Recommended
Browser support ✅ Native ⚠️ Requires JavaScript

Use Basic Auth for:

  • Internal admin dashboards
  • Development/staging environments
  • Simple authentication needs
  • Legacy system compatibility

Use JWT for:

  • Public-facing APIs
  • Mobile applications
  • Token expiration requirements
  • Stateless microservices

Contributing #

Contributions are welcome! Please see the main repository for contribution guidelines.

License #

See the LICENSE file in the main repository.

1
likes
160
points
125
downloads

Publisher

verified publisheraim-dart.dev

Weekly Downloads

Basic authentication middleware for Aim framework. Provides RFC 7617 compliant HTTP Basic Authentication with customizable user verification, realm support, and path exclusion.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

aim_server, crypto

More

Packages that depend on aim_server_basic_auth