token_keeper 1.1.2 copy "token_keeper: ^1.1.2" to clipboard
token_keeper: ^1.1.2 copied to clipboard

Auth tokens, handled. Pure-Dart token manager with single-flight refresh, proactive expiry handling, Result-based APIs, event stream and a Dio interceptor for drop-in 401 retry.

example/main.dart

// ignore_for_file: avoid_print
//
// A self-contained example: simulates a backend, wires Dio with a
// TokenKeeperInterceptor, and shows automatic refresh + 401 retry.
//
// `token_keeper` 1.1.0 uses `resilify`'s `Result<T>` / `Failure` types, so
// the refresher just bridges DioException onto Failure constructors.

import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:resilify/resilify_dio.dart' show mapDioException;
import 'package:token_keeper/dio.dart';
import 'package:token_keeper/token_keeper.dart';

Future<void> main() async {
  // 1. Storage (wrap with CachingTokenStorage on top of secure storage in
  //    production).
  final storage = CachingTokenStorage(InMemoryTokenStorage());

  // 2. Refresher: hits your /auth/refresh endpoint. Must NEVER throw —
  //    return a Result.error(Failure.x(...)) instead.
  final refresherDio = Dio(BaseOptions(baseUrl: 'https://api.example.com'));
  Future<Result<Token>> refresh(Token current) async {
    return Result.tryRunAsync<Token>(
      () async {
        final res = await refresherDio.post<Map<String, dynamic>>(
          '/auth/refresh',
          data: {'refreshToken': current.refreshToken},
        );
        final body = res.data!;
        return Token(
          accessToken: body['access_token'] as String,
          refreshToken: body['refresh_token'] as String?,
          expiresAt: DateTime.now().add(
            Duration(seconds: body['expires_in'] as int),
          ),
        );
      },
      // resilify's Dio integration converts DioException into a structured
      // Failure with the right code and message.
      onError: (e, st) =>
          e is DioException ? mapDioException(e) : Failure.unknown(cause: e),
    );
  }

  // 3. Keeper.
  final keeper = TokenKeeper(
    storage: storage,
    refresher: refresh,
    proactiveWindow: const Duration(seconds: 30),
    retryConfig: RefreshRetryConfig.exponential(maxAttempts: 3),
    logger: (level, message, {error, stackTrace}) {
      print('[token_keeper][${level.name}] $message');
    },
  );

  // 4. Listen for logout events.
  keeper.events.listen((event) {
    switch (event) {
      case TokenRefreshedEvent():
        print('-> token refreshed');
      case TokenClearedEvent():
        print('-> session ended; route user to login');
      case RefreshFailedEvent(:final failure):
        print('-> refresh failed: ${failure.message}');
    }
  });

  // 5. App-wide Dio wired up with the interceptor.
  final api = Dio(BaseOptions(baseUrl: 'https://api.example.com'));
  api.interceptors.add(
    TokenKeeperInterceptor(
      keeper: keeper,
      dio: api,
      onRefreshFailed: (_) => print('navigate to /login'),
    ),
  );

  // 6. Login (the request that gets the initial tokens skips the interceptor).
  final loginRes = await api.post<Map<String, dynamic>>(
    '/auth/login',
    data: {'email': 'me@example.com', 'password': 'hunter2'},
    options: Options(extra: {'token_keeper_skip_auth': true}),
  );
  final body = loginRes.data!;
  await keeper.setTokens(
    Token(
      accessToken: body['access_token'] as String,
      refreshToken: body['refresh_token'] as String?,
      expiresAt: DateTime.now().add(
        Duration(seconds: body['expires_in'] as int),
      ),
    ),
  );

  // 7. Authenticated calls — the interceptor handles header + 401 retry.
  final me = await api.get<Map<String, dynamic>>('/me');
  print('me = ${jsonEncode(me.data)}');

  // 8. Or use withValidToken for non-Dio backends.
  final result = await keeper.withValidToken<String>((token) async {
    return Success('hello, ${token.accessToken.substring(0, 4)}…');
  });
  result.when(
    success: print,
    error: (f) => print('failed: ${f.message}'),
  );

  await keeper.dispose();
}
0
likes
160
points
323
downloads

Documentation

API reference

Publisher

verified publisherhimanshulahoti.is-a.dev

Weekly Downloads

Auth tokens, handled. Pure-Dart token manager with single-flight refresh, proactive expiry handling, Result-based APIs, event stream and a Dio interceptor for drop-in 401 retry.

Repository (GitHub)
View/report issues

Topics

#authentication #token #oauth #dio #interceptor

Funding

Consider supporting this project:

github.com

License

MIT (license)

Dependencies

dio, equatable, meta, resilify

More

Packages that depend on token_keeper