mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2024-12-25 07:39:25 -05:00
identity work
This commit is contained in:
parent
9eff8f0cb4
commit
ac58e1dea3
@ -1,3 +1,4 @@
|
||||
export 'identity.dart';
|
||||
export 'local_account.dart';
|
||||
export 'preferences.dart';
|
||||
export 'user_login.dart';
|
||||
|
@ -7,7 +7,8 @@ part 'identity.g.dart';
|
||||
// Identity Key points to accounts associated with this identity
|
||||
// accounts field has a map of service name or uuid to account key pairs
|
||||
// DHT Schema: DFLT(1)
|
||||
// DHT Key (Private): identityPublicKey
|
||||
// DHT Key (Private): identityRecordKey
|
||||
// DHT Owner Key: identityPublicKey
|
||||
// DHT Secret: identitySecretKey (stored encrypted with unlock code in local table store)
|
||||
@freezed
|
||||
class Identity with _$Identity {
|
||||
@ -25,21 +26,43 @@ class Identity with _$Identity {
|
||||
// Bidirectional Master<->Identity signature allows for
|
||||
// chain of identity ownership for account recovery process
|
||||
//
|
||||
// Backed by a DHT key at masterPublicKey, the secret is kept
|
||||
// Backed by a DHT key at masterRecordKey, the secret is kept
|
||||
// completely offline and only written to upon account recovery
|
||||
//
|
||||
// DHT Schema: DFLT(1)
|
||||
// DHT Key (Public): masterPublicKey
|
||||
// DHT Secret: masterSecretKey (kept offline)
|
||||
// DHT Record Key (Public): masterRecordKey
|
||||
// DHT Owner Key: masterPublicKey
|
||||
// DHT Owner Secret: masterSecretKey (kept offline)
|
||||
// Encryption: None
|
||||
@freezed
|
||||
class IdentityMaster with _$IdentityMaster {
|
||||
const factory IdentityMaster(
|
||||
{required TypedKey identityPublicKey,
|
||||
required TypedKey masterPublicKey,
|
||||
{
|
||||
// Private DHT record storing identity account mapping
|
||||
required TypedKey identityRecordKey,
|
||||
// Public key of identity
|
||||
required PublicKey identityPublicKey,
|
||||
// Public DHT record storing this structure for account recovery
|
||||
required TypedKey masterRecordKey,
|
||||
// Public key of master identity used to sign identity keys for recovery
|
||||
required PublicKey masterPublicKey,
|
||||
// Signature of identityRecordKey and identityPublicKey by masterPublicKey
|
||||
required Signature identitySignature,
|
||||
// Signature of masterRecordKey and masterPublicKey by identityPublicKey
|
||||
required Signature masterSignature}) = _IdentityMaster;
|
||||
|
||||
factory IdentityMaster.fromJson(Map<String, dynamic> json) =>
|
||||
_$IdentityMasterFromJson(json);
|
||||
}
|
||||
|
||||
// Identity Master with secrets
|
||||
// Not freezed because we never persist this class in its entirety
|
||||
class IdentityMasterWithSecrets {
|
||||
IdentityMaster identityMaster;
|
||||
SecretKey masterSecret;
|
||||
SecretKey identitySecret;
|
||||
IdentityMasterWithSecrets(
|
||||
{required this.identityMaster,
|
||||
required this.masterSecret,
|
||||
required this.identitySecret});
|
||||
}
|
||||
|
@ -166,12 +166,17 @@ IdentityMaster _$IdentityMasterFromJson(Map<String, dynamic> json) {
|
||||
|
||||
/// @nodoc
|
||||
mixin _$IdentityMaster {
|
||||
Typed<FixedEncodedString43> get identityPublicKey =>
|
||||
throw _privateConstructorUsedError;
|
||||
Typed<FixedEncodedString43> get masterPublicKey =>
|
||||
throw _privateConstructorUsedError;
|
||||
// Private DHT record storing identity account mapping
|
||||
Typed<FixedEncodedString43> get identityRecordKey =>
|
||||
throw _privateConstructorUsedError; // Public key of identity
|
||||
FixedEncodedString43 get identityPublicKey =>
|
||||
throw _privateConstructorUsedError; // Public DHT record storing this structure for account recovery
|
||||
Typed<FixedEncodedString43> get masterRecordKey =>
|
||||
throw _privateConstructorUsedError; // Public key of master identity used to sign identity keys for recovery
|
||||
FixedEncodedString43 get masterPublicKey =>
|
||||
throw _privateConstructorUsedError; // Signature of identityRecordKey and identityPublicKey by masterPublicKey
|
||||
FixedEncodedString86 get identitySignature =>
|
||||
throw _privateConstructorUsedError;
|
||||
throw _privateConstructorUsedError; // Signature of masterRecordKey and masterPublicKey by identityPublicKey
|
||||
FixedEncodedString86 get masterSignature =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
@ -188,8 +193,10 @@ abstract class $IdentityMasterCopyWith<$Res> {
|
||||
_$IdentityMasterCopyWithImpl<$Res, IdentityMaster>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{Typed<FixedEncodedString43> identityPublicKey,
|
||||
Typed<FixedEncodedString43> masterPublicKey,
|
||||
{Typed<FixedEncodedString43> identityRecordKey,
|
||||
FixedEncodedString43 identityPublicKey,
|
||||
Typed<FixedEncodedString43> masterRecordKey,
|
||||
FixedEncodedString43 masterPublicKey,
|
||||
FixedEncodedString86 identitySignature,
|
||||
FixedEncodedString86 masterSignature});
|
||||
}
|
||||
@ -207,20 +214,30 @@ class _$IdentityMasterCopyWithImpl<$Res, $Val extends IdentityMaster>
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? identityRecordKey = null,
|
||||
Object? identityPublicKey = null,
|
||||
Object? masterRecordKey = null,
|
||||
Object? masterPublicKey = null,
|
||||
Object? identitySignature = null,
|
||||
Object? masterSignature = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
identityRecordKey: null == identityRecordKey
|
||||
? _value.identityRecordKey
|
||||
: identityRecordKey // ignore: cast_nullable_to_non_nullable
|
||||
as Typed<FixedEncodedString43>,
|
||||
identityPublicKey: null == identityPublicKey
|
||||
? _value.identityPublicKey
|
||||
: identityPublicKey // ignore: cast_nullable_to_non_nullable
|
||||
as FixedEncodedString43,
|
||||
masterRecordKey: null == masterRecordKey
|
||||
? _value.masterRecordKey
|
||||
: masterRecordKey // ignore: cast_nullable_to_non_nullable
|
||||
as Typed<FixedEncodedString43>,
|
||||
masterPublicKey: null == masterPublicKey
|
||||
? _value.masterPublicKey
|
||||
: masterPublicKey // ignore: cast_nullable_to_non_nullable
|
||||
as Typed<FixedEncodedString43>,
|
||||
as FixedEncodedString43,
|
||||
identitySignature: null == identitySignature
|
||||
? _value.identitySignature
|
||||
: identitySignature // ignore: cast_nullable_to_non_nullable
|
||||
@ -242,8 +259,10 @@ abstract class _$$_IdentityMasterCopyWith<$Res>
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{Typed<FixedEncodedString43> identityPublicKey,
|
||||
Typed<FixedEncodedString43> masterPublicKey,
|
||||
{Typed<FixedEncodedString43> identityRecordKey,
|
||||
FixedEncodedString43 identityPublicKey,
|
||||
Typed<FixedEncodedString43> masterRecordKey,
|
||||
FixedEncodedString43 masterPublicKey,
|
||||
FixedEncodedString86 identitySignature,
|
||||
FixedEncodedString86 masterSignature});
|
||||
}
|
||||
@ -259,20 +278,30 @@ class __$$_IdentityMasterCopyWithImpl<$Res>
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? identityRecordKey = null,
|
||||
Object? identityPublicKey = null,
|
||||
Object? masterRecordKey = null,
|
||||
Object? masterPublicKey = null,
|
||||
Object? identitySignature = null,
|
||||
Object? masterSignature = null,
|
||||
}) {
|
||||
return _then(_$_IdentityMaster(
|
||||
identityRecordKey: null == identityRecordKey
|
||||
? _value.identityRecordKey
|
||||
: identityRecordKey // ignore: cast_nullable_to_non_nullable
|
||||
as Typed<FixedEncodedString43>,
|
||||
identityPublicKey: null == identityPublicKey
|
||||
? _value.identityPublicKey
|
||||
: identityPublicKey // ignore: cast_nullable_to_non_nullable
|
||||
as FixedEncodedString43,
|
||||
masterRecordKey: null == masterRecordKey
|
||||
? _value.masterRecordKey
|
||||
: masterRecordKey // ignore: cast_nullable_to_non_nullable
|
||||
as Typed<FixedEncodedString43>,
|
||||
masterPublicKey: null == masterPublicKey
|
||||
? _value.masterPublicKey
|
||||
: masterPublicKey // ignore: cast_nullable_to_non_nullable
|
||||
as Typed<FixedEncodedString43>,
|
||||
as FixedEncodedString43,
|
||||
identitySignature: null == identitySignature
|
||||
? _value.identitySignature
|
||||
: identitySignature // ignore: cast_nullable_to_non_nullable
|
||||
@ -289,7 +318,9 @@ class __$$_IdentityMasterCopyWithImpl<$Res>
|
||||
@JsonSerializable()
|
||||
class _$_IdentityMaster implements _IdentityMaster {
|
||||
const _$_IdentityMaster(
|
||||
{required this.identityPublicKey,
|
||||
{required this.identityRecordKey,
|
||||
required this.identityPublicKey,
|
||||
required this.masterRecordKey,
|
||||
required this.masterPublicKey,
|
||||
required this.identitySignature,
|
||||
required this.masterSignature});
|
||||
@ -297,18 +328,28 @@ class _$_IdentityMaster implements _IdentityMaster {
|
||||
factory _$_IdentityMaster.fromJson(Map<String, dynamic> json) =>
|
||||
_$$_IdentityMasterFromJson(json);
|
||||
|
||||
// Private DHT record storing identity account mapping
|
||||
@override
|
||||
final Typed<FixedEncodedString43> identityPublicKey;
|
||||
final Typed<FixedEncodedString43> identityRecordKey;
|
||||
// Public key of identity
|
||||
@override
|
||||
final Typed<FixedEncodedString43> masterPublicKey;
|
||||
final FixedEncodedString43 identityPublicKey;
|
||||
// Public DHT record storing this structure for account recovery
|
||||
@override
|
||||
final Typed<FixedEncodedString43> masterRecordKey;
|
||||
// Public key of master identity used to sign identity keys for recovery
|
||||
@override
|
||||
final FixedEncodedString43 masterPublicKey;
|
||||
// Signature of identityRecordKey and identityPublicKey by masterPublicKey
|
||||
@override
|
||||
final FixedEncodedString86 identitySignature;
|
||||
// Signature of masterRecordKey and masterPublicKey by identityPublicKey
|
||||
@override
|
||||
final FixedEncodedString86 masterSignature;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'IdentityMaster(identityPublicKey: $identityPublicKey, masterPublicKey: $masterPublicKey, identitySignature: $identitySignature, masterSignature: $masterSignature)';
|
||||
return 'IdentityMaster(identityRecordKey: $identityRecordKey, identityPublicKey: $identityPublicKey, masterRecordKey: $masterRecordKey, masterPublicKey: $masterPublicKey, identitySignature: $identitySignature, masterSignature: $masterSignature)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -316,8 +357,12 @@ class _$_IdentityMaster implements _IdentityMaster {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$_IdentityMaster &&
|
||||
(identical(other.identityRecordKey, identityRecordKey) ||
|
||||
other.identityRecordKey == identityRecordKey) &&
|
||||
(identical(other.identityPublicKey, identityPublicKey) ||
|
||||
other.identityPublicKey == identityPublicKey) &&
|
||||
(identical(other.masterRecordKey, masterRecordKey) ||
|
||||
other.masterRecordKey == masterRecordKey) &&
|
||||
(identical(other.masterPublicKey, masterPublicKey) ||
|
||||
other.masterPublicKey == masterPublicKey) &&
|
||||
(identical(other.identitySignature, identitySignature) ||
|
||||
@ -328,8 +373,14 @@ class _$_IdentityMaster implements _IdentityMaster {
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, identityPublicKey,
|
||||
masterPublicKey, identitySignature, masterSignature);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
identityRecordKey,
|
||||
identityPublicKey,
|
||||
masterRecordKey,
|
||||
masterPublicKey,
|
||||
identitySignature,
|
||||
masterSignature);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@ -347,21 +398,27 @@ class _$_IdentityMaster implements _IdentityMaster {
|
||||
|
||||
abstract class _IdentityMaster implements IdentityMaster {
|
||||
const factory _IdentityMaster(
|
||||
{required final Typed<FixedEncodedString43> identityPublicKey,
|
||||
required final Typed<FixedEncodedString43> masterPublicKey,
|
||||
{required final Typed<FixedEncodedString43> identityRecordKey,
|
||||
required final FixedEncodedString43 identityPublicKey,
|
||||
required final Typed<FixedEncodedString43> masterRecordKey,
|
||||
required final FixedEncodedString43 masterPublicKey,
|
||||
required final FixedEncodedString86 identitySignature,
|
||||
required final FixedEncodedString86 masterSignature}) = _$_IdentityMaster;
|
||||
|
||||
factory _IdentityMaster.fromJson(Map<String, dynamic> json) =
|
||||
_$_IdentityMaster.fromJson;
|
||||
|
||||
@override
|
||||
Typed<FixedEncodedString43> get identityPublicKey;
|
||||
@override
|
||||
Typed<FixedEncodedString43> get masterPublicKey;
|
||||
@override
|
||||
@override // Private DHT record storing identity account mapping
|
||||
Typed<FixedEncodedString43> get identityRecordKey;
|
||||
@override // Public key of identity
|
||||
FixedEncodedString43 get identityPublicKey;
|
||||
@override // Public DHT record storing this structure for account recovery
|
||||
Typed<FixedEncodedString43> get masterRecordKey;
|
||||
@override // Public key of master identity used to sign identity keys for recovery
|
||||
FixedEncodedString43 get masterPublicKey;
|
||||
@override // Signature of identityRecordKey and identityPublicKey by masterPublicKey
|
||||
FixedEncodedString86 get identitySignature;
|
||||
@override
|
||||
@override // Signature of masterRecordKey and masterPublicKey by identityPublicKey
|
||||
FixedEncodedString86 get masterSignature;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
|
@ -20,10 +20,13 @@ Map<String, dynamic> _$$_IdentityToJson(_$_Identity instance) =>
|
||||
|
||||
_$_IdentityMaster _$$_IdentityMasterFromJson(Map<String, dynamic> json) =>
|
||||
_$_IdentityMaster(
|
||||
identityRecordKey:
|
||||
Typed<FixedEncodedString43>.fromJson(json['identity_record_key']),
|
||||
identityPublicKey:
|
||||
Typed<FixedEncodedString43>.fromJson(json['identity_public_key']),
|
||||
masterPublicKey:
|
||||
Typed<FixedEncodedString43>.fromJson(json['master_public_key']),
|
||||
FixedEncodedString43.fromJson(json['identity_public_key']),
|
||||
masterRecordKey:
|
||||
Typed<FixedEncodedString43>.fromJson(json['master_record_key']),
|
||||
masterPublicKey: FixedEncodedString43.fromJson(json['master_public_key']),
|
||||
identitySignature:
|
||||
FixedEncodedString86.fromJson(json['identity_signature']),
|
||||
masterSignature: FixedEncodedString86.fromJson(json['master_signature']),
|
||||
@ -31,7 +34,9 @@ _$_IdentityMaster _$$_IdentityMasterFromJson(Map<String, dynamic> json) =>
|
||||
|
||||
Map<String, dynamic> _$$_IdentityMasterToJson(_$_IdentityMaster instance) =>
|
||||
<String, dynamic>{
|
||||
'identity_record_key': instance.identityRecordKey.toJson(),
|
||||
'identity_public_key': instance.identityPublicKey.toJson(),
|
||||
'master_record_key': instance.masterRecordKey.toJson(),
|
||||
'master_public_key': instance.masterPublicKey.toJson(),
|
||||
'identity_signature': instance.identitySignature.toJson(),
|
||||
'master_signature': instance.masterSignature.toJson(),
|
||||
|
@ -2,6 +2,7 @@ import 'dart:typed_data';
|
||||
|
||||
import 'package:change_case/change_case.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import 'package:veilid/veilid.dart';
|
||||
import 'identity.dart';
|
||||
|
||||
|
42
lib/entities/user_login.dart
Normal file
42
lib/entities/user_login.dart
Normal file
@ -0,0 +1,42 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:veilid/veilid.dart';
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
|
||||
part 'user_login.freezed.dart';
|
||||
part 'user_login.g.dart';
|
||||
|
||||
// Represents a currently logged in account
|
||||
// User logins are stored in the user_logins tablestore table
|
||||
// indexed by the accountMasterKey
|
||||
@freezed
|
||||
class UserLogin with _$UserLogin {
|
||||
const factory UserLogin({
|
||||
// Master public key for the user used to index the local accounts table
|
||||
required TypedKey accountMasterKey,
|
||||
// The identity secret as unlocked from the local accounts table
|
||||
required TypedSecret secretKey,
|
||||
// The time this login was most recently used
|
||||
required Timestamp lastActive,
|
||||
}) = _UserLogin;
|
||||
|
||||
factory UserLogin.fromJson(Map<String, dynamic> json) =>
|
||||
_$UserLoginFromJson(json);
|
||||
}
|
||||
|
||||
// Represents a set of user logins
|
||||
// and the currently selected account
|
||||
@freezed
|
||||
class ActiveLogins with _$ActiveLogins {
|
||||
const factory ActiveLogins({
|
||||
// The list of current logged in accounts
|
||||
required IList<UserLogin> userLogins,
|
||||
// The current selected account indexed by master key
|
||||
TypedKey? activeUserLogin,
|
||||
}) = _ActiveLogins;
|
||||
|
||||
factory ActiveLogins.empty() =>
|
||||
const ActiveLogins(userLogins: IListConst([]));
|
||||
|
||||
factory ActiveLogins.fromJson(Map<String, dynamic> json) =>
|
||||
_$ActiveLoginsFromJson(json);
|
||||
}
|
369
lib/entities/user_login.freezed.dart
Normal file
369
lib/entities/user_login.freezed.dart
Normal file
@ -0,0 +1,369 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'user_login.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
||||
|
||||
UserLogin _$UserLoginFromJson(Map<String, dynamic> json) {
|
||||
return _UserLogin.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$UserLogin {
|
||||
// Master public key for the user used to index the local accounts table
|
||||
Typed<FixedEncodedString43> get accountMasterKey =>
|
||||
throw _privateConstructorUsedError; // The identity secret as unlocked from the local accounts table
|
||||
Typed<FixedEncodedString43> get secretKey =>
|
||||
throw _privateConstructorUsedError; // The time this login was most recently used
|
||||
Timestamp get lastActive => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$UserLoginCopyWith<UserLogin> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $UserLoginCopyWith<$Res> {
|
||||
factory $UserLoginCopyWith(UserLogin value, $Res Function(UserLogin) then) =
|
||||
_$UserLoginCopyWithImpl<$Res, UserLogin>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{Typed<FixedEncodedString43> accountMasterKey,
|
||||
Typed<FixedEncodedString43> secretKey,
|
||||
Timestamp lastActive});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$UserLoginCopyWithImpl<$Res, $Val extends UserLogin>
|
||||
implements $UserLoginCopyWith<$Res> {
|
||||
_$UserLoginCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? accountMasterKey = null,
|
||||
Object? secretKey = null,
|
||||
Object? lastActive = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
accountMasterKey: null == accountMasterKey
|
||||
? _value.accountMasterKey
|
||||
: accountMasterKey // ignore: cast_nullable_to_non_nullable
|
||||
as Typed<FixedEncodedString43>,
|
||||
secretKey: null == secretKey
|
||||
? _value.secretKey
|
||||
: secretKey // ignore: cast_nullable_to_non_nullable
|
||||
as Typed<FixedEncodedString43>,
|
||||
lastActive: null == lastActive
|
||||
? _value.lastActive
|
||||
: lastActive // ignore: cast_nullable_to_non_nullable
|
||||
as Timestamp,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$_UserLoginCopyWith<$Res> implements $UserLoginCopyWith<$Res> {
|
||||
factory _$$_UserLoginCopyWith(
|
||||
_$_UserLogin value, $Res Function(_$_UserLogin) then) =
|
||||
__$$_UserLoginCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{Typed<FixedEncodedString43> accountMasterKey,
|
||||
Typed<FixedEncodedString43> secretKey,
|
||||
Timestamp lastActive});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$_UserLoginCopyWithImpl<$Res>
|
||||
extends _$UserLoginCopyWithImpl<$Res, _$_UserLogin>
|
||||
implements _$$_UserLoginCopyWith<$Res> {
|
||||
__$$_UserLoginCopyWithImpl(
|
||||
_$_UserLogin _value, $Res Function(_$_UserLogin) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? accountMasterKey = null,
|
||||
Object? secretKey = null,
|
||||
Object? lastActive = null,
|
||||
}) {
|
||||
return _then(_$_UserLogin(
|
||||
accountMasterKey: null == accountMasterKey
|
||||
? _value.accountMasterKey
|
||||
: accountMasterKey // ignore: cast_nullable_to_non_nullable
|
||||
as Typed<FixedEncodedString43>,
|
||||
secretKey: null == secretKey
|
||||
? _value.secretKey
|
||||
: secretKey // ignore: cast_nullable_to_non_nullable
|
||||
as Typed<FixedEncodedString43>,
|
||||
lastActive: null == lastActive
|
||||
? _value.lastActive
|
||||
: lastActive // ignore: cast_nullable_to_non_nullable
|
||||
as Timestamp,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$_UserLogin implements _UserLogin {
|
||||
const _$_UserLogin(
|
||||
{required this.accountMasterKey,
|
||||
required this.secretKey,
|
||||
required this.lastActive});
|
||||
|
||||
factory _$_UserLogin.fromJson(Map<String, dynamic> json) =>
|
||||
_$$_UserLoginFromJson(json);
|
||||
|
||||
// Master public key for the user used to index the local accounts table
|
||||
@override
|
||||
final Typed<FixedEncodedString43> accountMasterKey;
|
||||
// The identity secret as unlocked from the local accounts table
|
||||
@override
|
||||
final Typed<FixedEncodedString43> secretKey;
|
||||
// The time this login was most recently used
|
||||
@override
|
||||
final Timestamp lastActive;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'UserLogin(accountMasterKey: $accountMasterKey, secretKey: $secretKey, lastActive: $lastActive)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$_UserLogin &&
|
||||
(identical(other.accountMasterKey, accountMasterKey) ||
|
||||
other.accountMasterKey == accountMasterKey) &&
|
||||
(identical(other.secretKey, secretKey) ||
|
||||
other.secretKey == secretKey) &&
|
||||
(identical(other.lastActive, lastActive) ||
|
||||
other.lastActive == lastActive));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, accountMasterKey, secretKey, lastActive);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$_UserLoginCopyWith<_$_UserLogin> get copyWith =>
|
||||
__$$_UserLoginCopyWithImpl<_$_UserLogin>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$_UserLoginToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _UserLogin implements UserLogin {
|
||||
const factory _UserLogin(
|
||||
{required final Typed<FixedEncodedString43> accountMasterKey,
|
||||
required final Typed<FixedEncodedString43> secretKey,
|
||||
required final Timestamp lastActive}) = _$_UserLogin;
|
||||
|
||||
factory _UserLogin.fromJson(Map<String, dynamic> json) =
|
||||
_$_UserLogin.fromJson;
|
||||
|
||||
@override // Master public key for the user used to index the local accounts table
|
||||
Typed<FixedEncodedString43> get accountMasterKey;
|
||||
@override // The identity secret as unlocked from the local accounts table
|
||||
Typed<FixedEncodedString43> get secretKey;
|
||||
@override // The time this login was most recently used
|
||||
Timestamp get lastActive;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$_UserLoginCopyWith<_$_UserLogin> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
ActiveLogins _$ActiveLoginsFromJson(Map<String, dynamic> json) {
|
||||
return _ActiveLogins.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ActiveLogins {
|
||||
// The list of current logged in accounts
|
||||
IList<UserLogin> get userLogins =>
|
||||
throw _privateConstructorUsedError; // The current selected account indexed by master key
|
||||
Typed<FixedEncodedString43>? get activeUserLogin =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$ActiveLoginsCopyWith<ActiveLogins> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ActiveLoginsCopyWith<$Res> {
|
||||
factory $ActiveLoginsCopyWith(
|
||||
ActiveLogins value, $Res Function(ActiveLogins) then) =
|
||||
_$ActiveLoginsCopyWithImpl<$Res, ActiveLogins>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{IList<UserLogin> userLogins,
|
||||
Typed<FixedEncodedString43>? activeUserLogin});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ActiveLoginsCopyWithImpl<$Res, $Val extends ActiveLogins>
|
||||
implements $ActiveLoginsCopyWith<$Res> {
|
||||
_$ActiveLoginsCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? userLogins = null,
|
||||
Object? activeUserLogin = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
userLogins: null == userLogins
|
||||
? _value.userLogins
|
||||
: userLogins // ignore: cast_nullable_to_non_nullable
|
||||
as IList<UserLogin>,
|
||||
activeUserLogin: freezed == activeUserLogin
|
||||
? _value.activeUserLogin
|
||||
: activeUserLogin // ignore: cast_nullable_to_non_nullable
|
||||
as Typed<FixedEncodedString43>?,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$_ActiveLoginsCopyWith<$Res>
|
||||
implements $ActiveLoginsCopyWith<$Res> {
|
||||
factory _$$_ActiveLoginsCopyWith(
|
||||
_$_ActiveLogins value, $Res Function(_$_ActiveLogins) then) =
|
||||
__$$_ActiveLoginsCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{IList<UserLogin> userLogins,
|
||||
Typed<FixedEncodedString43>? activeUserLogin});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$_ActiveLoginsCopyWithImpl<$Res>
|
||||
extends _$ActiveLoginsCopyWithImpl<$Res, _$_ActiveLogins>
|
||||
implements _$$_ActiveLoginsCopyWith<$Res> {
|
||||
__$$_ActiveLoginsCopyWithImpl(
|
||||
_$_ActiveLogins _value, $Res Function(_$_ActiveLogins) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? userLogins = null,
|
||||
Object? activeUserLogin = freezed,
|
||||
}) {
|
||||
return _then(_$_ActiveLogins(
|
||||
userLogins: null == userLogins
|
||||
? _value.userLogins
|
||||
: userLogins // ignore: cast_nullable_to_non_nullable
|
||||
as IList<UserLogin>,
|
||||
activeUserLogin: freezed == activeUserLogin
|
||||
? _value.activeUserLogin
|
||||
: activeUserLogin // ignore: cast_nullable_to_non_nullable
|
||||
as Typed<FixedEncodedString43>?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$_ActiveLogins implements _ActiveLogins {
|
||||
const _$_ActiveLogins({required this.userLogins, this.activeUserLogin});
|
||||
|
||||
factory _$_ActiveLogins.fromJson(Map<String, dynamic> json) =>
|
||||
_$$_ActiveLoginsFromJson(json);
|
||||
|
||||
// The list of current logged in accounts
|
||||
@override
|
||||
final IList<UserLogin> userLogins;
|
||||
// The current selected account indexed by master key
|
||||
@override
|
||||
final Typed<FixedEncodedString43>? activeUserLogin;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ActiveLogins(userLogins: $userLogins, activeUserLogin: $activeUserLogin)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$_ActiveLogins &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.userLogins, userLogins) &&
|
||||
(identical(other.activeUserLogin, activeUserLogin) ||
|
||||
other.activeUserLogin == activeUserLogin));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,
|
||||
const DeepCollectionEquality().hash(userLogins), activeUserLogin);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$_ActiveLoginsCopyWith<_$_ActiveLogins> get copyWith =>
|
||||
__$$_ActiveLoginsCopyWithImpl<_$_ActiveLogins>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$_ActiveLoginsToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _ActiveLogins implements ActiveLogins {
|
||||
const factory _ActiveLogins(
|
||||
{required final IList<UserLogin> userLogins,
|
||||
final Typed<FixedEncodedString43>? activeUserLogin}) = _$_ActiveLogins;
|
||||
|
||||
factory _ActiveLogins.fromJson(Map<String, dynamic> json) =
|
||||
_$_ActiveLogins.fromJson;
|
||||
|
||||
@override // The list of current logged in accounts
|
||||
IList<UserLogin> get userLogins;
|
||||
@override // The current selected account indexed by master key
|
||||
Typed<FixedEncodedString43>? get activeUserLogin;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$_ActiveLoginsCopyWith<_$_ActiveLogins> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
38
lib/entities/user_login.g.dart
Normal file
38
lib/entities/user_login.g.dart
Normal file
@ -0,0 +1,38 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'user_login.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$_UserLogin _$$_UserLoginFromJson(Map<String, dynamic> json) => _$_UserLogin(
|
||||
accountMasterKey:
|
||||
Typed<FixedEncodedString43>.fromJson(json['account_master_key']),
|
||||
secretKey: Typed<FixedEncodedString43>.fromJson(json['secret_key']),
|
||||
lastActive: Timestamp.fromJson(json['last_active']),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$_UserLoginToJson(_$_UserLogin instance) =>
|
||||
<String, dynamic>{
|
||||
'account_master_key': instance.accountMasterKey.toJson(),
|
||||
'secret_key': instance.secretKey.toJson(),
|
||||
'last_active': instance.lastActive.toJson(),
|
||||
};
|
||||
|
||||
_$_ActiveLogins _$$_ActiveLoginsFromJson(Map<String, dynamic> json) =>
|
||||
_$_ActiveLogins(
|
||||
userLogins: IList<UserLogin>.fromJson(json['user_logins'],
|
||||
(value) => UserLogin.fromJson(value as Map<String, dynamic>)),
|
||||
activeUserLogin: json['active_user_login'] == null
|
||||
? null
|
||||
: Typed<FixedEncodedString43>.fromJson(json['active_user_login']),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$_ActiveLoginsToJson(_$_ActiveLogins instance) =>
|
||||
<String, dynamic>{
|
||||
'user_logins': instance.userLogins.toJson(
|
||||
(value) => value.toJson(),
|
||||
),
|
||||
'active_user_login': instance.activeUserLogin?.toJson(),
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '../state/auth.dart';
|
||||
import '../state/active_logins_state.dart';
|
||||
|
||||
class LoginPage extends ConsumerWidget {
|
||||
const LoginPage({super.key});
|
||||
|
@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../pages/pages.dart';
|
||||
import '../state/auth.dart';
|
||||
import '../state/active_logins_state.dart';
|
||||
|
||||
/// This notifier is meant to implement the [Listenable] our [GoRouter] needs.
|
||||
///
|
||||
|
107
lib/state/active_logins_state.dart
Normal file
107
lib/state/active_logins_state.dart
Normal file
@ -0,0 +1,107 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:veilid/veilid.dart';
|
||||
import '../entities/entities.dart';
|
||||
|
||||
part 'active_logins_state.g.dart';
|
||||
|
||||
@riverpod
|
||||
class ActiveLoginsState extends _$ActiveLoginsState {
|
||||
VeilidTableDB? _userLoginsTable;
|
||||
ActiveLogins _activeLogins;
|
||||
|
||||
ActiveLoginsState() : _activeLogins = ActiveLogins.empty();
|
||||
|
||||
@override
|
||||
FutureOr<ActiveLogins> build() async {
|
||||
_userLoginsTable ??= await Veilid.instance.openTableDB("login_state", 1);
|
||||
_activeLogins =
|
||||
(await _userLoginsTable!.loadStringJson(0, "active_logins") ??
|
||||
ActiveLogins.empty()) as ActiveLogins;
|
||||
_persistenceRefreshLogic();
|
||||
return _activeLogins;
|
||||
}
|
||||
|
||||
/// Log out of active user
|
||||
Future<void> logout() async {
|
||||
// If no user is active, then logout does nothing
|
||||
if (_activeLogins.activeUserLogin == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove userlogin and set the active user to logged out
|
||||
final newUserLogins = _activeLogins.userLogins.removeWhere(
|
||||
(ul) => _activeLogins.activeUserLogin == ul.accountMasterKey);
|
||||
_activeLogins = _activeLogins.copyWith(
|
||||
activeUserLogin: null, userLogins: newUserLogins);
|
||||
|
||||
// Report changed state
|
||||
state = AsyncValue.data(_activeLogins);
|
||||
}
|
||||
|
||||
/// Log all users
|
||||
Future<void> logoutAll() async {
|
||||
// If no user is active, then logout does nothing
|
||||
if (_activeLogins.activeUserLogin == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove all userlogins and set the active user to logged out
|
||||
_activeLogins = ActiveLogins.empty();
|
||||
|
||||
// Report changed state
|
||||
state = AsyncValue.data(_activeLogins);
|
||||
}
|
||||
|
||||
/// Log out specific user identified by its master public key
|
||||
Future<void> logoutUser(TypedKey user) async {
|
||||
// Remove userlogin and set the active user to logged out
|
||||
final newUserLogins = _activeLogins.userLogins
|
||||
.removeWhere((ul) => user == ul.accountMasterKey);
|
||||
final newActiveUserLogin = _activeLogins.activeUserLogin == user
|
||||
? null
|
||||
: _activeLogins.activeUserLogin;
|
||||
_activeLogins = ActiveLogins(
|
||||
userLogins: newUserLogins, activeUserLogin: newActiveUserLogin);
|
||||
|
||||
// Report changed state
|
||||
state = AsyncValue.data(_activeLogins);
|
||||
}
|
||||
|
||||
/// Attempt a login and if successful make that user active
|
||||
Future<void> login(String publicKey, String password) async {
|
||||
state = await AsyncValue.guard<User?>(() async {
|
||||
return Future.delayed(
|
||||
networkRoundTripTime,
|
||||
() => _dummyUser,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Internal method used to listen authentication state changes.
|
||||
/// When the auth object is in a loading state, nothing happens.
|
||||
/// When the auth object is in a error state, we choose to remove the token
|
||||
/// Otherwise, we expect the current auth value to be reflected in our persistence API
|
||||
void _persistenceRefreshLogic() {
|
||||
ref.listenSelf((_, next) {
|
||||
if (next.isLoading) return;
|
||||
if (next.hasError) {
|
||||
sharedPreferences.remove(_sharedPrefsKey);
|
||||
return;
|
||||
}
|
||||
|
||||
final val = next.requireValue;
|
||||
final isAuthenticated = val == null;
|
||||
|
||||
isAuthenticated
|
||||
? sharedPreferences.remove(_sharedPrefsKey)
|
||||
: sharedPreferences.setString(_sharedPrefsKey, val.publicKey);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class UnauthorizedException implements Exception {
|
||||
final String message;
|
||||
const UnauthorizedException(this.message);
|
||||
}
|
25
lib/state/active_logins_state.g.dart
Normal file
25
lib/state/active_logins_state.g.dart
Normal file
@ -0,0 +1,25 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'active_logins_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$activeLoginsStateHash() => r'9b8795055e21f15f8fbf13534365725591311cf4';
|
||||
|
||||
/// See also [ActiveLoginsState].
|
||||
@ProviderFor(ActiveLoginsState)
|
||||
final activeLoginsStateProvider =
|
||||
AutoDisposeAsyncNotifierProvider<ActiveLoginsState, ActiveLogins>.internal(
|
||||
ActiveLoginsState.new,
|
||||
name: r'activeLoginsStateProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$activeLoginsStateHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$ActiveLoginsState = AutoDisposeAsyncNotifier<ActiveLogins>;
|
||||
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
|
@ -1,110 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class User {
|
||||
final String publicKey;
|
||||
final String secretKey;
|
||||
const User(this.publicKey, this.secretKey);
|
||||
}
|
||||
|
||||
/// A mock of an Authenticated User
|
||||
const _dummyUser = User("", "");
|
||||
|
||||
/// XXXX THIS IS TOTALLY BOGUS FOR NOW
|
||||
/// This notifier holds and handles the authentication state of the application
|
||||
class AuthNotifier extends AutoDisposeAsyncNotifier<User?> {
|
||||
late SharedPreferences sharedPreferences;
|
||||
static const _sharedPrefsKey = 'token';
|
||||
|
||||
/// Mock of the duration of a network request
|
||||
@override
|
||||
FutureOr<User?> build() async {
|
||||
sharedPreferences = await SharedPreferences.getInstance();
|
||||
|
||||
_persistenceRefreshLogic();
|
||||
|
||||
return await _loginRecoveryAttempt();
|
||||
}
|
||||
|
||||
/// Tries to perform a login with the saved token on the persistant storage.
|
||||
/// If _anything_ goes wrong, deletes the internal token and returns a [User.signedOut].
|
||||
Future<User?> _loginRecoveryAttempt() async {
|
||||
try {
|
||||
final savedToken = sharedPreferences.getString(_sharedPrefsKey);
|
||||
if (savedToken == null) {
|
||||
throw const UnauthorizedException(
|
||||
"Couldn't find the authentication token");
|
||||
}
|
||||
|
||||
return await _loginWithToken(savedToken);
|
||||
} catch (_, __) {
|
||||
await sharedPreferences.remove(_sharedPrefsKey);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Mock of a request performed on logout (might be common, or not, whatevs).
|
||||
Future<void> logout() async {
|
||||
await Future.delayed(networkRoundTripTime);
|
||||
state = const AsyncValue.data(null);
|
||||
}
|
||||
|
||||
/// Mock of a successful login attempt, which results come from the network.
|
||||
Future<void> login(String publicKey, String password) async {
|
||||
state = await AsyncValue.guard<User?>(() async {
|
||||
return Future.delayed(
|
||||
networkRoundTripTime,
|
||||
() => _dummyUser,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Mock of a login request performed with a saved token.
|
||||
/// If such request fails, this method will throw an [UnauthorizedException].
|
||||
Future<User> _loginWithToken(String token) async {
|
||||
final logInAttempt = await Future.delayed(
|
||||
networkRoundTripTime,
|
||||
() => true,
|
||||
);
|
||||
|
||||
if (logInAttempt) return _dummyUser;
|
||||
|
||||
throw const UnauthorizedException('401 Unauthorized or something');
|
||||
}
|
||||
|
||||
/// Internal method used to listen authentication state changes.
|
||||
/// When the auth object is in a loading state, nothing happens.
|
||||
/// When the auth object is in a error state, we choose to remove the token
|
||||
/// Otherwise, we expect the current auth value to be reflected in our persistence API
|
||||
void _persistenceRefreshLogic() {
|
||||
ref.listenSelf((_, next) {
|
||||
if (next.isLoading) return;
|
||||
if (next.hasError) {
|
||||
sharedPreferences.remove(_sharedPrefsKey);
|
||||
return;
|
||||
}
|
||||
|
||||
final val = next.requireValue;
|
||||
final isAuthenticated = val == null;
|
||||
|
||||
isAuthenticated
|
||||
? sharedPreferences.remove(_sharedPrefsKey)
|
||||
: sharedPreferences.setString(_sharedPrefsKey, val.publicKey);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
final authNotifierProvider =
|
||||
AutoDisposeAsyncNotifierProvider<AuthNotifier, User?>(() {
|
||||
return AuthNotifier();
|
||||
});
|
||||
|
||||
class UnauthorizedException implements Exception {
|
||||
final String message;
|
||||
const UnauthorizedException(this.message);
|
||||
}
|
||||
|
||||
/// Mock of the duration of a network request
|
||||
const networkRoundTripTime = Duration(milliseconds: 750);
|
109
lib/state/local_account_manager.dart
Normal file
109
lib/state/local_account_manager.dart
Normal file
@ -0,0 +1,109 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:veilid/veilid.dart';
|
||||
|
||||
import '../entities/entities.dart';
|
||||
import '../entities/proto.dart' as proto;
|
||||
|
||||
part 'local_account_manager.g.dart';
|
||||
|
||||
// Local account manager
|
||||
class LocalAccountManager {
|
||||
final VeilidTableDB _localAccountsTable;
|
||||
final IList<LocalAccount> _localAccounts;
|
||||
|
||||
const LocalAccountManager(
|
||||
{required VeilidTableDB localAccountsTable,
|
||||
required IList<LocalAccount> localAccounts})
|
||||
: _localAccountsTable = localAccountsTable,
|
||||
_localAccounts = localAccounts;
|
||||
|
||||
/// Gets or creates a local account manager
|
||||
static Future<LocalAccountManager> open() async {
|
||||
final localAccountsTable =
|
||||
await Veilid.instance.openTableDB("local_account_manager", 1);
|
||||
final localAccounts =
|
||||
(await localAccountsTable.loadStringJson(0, "local_accounts") ??
|
||||
const IListConst([])) as IList<LocalAccount>;
|
||||
return LocalAccountManager(
|
||||
localAccountsTable: localAccountsTable, localAccounts: localAccounts);
|
||||
}
|
||||
|
||||
/// Flush things to storage
|
||||
Future<void> flush() async {}
|
||||
|
||||
/// Creates a new master identity and returns it with its secrets
|
||||
Future<IdentityMasterWithSecrets> newIdentityMaster() async {
|
||||
final dhtctx = (await Veilid.instance.routingContext())
|
||||
.withPrivacy()
|
||||
.withSequencing(Sequencing.ensureOrdered);
|
||||
final masterRec =
|
||||
await dhtctx.createDHTRecord(const DHTSchema.dflt(oCnt: 1));
|
||||
final identityRec =
|
||||
await dhtctx.createDHTRecord(const DHTSchema.dflt(oCnt: 1));
|
||||
final crypto = await Veilid.instance.bestCryptoSystem();
|
||||
assert(masterRec.key.kind == crypto.kind());
|
||||
assert(identityRec.key.kind == crypto.kind());
|
||||
|
||||
// IdentityMaster
|
||||
final masterRecordKey = masterRec.key;
|
||||
final masterPublicKey = masterRec.owner;
|
||||
final masterSecret = masterRec.ownerSecret!;
|
||||
final masterSigBuf = masterRecordKey.decode()
|
||||
..addAll(masterPublicKey.decode());
|
||||
|
||||
final identityRecordKey = identityRec.key;
|
||||
final identityPublicKey = identityRec.owner;
|
||||
final identitySecret = identityRec.ownerSecret!;
|
||||
final identitySigBuf = identityRecordKey.decode()
|
||||
..addAll(identityPublicKey.decode());
|
||||
|
||||
final identitySignature =
|
||||
await crypto.sign(masterPublicKey, masterSecret, identitySigBuf);
|
||||
final masterSignature =
|
||||
await crypto.sign(identityPublicKey, identitySecret, masterSigBuf);
|
||||
|
||||
final identityMaster = IdentityMaster(
|
||||
identityRecordKey: identityRecordKey,
|
||||
identityPublicKey: identityPublicKey,
|
||||
masterRecordKey: masterRecordKey,
|
||||
masterPublicKey: masterPublicKey,
|
||||
identitySignature: identitySignature,
|
||||
masterSignature: masterSignature);
|
||||
|
||||
// Write identity master to master dht key
|
||||
final identityMasterBytes =
|
||||
Uint8List.fromList(utf8.encode(jsonEncode(identityMaster)));
|
||||
await dhtctx.setDHTValue(masterRecordKey, 0, identityMasterBytes);
|
||||
|
||||
return IdentityMasterWithSecrets(
|
||||
identityMaster: identityMaster,
|
||||
masterSecret: masterSecret,
|
||||
identitySecret: identitySecret);
|
||||
}
|
||||
|
||||
/// Creates a new account associated with master identity
|
||||
Future<LocalAccount> newAccount(
|
||||
IdentityMaster identityMaster,
|
||||
SecretKey identitySecret,
|
||||
EncryptionKeyType encryptionKeyType,
|
||||
String encryptionKey) async {
|
||||
//
|
||||
|
||||
return LocalAccount(
|
||||
identityMaster: identityMaster,
|
||||
identitySecretKeyBytes: identitySecretBytes,
|
||||
encryptionKeyType: encryptionKeyType,
|
||||
biometricsEnabled: false,
|
||||
hiddenAccount: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Future<LocalAccountManager> localAccountManager(LocalAccountManagerRef ref) {
|
||||
return LocalAccountManager.open();
|
||||
}
|
@ -1,2 +1,2 @@
|
||||
export 'connection_state.dart';
|
||||
export 'auth.dart';
|
||||
export 'active_logins_state.dart';
|
||||
|
Loading…
Reference in New Issue
Block a user