mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2024-10-01 06:55:46 -04:00
identity work
This commit is contained in:
parent
9eff8f0cb4
commit
ac58e1dea3
@ -1,3 +1,4 @@
|
|||||||
export 'identity.dart';
|
export 'identity.dart';
|
||||||
export 'local_account.dart';
|
export 'local_account.dart';
|
||||||
export 'preferences.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
|
// Identity Key points to accounts associated with this identity
|
||||||
// accounts field has a map of service name or uuid to account key pairs
|
// accounts field has a map of service name or uuid to account key pairs
|
||||||
// DHT Schema: DFLT(1)
|
// 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)
|
// DHT Secret: identitySecretKey (stored encrypted with unlock code in local table store)
|
||||||
@freezed
|
@freezed
|
||||||
class Identity with _$Identity {
|
class Identity with _$Identity {
|
||||||
@ -25,21 +26,43 @@ class Identity with _$Identity {
|
|||||||
// Bidirectional Master<->Identity signature allows for
|
// Bidirectional Master<->Identity signature allows for
|
||||||
// chain of identity ownership for account recovery process
|
// 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
|
// completely offline and only written to upon account recovery
|
||||||
//
|
//
|
||||||
// DHT Schema: DFLT(1)
|
// DHT Schema: DFLT(1)
|
||||||
// DHT Key (Public): masterPublicKey
|
// DHT Record Key (Public): masterRecordKey
|
||||||
// DHT Secret: masterSecretKey (kept offline)
|
// DHT Owner Key: masterPublicKey
|
||||||
|
// DHT Owner Secret: masterSecretKey (kept offline)
|
||||||
// Encryption: None
|
// Encryption: None
|
||||||
@freezed
|
@freezed
|
||||||
class IdentityMaster with _$IdentityMaster {
|
class IdentityMaster with _$IdentityMaster {
|
||||||
const factory 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,
|
required Signature identitySignature,
|
||||||
|
// Signature of masterRecordKey and masterPublicKey by identityPublicKey
|
||||||
required Signature masterSignature}) = _IdentityMaster;
|
required Signature masterSignature}) = _IdentityMaster;
|
||||||
|
|
||||||
factory IdentityMaster.fromJson(Map<String, dynamic> json) =>
|
factory IdentityMaster.fromJson(Map<String, dynamic> json) =>
|
||||||
_$IdentityMasterFromJson(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
|
/// @nodoc
|
||||||
mixin _$IdentityMaster {
|
mixin _$IdentityMaster {
|
||||||
Typed<FixedEncodedString43> get identityPublicKey =>
|
// Private DHT record storing identity account mapping
|
||||||
throw _privateConstructorUsedError;
|
Typed<FixedEncodedString43> get identityRecordKey =>
|
||||||
Typed<FixedEncodedString43> get masterPublicKey =>
|
throw _privateConstructorUsedError; // Public key of identity
|
||||||
throw _privateConstructorUsedError;
|
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 =>
|
FixedEncodedString86 get identitySignature =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError; // Signature of masterRecordKey and masterPublicKey by identityPublicKey
|
||||||
FixedEncodedString86 get masterSignature =>
|
FixedEncodedString86 get masterSignature =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
|
|
||||||
@ -188,8 +193,10 @@ abstract class $IdentityMasterCopyWith<$Res> {
|
|||||||
_$IdentityMasterCopyWithImpl<$Res, IdentityMaster>;
|
_$IdentityMasterCopyWithImpl<$Res, IdentityMaster>;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call(
|
$Res call(
|
||||||
{Typed<FixedEncodedString43> identityPublicKey,
|
{Typed<FixedEncodedString43> identityRecordKey,
|
||||||
Typed<FixedEncodedString43> masterPublicKey,
|
FixedEncodedString43 identityPublicKey,
|
||||||
|
Typed<FixedEncodedString43> masterRecordKey,
|
||||||
|
FixedEncodedString43 masterPublicKey,
|
||||||
FixedEncodedString86 identitySignature,
|
FixedEncodedString86 identitySignature,
|
||||||
FixedEncodedString86 masterSignature});
|
FixedEncodedString86 masterSignature});
|
||||||
}
|
}
|
||||||
@ -207,20 +214,30 @@ class _$IdentityMasterCopyWithImpl<$Res, $Val extends IdentityMaster>
|
|||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
@override
|
@override
|
||||||
$Res call({
|
$Res call({
|
||||||
|
Object? identityRecordKey = null,
|
||||||
Object? identityPublicKey = null,
|
Object? identityPublicKey = null,
|
||||||
|
Object? masterRecordKey = null,
|
||||||
Object? masterPublicKey = null,
|
Object? masterPublicKey = null,
|
||||||
Object? identitySignature = null,
|
Object? identitySignature = null,
|
||||||
Object? masterSignature = null,
|
Object? masterSignature = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_value.copyWith(
|
return _then(_value.copyWith(
|
||||||
|
identityRecordKey: null == identityRecordKey
|
||||||
|
? _value.identityRecordKey
|
||||||
|
: identityRecordKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Typed<FixedEncodedString43>,
|
||||||
identityPublicKey: null == identityPublicKey
|
identityPublicKey: null == identityPublicKey
|
||||||
? _value.identityPublicKey
|
? _value.identityPublicKey
|
||||||
: identityPublicKey // ignore: cast_nullable_to_non_nullable
|
: identityPublicKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as FixedEncodedString43,
|
||||||
|
masterRecordKey: null == masterRecordKey
|
||||||
|
? _value.masterRecordKey
|
||||||
|
: masterRecordKey // ignore: cast_nullable_to_non_nullable
|
||||||
as Typed<FixedEncodedString43>,
|
as Typed<FixedEncodedString43>,
|
||||||
masterPublicKey: null == masterPublicKey
|
masterPublicKey: null == masterPublicKey
|
||||||
? _value.masterPublicKey
|
? _value.masterPublicKey
|
||||||
: masterPublicKey // ignore: cast_nullable_to_non_nullable
|
: masterPublicKey // ignore: cast_nullable_to_non_nullable
|
||||||
as Typed<FixedEncodedString43>,
|
as FixedEncodedString43,
|
||||||
identitySignature: null == identitySignature
|
identitySignature: null == identitySignature
|
||||||
? _value.identitySignature
|
? _value.identitySignature
|
||||||
: identitySignature // ignore: cast_nullable_to_non_nullable
|
: identitySignature // ignore: cast_nullable_to_non_nullable
|
||||||
@ -242,8 +259,10 @@ abstract class _$$_IdentityMasterCopyWith<$Res>
|
|||||||
@override
|
@override
|
||||||
@useResult
|
@useResult
|
||||||
$Res call(
|
$Res call(
|
||||||
{Typed<FixedEncodedString43> identityPublicKey,
|
{Typed<FixedEncodedString43> identityRecordKey,
|
||||||
Typed<FixedEncodedString43> masterPublicKey,
|
FixedEncodedString43 identityPublicKey,
|
||||||
|
Typed<FixedEncodedString43> masterRecordKey,
|
||||||
|
FixedEncodedString43 masterPublicKey,
|
||||||
FixedEncodedString86 identitySignature,
|
FixedEncodedString86 identitySignature,
|
||||||
FixedEncodedString86 masterSignature});
|
FixedEncodedString86 masterSignature});
|
||||||
}
|
}
|
||||||
@ -259,20 +278,30 @@ class __$$_IdentityMasterCopyWithImpl<$Res>
|
|||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
@override
|
@override
|
||||||
$Res call({
|
$Res call({
|
||||||
|
Object? identityRecordKey = null,
|
||||||
Object? identityPublicKey = null,
|
Object? identityPublicKey = null,
|
||||||
|
Object? masterRecordKey = null,
|
||||||
Object? masterPublicKey = null,
|
Object? masterPublicKey = null,
|
||||||
Object? identitySignature = null,
|
Object? identitySignature = null,
|
||||||
Object? masterSignature = null,
|
Object? masterSignature = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$_IdentityMaster(
|
return _then(_$_IdentityMaster(
|
||||||
|
identityRecordKey: null == identityRecordKey
|
||||||
|
? _value.identityRecordKey
|
||||||
|
: identityRecordKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Typed<FixedEncodedString43>,
|
||||||
identityPublicKey: null == identityPublicKey
|
identityPublicKey: null == identityPublicKey
|
||||||
? _value.identityPublicKey
|
? _value.identityPublicKey
|
||||||
: identityPublicKey // ignore: cast_nullable_to_non_nullable
|
: identityPublicKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as FixedEncodedString43,
|
||||||
|
masterRecordKey: null == masterRecordKey
|
||||||
|
? _value.masterRecordKey
|
||||||
|
: masterRecordKey // ignore: cast_nullable_to_non_nullable
|
||||||
as Typed<FixedEncodedString43>,
|
as Typed<FixedEncodedString43>,
|
||||||
masterPublicKey: null == masterPublicKey
|
masterPublicKey: null == masterPublicKey
|
||||||
? _value.masterPublicKey
|
? _value.masterPublicKey
|
||||||
: masterPublicKey // ignore: cast_nullable_to_non_nullable
|
: masterPublicKey // ignore: cast_nullable_to_non_nullable
|
||||||
as Typed<FixedEncodedString43>,
|
as FixedEncodedString43,
|
||||||
identitySignature: null == identitySignature
|
identitySignature: null == identitySignature
|
||||||
? _value.identitySignature
|
? _value.identitySignature
|
||||||
: identitySignature // ignore: cast_nullable_to_non_nullable
|
: identitySignature // ignore: cast_nullable_to_non_nullable
|
||||||
@ -289,7 +318,9 @@ class __$$_IdentityMasterCopyWithImpl<$Res>
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class _$_IdentityMaster implements _IdentityMaster {
|
class _$_IdentityMaster implements _IdentityMaster {
|
||||||
const _$_IdentityMaster(
|
const _$_IdentityMaster(
|
||||||
{required this.identityPublicKey,
|
{required this.identityRecordKey,
|
||||||
|
required this.identityPublicKey,
|
||||||
|
required this.masterRecordKey,
|
||||||
required this.masterPublicKey,
|
required this.masterPublicKey,
|
||||||
required this.identitySignature,
|
required this.identitySignature,
|
||||||
required this.masterSignature});
|
required this.masterSignature});
|
||||||
@ -297,18 +328,28 @@ class _$_IdentityMaster implements _IdentityMaster {
|
|||||||
factory _$_IdentityMaster.fromJson(Map<String, dynamic> json) =>
|
factory _$_IdentityMaster.fromJson(Map<String, dynamic> json) =>
|
||||||
_$$_IdentityMasterFromJson(json);
|
_$$_IdentityMasterFromJson(json);
|
||||||
|
|
||||||
|
// Private DHT record storing identity account mapping
|
||||||
@override
|
@override
|
||||||
final Typed<FixedEncodedString43> identityPublicKey;
|
final Typed<FixedEncodedString43> identityRecordKey;
|
||||||
|
// Public key of identity
|
||||||
@override
|
@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
|
@override
|
||||||
final FixedEncodedString86 identitySignature;
|
final FixedEncodedString86 identitySignature;
|
||||||
|
// Signature of masterRecordKey and masterPublicKey by identityPublicKey
|
||||||
@override
|
@override
|
||||||
final FixedEncodedString86 masterSignature;
|
final FixedEncodedString86 masterSignature;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
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
|
@override
|
||||||
@ -316,8 +357,12 @@ class _$_IdentityMaster implements _IdentityMaster {
|
|||||||
return identical(this, other) ||
|
return identical(this, other) ||
|
||||||
(other.runtimeType == runtimeType &&
|
(other.runtimeType == runtimeType &&
|
||||||
other is _$_IdentityMaster &&
|
other is _$_IdentityMaster &&
|
||||||
|
(identical(other.identityRecordKey, identityRecordKey) ||
|
||||||
|
other.identityRecordKey == identityRecordKey) &&
|
||||||
(identical(other.identityPublicKey, identityPublicKey) ||
|
(identical(other.identityPublicKey, identityPublicKey) ||
|
||||||
other.identityPublicKey == identityPublicKey) &&
|
other.identityPublicKey == identityPublicKey) &&
|
||||||
|
(identical(other.masterRecordKey, masterRecordKey) ||
|
||||||
|
other.masterRecordKey == masterRecordKey) &&
|
||||||
(identical(other.masterPublicKey, masterPublicKey) ||
|
(identical(other.masterPublicKey, masterPublicKey) ||
|
||||||
other.masterPublicKey == masterPublicKey) &&
|
other.masterPublicKey == masterPublicKey) &&
|
||||||
(identical(other.identitySignature, identitySignature) ||
|
(identical(other.identitySignature, identitySignature) ||
|
||||||
@ -328,8 +373,14 @@ class _$_IdentityMaster implements _IdentityMaster {
|
|||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType, identityPublicKey,
|
int get hashCode => Object.hash(
|
||||||
masterPublicKey, identitySignature, masterSignature);
|
runtimeType,
|
||||||
|
identityRecordKey,
|
||||||
|
identityPublicKey,
|
||||||
|
masterRecordKey,
|
||||||
|
masterPublicKey,
|
||||||
|
identitySignature,
|
||||||
|
masterSignature);
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
@override
|
@override
|
||||||
@ -347,21 +398,27 @@ class _$_IdentityMaster implements _IdentityMaster {
|
|||||||
|
|
||||||
abstract class _IdentityMaster implements IdentityMaster {
|
abstract class _IdentityMaster implements IdentityMaster {
|
||||||
const factory _IdentityMaster(
|
const factory _IdentityMaster(
|
||||||
{required final Typed<FixedEncodedString43> identityPublicKey,
|
{required final Typed<FixedEncodedString43> identityRecordKey,
|
||||||
required final Typed<FixedEncodedString43> masterPublicKey,
|
required final FixedEncodedString43 identityPublicKey,
|
||||||
|
required final Typed<FixedEncodedString43> masterRecordKey,
|
||||||
|
required final FixedEncodedString43 masterPublicKey,
|
||||||
required final FixedEncodedString86 identitySignature,
|
required final FixedEncodedString86 identitySignature,
|
||||||
required final FixedEncodedString86 masterSignature}) = _$_IdentityMaster;
|
required final FixedEncodedString86 masterSignature}) = _$_IdentityMaster;
|
||||||
|
|
||||||
factory _IdentityMaster.fromJson(Map<String, dynamic> json) =
|
factory _IdentityMaster.fromJson(Map<String, dynamic> json) =
|
||||||
_$_IdentityMaster.fromJson;
|
_$_IdentityMaster.fromJson;
|
||||||
|
|
||||||
@override
|
@override // Private DHT record storing identity account mapping
|
||||||
Typed<FixedEncodedString43> get identityPublicKey;
|
Typed<FixedEncodedString43> get identityRecordKey;
|
||||||
@override
|
@override // Public key of identity
|
||||||
Typed<FixedEncodedString43> get masterPublicKey;
|
FixedEncodedString43 get identityPublicKey;
|
||||||
@override
|
@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;
|
FixedEncodedString86 get identitySignature;
|
||||||
@override
|
@override // Signature of masterRecordKey and masterPublicKey by identityPublicKey
|
||||||
FixedEncodedString86 get masterSignature;
|
FixedEncodedString86 get masterSignature;
|
||||||
@override
|
@override
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
|
@ -20,10 +20,13 @@ Map<String, dynamic> _$$_IdentityToJson(_$_Identity instance) =>
|
|||||||
|
|
||||||
_$_IdentityMaster _$$_IdentityMasterFromJson(Map<String, dynamic> json) =>
|
_$_IdentityMaster _$$_IdentityMasterFromJson(Map<String, dynamic> json) =>
|
||||||
_$_IdentityMaster(
|
_$_IdentityMaster(
|
||||||
|
identityRecordKey:
|
||||||
|
Typed<FixedEncodedString43>.fromJson(json['identity_record_key']),
|
||||||
identityPublicKey:
|
identityPublicKey:
|
||||||
Typed<FixedEncodedString43>.fromJson(json['identity_public_key']),
|
FixedEncodedString43.fromJson(json['identity_public_key']),
|
||||||
masterPublicKey:
|
masterRecordKey:
|
||||||
Typed<FixedEncodedString43>.fromJson(json['master_public_key']),
|
Typed<FixedEncodedString43>.fromJson(json['master_record_key']),
|
||||||
|
masterPublicKey: FixedEncodedString43.fromJson(json['master_public_key']),
|
||||||
identitySignature:
|
identitySignature:
|
||||||
FixedEncodedString86.fromJson(json['identity_signature']),
|
FixedEncodedString86.fromJson(json['identity_signature']),
|
||||||
masterSignature: FixedEncodedString86.fromJson(json['master_signature']),
|
masterSignature: FixedEncodedString86.fromJson(json['master_signature']),
|
||||||
@ -31,7 +34,9 @@ _$_IdentityMaster _$$_IdentityMasterFromJson(Map<String, dynamic> json) =>
|
|||||||
|
|
||||||
Map<String, dynamic> _$$_IdentityMasterToJson(_$_IdentityMaster instance) =>
|
Map<String, dynamic> _$$_IdentityMasterToJson(_$_IdentityMaster instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
|
'identity_record_key': instance.identityRecordKey.toJson(),
|
||||||
'identity_public_key': instance.identityPublicKey.toJson(),
|
'identity_public_key': instance.identityPublicKey.toJson(),
|
||||||
|
'master_record_key': instance.masterRecordKey.toJson(),
|
||||||
'master_public_key': instance.masterPublicKey.toJson(),
|
'master_public_key': instance.masterPublicKey.toJson(),
|
||||||
'identity_signature': instance.identitySignature.toJson(),
|
'identity_signature': instance.identitySignature.toJson(),
|
||||||
'master_signature': instance.masterSignature.toJson(),
|
'master_signature': instance.masterSignature.toJson(),
|
||||||
|
@ -2,6 +2,7 @@ import 'dart:typed_data';
|
|||||||
|
|
||||||
import 'package:change_case/change_case.dart';
|
import 'package:change_case/change_case.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
import 'package:veilid/veilid.dart';
|
import 'package:veilid/veilid.dart';
|
||||||
import 'identity.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/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import '../state/auth.dart';
|
import '../state/active_logins_state.dart';
|
||||||
|
|
||||||
class LoginPage extends ConsumerWidget {
|
class LoginPage extends ConsumerWidget {
|
||||||
const LoginPage({super.key});
|
const LoginPage({super.key});
|
||||||
|
@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
import '../pages/pages.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.
|
/// 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 'connection_state.dart';
|
||||||
export 'auth.dart';
|
export 'active_logins_state.dart';
|
||||||
|
Loading…
Reference in New Issue
Block a user