authenticate method
Future<SessionAuthRequestResponse>
authenticate({
- required SessionAuthRequestParams params,
- String? walletUniversalLink,
- String? pairingTopic,
- List<
List< ? methods = const [[MethodConstants.WC_SESSION_AUTHENTICATE]],String> >
override
Implementation
@override
Future<SessionAuthRequestResponse> authenticate({
required SessionAuthRequestParams params,
String? walletUniversalLink,
String? pairingTopic,
List<List<String>>? methods = const [
[MethodConstants.WC_SESSION_AUTHENTICATE]
],
}) async {
_checkInitialized();
AuthApiValidators.isValidAuthenticate(params);
final isLinkMode = _isLinkModeAuthenticate(walletUniversalLink);
final transportType =
isLinkMode ? TransportType.linkMode : TransportType.relay;
if (!transportType.isLinkMode) {
_confirmOnlineStateOrThrow();
}
final chains = params.chains;
final resources = params.resources ?? [];
final requestMethods = params.methods ?? [];
String? pTopic = pairingTopic;
Uri? connectionUri;
if (pTopic == null) {
final CreateResponse pairing = await core.pairing.create(
methods: methods,
);
pTopic = pairing.topic;
connectionUri = pairing.uri;
} else {
core.pairing.isValidPairingTopic(topic: pTopic);
}
final publicKey = await core.crypto.generateKeyPair();
final responseTopic = core.crypto.getUtils().hashKey(publicKey);
await Future.wait([
authKeys.set(
StringConstants.OCAUTH_CLIENT_PUBLIC_KEY_NAME,
AuthPublicKey(publicKey: publicKey),
),
pairingTopics.set(responseTopic, pTopic),
]);
if (requestMethods.isNotEmpty) {
final namespace = NamespaceUtils.getNamespaceFromChain(chains.first);
String recap = ReCapsUtils.createEncodedRecap(
namespace,
'request',
requestMethods,
);
final existingRecap = ReCapsUtils.getRecapFromResources(
resources: resources,
);
if (existingRecap != null) {
// per Recaps spec, recap must occupy the last position in the resources array
// using .removeLast() to remove the element given we already checked it's a recap and will replace it
recap = ReCapsUtils.mergeEncodedRecaps(recap, resources.removeLast());
}
resources.add(recap);
}
// Subscribe to the responseTopic because we expect the response to use this topic
await core.relayClient.subscribe(topic: responseTopic);
final id = JsonRpcUtils.payloadId();
final fallbackId = JsonRpcUtils.payloadId();
// Ensure the expiry is greater than the minimum required for the request - currently 1h
final method = MethodConstants.WC_SESSION_AUTHENTICATE;
final opts = MethodConstants.RPC_OPTS[method]!['req']!;
final authRequestExpiry = max((params.expiry ?? 0), opts.ttl);
final expiryTimestamp = DateTime.now().add(
Duration(seconds: authRequestExpiry),
);
final request = WcSessionAuthRequestParams(
authPayload: SessionAuthPayload.fromRequestParams(params).copyWith(
resources: resources,
),
requester: ConnectionMetadata(
publicKey: publicKey,
metadata: metadata,
),
expiryTimestamp: expiryTimestamp.millisecondsSinceEpoch,
transportType: transportType, // TODO LinkMode remove? Ask Nacho or Gancho
);
// Set the one time use receiver public key for decoding the Type 1 envelope
await core.pairing.setReceiverPublicKey(
topic: responseTopic,
publicKey: publicKey,
expiry: authRequestExpiry,
);
Completer<SessionAuthResponse> completer = Completer();
// ----- build fallback session proposal request ----- //
final fallbackMethod = MethodConstants.WC_SESSION_PROPOSE;
final fallbackOpts = MethodConstants.RPC_OPTS[fallbackMethod]!['req']!;
final fallbackExpiryTimestamp = DateTime.now().add(
Duration(seconds: fallbackOpts.ttl),
);
final proposalData = ProposalData(
id: fallbackId,
requiredNamespaces: {},
optionalNamespaces: {
'eip155': RequiredNamespace(
chains: chains,
methods: {'personal_sign', ...requestMethods}.toList(),
events: EventsConstants.requiredEvents,
),
},
relays: [Relay(WalletConnectConstants.RELAYER_DEFAULT_PROTOCOL)],
expiry: fallbackExpiryTimestamp.millisecondsSinceEpoch,
proposer: ConnectionMetadata(
publicKey: publicKey,
metadata: metadata,
),
pairingTopic: pTopic,
);
final proposeRequest = WcSessionProposeRequest(
relays: proposalData.relays,
requiredNamespaces: proposalData.requiredNamespaces,
optionalNamespaces: proposalData.optionalNamespaces,
proposer: proposalData.proposer,
);
await _setProposal(proposalData.id, proposalData);
Completer<SessionData> completerFallback = Completer();
pendingProposals.add(
SessionProposalCompleter(
id: fallbackId,
selfPublicKey: proposalData.proposer.publicKey,
pairingTopic: proposalData.pairingTopic,
requiredNamespaces: proposalData.requiredNamespaces,
optionalNamespaces: proposalData.optionalNamespaces,
completer: completerFallback,
),
);
// ------------------------------------------------------- //
late final Uri linkModeUri;
if (isLinkMode) {
final payload = JsonRpcUtils.formatJsonRpcRequest(
method,
request.toJson(),
id: id,
);
final message = await core.crypto.encode(
pTopic,
payload,
options: EncodeOptions(type: EncodeOptions.TYPE_2),
);
linkModeUri = WalletConnectUtils.getLinkModeURL(
walletUniversalLink!,
pTopic,
message!,
).toLinkMode;
} else {
// Send Session Proposal request (Only when on Relay mode)
_connectResponseHandler(
pTopic,
proposeRequest,
fallbackId,
);
}
// Send One-Click Auth request (When on Relay and LinkMode)
// TODO LinkMode: try to reduce the amount of params, maybe using a pendingSessionAuthRequest
_sessionAuthResponseHandler(
id: id,
publicKey: publicKey,
pairingTopic: pTopic,
responseTopic: responseTopic,
walletUniversalLink: walletUniversalLink,
isLinkMode: isLinkMode,
request: request,
expiry: authRequestExpiry, // TODO remove, use request.expiryTimestamp
completer: completer,
);
return SessionAuthRequestResponse(
id: id,
pairingTopic: pTopic,
// linkModeUri is sent in the response so the host app can trigger it
uri: isLinkMode ? linkModeUri : connectionUri,
completer: completer,
);
}