Skip to content

Commit

Permalink
feat: reauth after delegation available (#680)
Browse files Browse the repository at this point in the history
## Description
Reauth relays after user delegation is send.

## Type of Change
- [ ] Bug fix
- [x] New feature
- [ ] Breaking change
- [ ] Refactoring
- [ ] Documentation
- [ ] Chore
  • Loading branch information
ice-endymion authored Feb 11, 2025
1 parent ff874c8 commit 9c54298
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 25 deletions.
24 changes: 19 additions & 5 deletions lib/app/features/ion_connect/providers/ion_connect_notifier.c.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'
import 'package:ion/app/features/ion_connect/providers/ion_connect_event_parser.c.dart';
import 'package:ion/app/features/ion_connect/providers/ion_connect_event_signer_provider.c.dart';
import 'package:ion/app/features/ion_connect/providers/relay_creation_provider.c.dart';
import 'package:ion/app/features/user/providers/user_delegation_provider.c.dart';
import 'package:ion/app/services/ion_identity/ion_identity_provider.c.dart';
import 'package:ion/app/services/logger/logger.dart';
import 'package:ion/app/utils/retry.dart';
Expand Down Expand Up @@ -45,7 +46,7 @@ class IonConnectNotifier extends _$IonConnectNotifier {
.getRelay(actionSource, dislikedUrls: dislikedRelaysUrls);

if (_isAuthRequired(error)) {
await sendAuthEvent(relay!);
await _sendAuthEvent(relay!);
}

await relay!.sendEvents(events);
Expand Down Expand Up @@ -78,7 +79,20 @@ class IonConnectNotifier extends _$IonConnectNotifier {
return result?.elementAtOrNull(0);
}

Future<void> sendAuthEvent(IonConnectRelay relay) async {
Future<void> initRelayAuth(IonConnectRelay relay) async {
final signedAuthEvent = await createAuthEvent(
challenge: 'init',
relayUrl: Uri.parse(relay.url).toString(),
);

await sendEvent(
signedAuthEvent,
relay: relay,
cache: false,
);
}

Future<void> _sendAuthEvent(IonConnectRelay relay) async {
final challenge = ref.read(authChallengeProvider(relay.url));
if (challenge == null || challenge.isEmpty) throw AuthChallengeIsEmptyException();

Expand Down Expand Up @@ -113,7 +127,8 @@ class IonConnectNotifier extends _$IonConnectNotifier {
relay: relayUrl,
);

return sign(authEvent);
final delegation = await ref.read(currentUserCachedDelegationProvider.future);
return sign(authEvent, includeMasterPubkey: delegation != null);
}

Future<List<IonConnectEntity>?> sendEntitiesData(
Expand Down Expand Up @@ -151,8 +166,7 @@ class IonConnectNotifier extends _$IonConnectNotifier {

if (_isAuthRequired(error)) {
try {
// TODO: handle multiple auth requests to one connectoon properly
await sendAuthEvent(relay!);
await _sendAuthEvent(relay!);
} catch (error, stackTrace) {
Logger.log('Send auth exception', error: error, stackTrace: stackTrace);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: ice License 1.0

import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:ion/app/features/ion_connect/ion_connect.dart';
import 'package:ion/app/features/ion_connect/providers/ion_connect_event_signer_provider.c.dart';
import 'package:ion/app/features/ion_connect/providers/ion_connect_notifier.c.dart';
import 'package:ion/app/features/user/model/user_delegation.c.dart';
import 'package:ion/app/features/user/providers/user_delegation_provider.c.dart';

mixin RelayDelegationMixin {
void initializeDelegationListener(IonConnectRelay relay, Ref ref) {
ref.listen(
currentUserCachedDelegationProvider,
(previous, next) => _handleDelegationChange(previous, next, relay, ref),
);
}

void _handleDelegationChange(
AsyncValue<UserDelegationEntity?>? previous,
AsyncValue<UserDelegationEntity?> next,
IonConnectRelay relay,
Ref ref,
) {
if (previous?.value == null && next.value != null) {
ref.read(currentUserIonConnectEventSignerProvider.future).then((eventSigner) {
if (eventSigner != null) {
final hasDelegate =
next.value?.data.hasDelegateFor(pubkey: eventSigner.publicKey) ?? false;

if (hasDelegate) {
ref.read(ionConnectNotifierProvider.notifier).initRelayAuth(relay);
}
}
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,7 @@ mixin RelayInitMixin {
_initCompleters[relay.url] = completer;

try {
final signedAuthEvent = await ref.read(ionConnectNotifierProvider.notifier).createAuthEvent(
challenge: 'init',
relayUrl: Uri.parse(relay.url).toString(),
);

await ref.read(ionConnectNotifierProvider.notifier).sendEvent(
signedAuthEvent,
relay: relay,
cache: false,
);
await ref.read(ionConnectNotifierProvider.notifier).initRelayAuth(relay);

_subscriptions[relay.url] = relay.onClose.listen(
(url) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import 'package:ion/app/features/ion_connect/ion_connect.dart';
import 'package:ion/app/features/ion_connect/providers/mixins/relay_auth_mixin.dart';
import 'package:ion/app/features/ion_connect/providers/mixins/relay_delegation_mixin.dart';
import 'package:ion/app/features/ion_connect/providers/mixins/relay_init_mixin.dart';
import 'package:ion/app/features/ion_connect/providers/mixins/relay_timer_mixin.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
Expand All @@ -11,13 +12,16 @@ part 'relays_provider.c.g.dart';
typedef RelaysState = Map<String, IonConnectRelay>;

@Riverpod(keepAlive: true)
class Relay extends _$Relay with RelayTimerMixin, RelayAuthMixin, RelayInitMixin {
class Relay extends _$Relay
with RelayTimerMixin, RelayAuthMixin, RelayInitMixin, RelayDelegationMixin {
@override
Future<IonConnectRelay> build(String url, {bool anonymous = false}) async {
final relay = await IonConnectRelay.connect(url);

initializeRelayTimer(relay, ref);

if (!anonymous) {
initializeDelegationListener(relay, ref);
initializeAuthMessageListener(relay, ref);
await initRelay(relay, ref);
}
Expand Down
41 changes: 32 additions & 9 deletions lib/app/features/user/providers/user_delegation_provider.c.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,10 @@ part 'user_delegation_provider.c.g.dart';

@Riverpod(keepAlive: true)
Future<UserDelegationEntity?> userDelegation(Ref ref, String pubkey) async {
final userDelegation = ref.watch(
ionConnectCacheProvider.select(
cacheSelector<UserDelegationEntity>(
CacheableEntity.cacheKeyBuilder(
eventReference:
ReplaceableEventReference(pubkey: pubkey, kind: UserDelegationEntity.kind),
),
),
),
final userDelegation = await ref.watch(
cachedUserDelegationProvider(pubkey).future,
);

if (userDelegation != null) {
return userDelegation;
}
Expand All @@ -40,6 +34,24 @@ Future<UserDelegationEntity?> userDelegation(Ref ref, String pubkey) async {
);
}

@Riverpod(keepAlive: true)
Future<UserDelegationEntity?> cachedUserDelegation(Ref ref, String pubkey) async {
final userDelegation = ref.watch(
ionConnectCacheProvider.select(
cacheSelector<UserDelegationEntity>(
CacheableEntity.cacheKeyBuilder(
eventReference: ReplaceableEventReference(
pubkey: pubkey,
kind: UserDelegationEntity.kind,
),
),
),
),
);

return userDelegation;
}

@Riverpod(keepAlive: true)
Future<UserDelegationEntity?> currentUserDelegation(Ref ref) async {
final mainWallet = await ref.watch(mainWalletProvider.future);
Expand All @@ -51,6 +63,17 @@ Future<UserDelegationEntity?> currentUserDelegation(Ref ref) async {
}
}

@Riverpod(keepAlive: true)
Future<UserDelegationEntity?> currentUserCachedDelegation(Ref ref) async {
final mainWallet = await ref.watch(mainWalletProvider.future);

try {
return await ref.watch(cachedUserDelegationProvider(mainWallet.signingKey.publicKey).future);
} on UserRelaysNotFoundException catch (_) {
return null;
}
}

@riverpod
class UserDelegationManager extends _$UserDelegationManager {
@override
Expand Down

0 comments on commit 9c54298

Please sign in to comment.