keychain_access 1.0.4 copy "keychain_access: ^1.0.4" to clipboard
keychain_access: ^1.0.4 copied to clipboard

PlatformiOSmacOS

Flutter plugin to access Keychain Access apis on MacOS and iOS. Provides a secure way to store and retrieve secure content from the keychain

example/lib/main.dart

import 'package:flutter/material.dart';
import 'dart:async';

import 'package:flutter/services.dart';
import 'package:keychain_access/keychain_access.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final _simpleResults = _PluginFunctionResults();
  final _resultsForApplicationName = _PluginFunctionResults();
  final _keychainAccessPlugin = KeychainAccess();

  final applicationName = 'org.gps.flutterKeychainAccessPlugin';
  final exampleUsername = 'username123';
  final examplePassword = 'password123';
  final exampleUpdatedPassword = 'updatedPassword123';
  final exampleAddOrUpdatePassword = 'addOrUpdatePassword123';

  @override
  void initState() {
    super.initState();
    initPlatformState();
  }

  Future<void> _triggerAddPassword(_PluginFunctionResults results,
      {String? application}) async {
    String passwordAddSuccessful;
    try {
      passwordAddSuccessful = await _keychainAccessPlugin.addSecureData(
              exampleUsername, examplePassword,
              application: application)
          ? "Success"
          : "FAILED";
    } on PlatformException catch (e) {
      passwordAddSuccessful = 'Failed to addPassword $e';
    }

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;

    setState(() {
      results.addPasswordStatus.message = passwordAddSuccessful;
      results.addPasswordStatus.status = passwordAddSuccessful == "Success";
    });
  }

  Future<void> _triggerUpdatePassword(_PluginFunctionResults results,
      {String? application}) async {
    String passwordUpdateSuccessful;
    try {
      passwordUpdateSuccessful = await _keychainAccessPlugin.updateSecureData(
              exampleUsername, exampleUpdatedPassword,
              application: application)
          ? "Success"
          : "FAILED";
    } on PlatformException catch (e) {
      passwordUpdateSuccessful = 'Failed to updatePassword $e';
    }

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;

    setState(() {
      results.updatePasswordStatus.message = passwordUpdateSuccessful;
      results.updatePasswordStatus.status =
          passwordUpdateSuccessful == 'Success';
    });
  }

  Future<void> _triggerAddOrUpdatePassword(_PluginFunctionResults results,
      {String? application}) async {
    String addOrUpdatePasswordSuccessful;
    try {
      addOrUpdatePasswordSuccessful = await _keychainAccessPlugin
              .updateSecureData(exampleUsername, exampleAddOrUpdatePassword,
                  application: application)
          ? "Success"
          : "FAILED";
    } on PlatformException catch (e) {
      addOrUpdatePasswordSuccessful = 'Failed to addOrUpdatePassword $e';
    }

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;

    setState(() {
      results.addOrUpdatePasswordStatus.message = addOrUpdatePasswordSuccessful;
      results.addOrUpdatePasswordStatus.status =
          addOrUpdatePasswordSuccessful == 'Success';
    });
  }

  Future<void> _triggerFindPassword(
      _PluginFunctionResults results, String? expected,
      {String? application, String? key}) async {
    String findPasswordSuccessful;
    bool isSuccess = false;
    try {
      key ??= exampleUsername;
      final passwordValue = await _keychainAccessPlugin.findSecureData(key,
          application: application);
      isSuccess = passwordValue == expected;
      findPasswordSuccessful =
          isSuccess ? "$passwordValue" : "Failed";
    } on PlatformException catch (e) {
      findPasswordSuccessful = 'Failed to findPassword $e';
    }

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;

    setState(() {
      final result = _PluginFunctionResult();
      result.message = findPasswordSuccessful;
      result.status = isSuccess;
      results.findPasswordStatuses.add(result);
    });
  }

  Future<void> _triggerDeletePassword(_PluginFunctionResults results,
      {String? application}) async {
    String deletePasswordSuccessful;
    try {
      deletePasswordSuccessful = await _keychainAccessPlugin
              .deleteSecureData(exampleUsername, application: application)
          ? "Success"
          : "Failed";
    } on PlatformException catch (e) {
      deletePasswordSuccessful = 'Failed to deletePassword $e';
    }

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;

    setState(() {
      results.deletePasswordStatus.message = deletePasswordSuccessful;
      results.deletePasswordStatus.status =
          deletePasswordSuccessful == 'Success';
    });
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    // Simple Results.
    await _triggerAddPassword(_simpleResults);
    await _triggerFindPassword(_simpleResults, examplePassword);

    await _triggerUpdatePassword(_simpleResults);
    await _triggerFindPassword(_simpleResults, exampleUpdatedPassword);

    await _triggerAddOrUpdatePassword(_simpleResults);
    await _triggerFindPassword(_simpleResults, exampleAddOrUpdatePassword);

    await _triggerDeletePassword(_simpleResults);
    await _triggerFindPassword(_simpleResults, null, key: "DOES_NOT_EXIST");

    // Results for Application name.
    await _triggerAddPassword(_resultsForApplicationName,
        application: applicationName);
    await _triggerFindPassword(_resultsForApplicationName, examplePassword,
        application: applicationName);

    await _triggerUpdatePassword(_resultsForApplicationName,
        application: applicationName);
    await _triggerFindPassword(
        _resultsForApplicationName, exampleUpdatedPassword,
        application: applicationName);

    await _triggerAddOrUpdatePassword(_resultsForApplicationName,
        application: applicationName);
    await _triggerFindPassword(
        _resultsForApplicationName, exampleAddOrUpdatePassword,
        application: applicationName);

    await _triggerDeletePassword(_resultsForApplicationName,
        application: applicationName);
    await _triggerFindPassword(_resultsForApplicationName, null,
        application: applicationName, key: "DOES_NOT_EXIST");
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
      appBar: AppBar(
        title: const Text('Keychain Access Plugin Example'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Table(
            border: TableBorder.all(),
            // columnWidths: {
            //   0: FixedColumnWidth(400),
            //   1: FixedColumnWidth(400),
            // },
            defaultVerticalAlignment: TableCellVerticalAlignment.middle,
            children: [
              TableRow(
                children: [
                  TableCell(child: columnField('Function Name', heading: true)),
                  TableCell(
                    child: columnField('No Application field', heading: true),
                  ),
                  TableCell(
                    child: columnField('W/ Application', heading: true),
                  ),
                ],
              ),
              TableRow(
                children: [
                  TableCell(
                    child: columnField('addSecureData'),
                  ),
                  TableCell(
                    child: columnField(_buildStatusMessage(
                        [_simpleResults.addPasswordStatus])),
                  ),
                  TableCell(
                    child: columnField(_buildStatusMessage(
                        [_resultsForApplicationName.addPasswordStatus])),
                  ),
                ],
              ),
              TableRow(
                children: [
                  TableCell(
                    child: columnField('updateSecureData'),
                  ),
                  TableCell(
                    child: columnField(_buildStatusMessage(
                        [_simpleResults.updatePasswordStatus])),
                  ),
                  TableCell(
                    child: columnField(_buildStatusMessage(
                        [_resultsForApplicationName.updatePasswordStatus])),
                  ),
                ],
              ),
              TableRow(
                children: [
                  TableCell(
                    child: columnField('addOrUpdateSecureData'),
                  ),
                  TableCell(
                    child: columnField(_buildStatusMessage(
                        [_simpleResults.addOrUpdatePasswordStatus])),
                  ),
                  TableCell(
                    child: columnField(_buildStatusMessage([
                      _resultsForApplicationName.addOrUpdatePasswordStatus
                    ])),
                  ),
                ],
              ),
              TableRow(
                children: [
                  TableCell(
                    child: columnField('findSecureData'),
                  ),
                  TableCell(
                    child: columnField(_buildStatusMessage(
                        _simpleResults.findPasswordStatuses)),
                  ),
                  TableCell(
                    child: columnField(_buildStatusMessage(
                        _resultsForApplicationName.findPasswordStatuses)),
                  ),
                ],
              ),
              TableRow(
                children: [
                  TableCell(
                    child: columnField('deleteSecureData'),
                  ),
                  TableCell(
                    child: columnField(_buildStatusMessage(
                        [_simpleResults.deletePasswordStatus])),
                  ),
                  TableCell(
                    child: columnField(_buildStatusMessage(
                        [_resultsForApplicationName.deletePasswordStatus])),
                  ),
                ],
              ),
            ]),
      ),
    ));
  }

  Widget columnField(String text, {bool heading = false}) {
    TextStyle style = const TextStyle();
    if (heading) {
      style = const TextStyle(fontWeight: FontWeight.bold);
    }
    return Padding(
      padding: const EdgeInsets.all(2.0),
      child: Text(
        text,
        style: style,
      ),
    );
  }

  String _buildStatusMessage(List<_PluginFunctionResult> statuses) {
    if (statuses.isEmpty) {
      return '';
    }
    bool allSuccess = statuses
        .map((e) => e.status)
        .reduce((value, element) => value && element);
    final message = statuses.map((e) => e.message).join('\n');
    if (allSuccess) {
      return '✅$message';
    }
    return '❌$message';
  }
}

class _PluginFunctionResults {
  _PluginFunctionResult addPasswordStatus = _PluginFunctionResult();
  _PluginFunctionResult updatePasswordStatus = _PluginFunctionResult();
  _PluginFunctionResult addOrUpdatePasswordStatus = _PluginFunctionResult();
  final List<_PluginFunctionResult> findPasswordStatuses = [];
  _PluginFunctionResult deletePasswordStatus = _PluginFunctionResult();
}

class _PluginFunctionResult {
  bool status = false;
  String message = 'Unknown';
}
1
likes
160
points
46
downloads

Publisher

unverified uploader

Weekly Downloads

Flutter plugin to access Keychain Access apis on MacOS and iOS. Provides a secure way to store and retrieve secure content from the keychain

Repository (GitHub)

Topics

#security #password #keychain #macos #apple

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on keychain_access