LogoRevali

Authentication

Secure your endpoints with a Guard

This tutorial protects an endpoint behind a bearer token using a Guard -- the lifecycle component dedicated to pass/block authorization decisions. It runs after middleware and before interceptors, so by the time your endpoint executes, authorization is already settled.

Build the guard#

A LifecycleComponent method that returns GuardResult acts as a guard. Read the Authorization header, validate it, and share the resolved value with the endpoint via Data:

lib/components/require_auth.dart
import 'package:revali_router/revali_router.dart';

class RequireAuth implements LifecycleComponent {
  const RequireAuth();

  GuardResult checkAuth(
    @Header('Authorization') String? authorization,
    Data data,
  ) {
    if (authorization == null || !authorization.startsWith('Bearer ')) {
      return const GuardResult.block(
        statusCode: 401,
        body: 'Missing or invalid Authorization header',
      );
    }

    final token = authorization.substring('Bearer '.length);

    if (!isValidToken(token)) {
      return const GuardResult.block(statusCode: 401, body: 'Invalid token');
    }

    data.add(token);
    return const GuardResult.pass();
  }
}

// Replace with a real lookup against your auth provider / database.
bool isValidToken(String token) => token == 'secret-token';

Protect an endpoint#

Apply the guard like any other lifecycle component, and read the token it stored in Data with the @Data() annotation:

routes/controllers/account_controller.dart
import 'package:revali_router/revali_router.dart';

@Controller('account')
class AccountController {
  const AccountController();

  @RequireAuth()
  @Get('me')
  String me(@Data() String token) => 'authenticated as $token';
}
  • GET /account/me with no Authorization header, or a malformed one → 401 Missing or invalid Authorization header.
  • GET /account/me with Authorization: Bearer wrong401 Invalid token.
  • GET /account/me with Authorization: Bearer secret-token200 authenticated as secret-token.

Protecting more than one endpoint#

Apply @RequireAuth() at the controller or app level (instead of on each endpoint) to guard every route beneath it in one place -- see Scoping. For role-based checks on top of authentication, add a second guard that reads the user Data the first guard stored and blocks based on role, the same pattern shown in the Guards reference's RoleGuard example.

What's next?#

  • Error Handling — return consistent error responses for your own exceptions
  • Guards reference — the full GuardResult API and a role-based example
  • Data Sharing — passing values between lifecycle components and endpoints