Page MenuHomePhabricator

D11330.id38081.diff
No OneTemporary

D11330.id38081.diff

diff --git a/lib/components/qr-auth-handler.react.js b/lib/components/qr-auth-handler.react.js
new file mode 100644
--- /dev/null
+++ b/lib/components/qr-auth-handler.react.js
@@ -0,0 +1,136 @@
+// @flow
+
+import invariant from 'invariant';
+import * as React from 'react';
+
+import { IdentityClientContext } from '../shared/identity-client-context.js';
+import { useTunnelbroker } from '../tunnelbroker/tunnelbroker-context.js';
+import {
+ tunnelbrokerMessageTypes,
+ type TunnelbrokerMessage,
+} from '../types/tunnelbroker/messages.js';
+import { peerToPeerMessageTypes } from '../types/tunnelbroker/peer-to-peer-message-types.js';
+import { qrCodeAuthMessageTypes } from '../types/tunnelbroker/qr-code-auth-message-types.js';
+import {
+ createQRAuthTunnelbrokerMessage,
+ parseQRAuthTunnelbrokerMessage,
+} from '../utils/qr-code-auth.js';
+
+type QRAuthHandlerProps = {
+ secondaryDeviceID: ?string,
+ aesKey: ?string,
+ performSecondaryDeviceRegistration: (userID: string) => Promise<void>,
+};
+
+function QRAuthHandler(props: QRAuthHandlerProps): React.Node {
+ const { secondaryDeviceID, aesKey, performSecondaryDeviceRegistration } =
+ props;
+ const [primaryDeviceID, setPrimaryDeviceID] = React.useState<?string>();
+ const {
+ setUnauthorizedDeviceID,
+ addListener,
+ removeListener,
+ connected: tunnelbrokerConnected,
+ isAuthorized,
+ sendMessage,
+ } = useTunnelbroker();
+
+ const identityContext = React.useContext(IdentityClientContext);
+ const identityClient = identityContext?.identityClient;
+
+ React.useEffect(() => {
+ if (
+ !secondaryDeviceID ||
+ !aesKey ||
+ !tunnelbrokerConnected ||
+ !isAuthorized ||
+ !primaryDeviceID
+ ) {
+ return;
+ }
+
+ void (async () => {
+ const message = createQRAuthTunnelbrokerMessage(aesKey, {
+ type: qrCodeAuthMessageTypes.SECONDARY_DEVICE_REGISTRATION_SUCCESS,
+ });
+ await sendMessage({
+ deviceID: primaryDeviceID,
+ payload: JSON.stringify(message),
+ });
+ })();
+ }, [
+ tunnelbrokerConnected,
+ isAuthorized,
+ sendMessage,
+ primaryDeviceID,
+ aesKey,
+ secondaryDeviceID,
+ ]);
+
+ const tunnelbrokerMessageListener = React.useCallback(
+ async (message: TunnelbrokerMessage) => {
+ invariant(identityClient, 'identity context not set');
+ if (
+ !aesKey ||
+ message.type !== tunnelbrokerMessageTypes.MESSAGE_TO_DEVICE
+ ) {
+ return;
+ }
+
+ const innerMessage = JSON.parse(message.payload);
+ if (innerMessage.type !== peerToPeerMessageTypes.QR_CODE_AUTH_MESSAGE) {
+ return;
+ }
+ const qrCodeAuthMessage = parseQRAuthTunnelbrokerMessage(
+ aesKey,
+ innerMessage,
+ );
+
+ if (
+ qrCodeAuthMessage?.type ===
+ qrCodeAuthMessageTypes.BACKUP_DATA_KEY_MESSAGE
+ ) {
+ return;
+ }
+
+ if (
+ !qrCodeAuthMessage ||
+ qrCodeAuthMessage.type !==
+ qrCodeAuthMessageTypes.DEVICE_LIST_UPDATE_SUCCESS
+ ) {
+ return;
+ }
+ const { primaryDeviceID: receivedPrimaryDeviceID, userID } =
+ qrCodeAuthMessage;
+ setPrimaryDeviceID(receivedPrimaryDeviceID);
+
+ await performSecondaryDeviceRegistration(userID);
+ setUnauthorizedDeviceID(null);
+ },
+ [
+ setUnauthorizedDeviceID,
+ identityClient,
+ aesKey,
+ performSecondaryDeviceRegistration,
+ ],
+ );
+
+ React.useEffect(() => {
+ if (!secondaryDeviceID) {
+ return () => {};
+ }
+ addListener(tunnelbrokerMessageListener);
+
+ return () => {
+ removeListener(tunnelbrokerMessageListener);
+ };
+ }, [
+ secondaryDeviceID,
+ addListener,
+ removeListener,
+ tunnelbrokerMessageListener,
+ ]);
+ return null;
+}
+
+export { QRAuthHandler };
diff --git a/native/qr-code/qr-code-screen.react.js b/native/qr-code/qr-code-screen.react.js
--- a/native/qr-code/qr-code-screen.react.js
+++ b/native/qr-code/qr-code-screen.react.js
@@ -5,6 +5,7 @@
import { View, Text } from 'react-native';
import QRCode from 'react-native-qrcode-svg';
+import { QRAuthHandler } from 'lib/components/qr-auth-handler.react.js';
import { qrCodeLinkURL } from 'lib/facts/links.js';
import { uintArrayToHexString } from 'lib/media/data-utils.js';
import { IdentityClientContext } from 'lib/shared/identity-client-context.js';
@@ -13,16 +14,6 @@
NonceChallenge,
SignedMessage,
} from 'lib/types/identity-service-types.js';
-import {
- tunnelbrokerMessageTypes,
- type TunnelbrokerMessage,
-} from 'lib/types/tunnelbroker/messages.js';
-import { peerToPeerMessageTypes } from 'lib/types/tunnelbroker/peer-to-peer-message-types.js';
-import { qrCodeAuthMessageTypes } from 'lib/types/tunnelbroker/qr-code-auth-message-types.js';
-import {
- createQRAuthTunnelbrokerMessage,
- parseQRAuthTunnelbrokerMessage,
-} from 'lib/utils/qr-code-auth.js';
import type { QRCodeSignInNavigationProp } from './qr-code-sign-in-navigator.react.js';
import { commCoreModule } from '../native-modules.js';
@@ -42,79 +33,14 @@
const [qrCodeValue, setQrCodeValue] = React.useState<?string>();
const [deviceKeys, setDeviceKeys] =
React.useState<?{ +deviceID: string, +aesKey: string }>();
- const [primaryDeviceID, setPrimaryDeviceID] = React.useState<?string>();
- const {
- setUnauthorizedDeviceID,
- addListener,
- removeListener,
- connected: tunnelbrokerConnected,
- isAuthorized,
- sendMessage,
- } = useTunnelbroker();
+ const { setUnauthorizedDeviceID } = useTunnelbroker();
+
const identityContext = React.useContext(IdentityClientContext);
const identityClient = identityContext?.identityClient;
- React.useEffect(() => {
- if (
- !(tunnelbrokerConnected && isAuthorized && primaryDeviceID && deviceKeys)
- ) {
- return;
- }
-
- const message = createQRAuthTunnelbrokerMessage(deviceKeys.aesKey, {
- type: qrCodeAuthMessageTypes.SECONDARY_DEVICE_REGISTRATION_SUCCESS,
- });
- console.log('SECONDARY MESSAGE', message);
- void sendMessage({
- deviceID: primaryDeviceID,
- payload: JSON.stringify(message),
- });
- }, [
- tunnelbrokerConnected,
- isAuthorized,
- sendMessage,
- primaryDeviceID,
- deviceKeys,
- ]);
-
- const tunnelbrokerMessageListener = React.useCallback(
- async (message: TunnelbrokerMessage) => {
+ const performRegistration = React.useCallback(
+ async (userID: string) => {
invariant(identityClient, 'identity context not set');
- if (
- !deviceKeys ||
- message.type !== tunnelbrokerMessageTypes.MESSAGE_TO_DEVICE
- ) {
- return;
- }
-
- const innerMessage = JSON.parse(message.payload);
- if (innerMessage.type !== peerToPeerMessageTypes.QR_CODE_AUTH_MESSAGE) {
- return;
- }
- const qrCodeAuthMessage = parseQRAuthTunnelbrokerMessage(
- deviceKeys.aesKey,
- innerMessage,
- );
-
- if (
- qrCodeAuthMessage?.type ===
- qrCodeAuthMessageTypes.BACKUP_DATA_KEY_MESSAGE
- ) {
- console.log('Received backup data key:', qrCodeAuthMessage);
- return;
- }
-
- if (
- !qrCodeAuthMessage ||
- qrCodeAuthMessage.type !==
- qrCodeAuthMessageTypes.DEVICE_LIST_UPDATE_SUCCESS
- ) {
- return;
- }
- const { primaryDeviceID: receivedPrimaryDeviceID, userID } =
- qrCodeAuthMessage;
- setPrimaryDeviceID(receivedPrimaryDeviceID);
-
try {
const nonce = await identityClient.generateNonce();
const nonceChallenge: NonceChallenge = { nonce };
@@ -137,28 +63,9 @@
]);
}
},
- [setUnauthorizedDeviceID, identityClient, deviceKeys],
+ [setUnauthorizedDeviceID, identityClient],
);
- React.useEffect(() => {
- if (!deviceKeys) {
- return () => {};
- }
- addListener(tunnelbrokerMessageListener);
- setUnauthorizedDeviceID(deviceKeys.deviceID);
-
- return () => {
- removeListener(tunnelbrokerMessageListener);
- setUnauthorizedDeviceID(null);
- };
- }, [
- setUnauthorizedDeviceID,
- deviceKeys,
- addListener,
- removeListener,
- tunnelbrokerMessageListener,
- ]);
-
const generateQRCode = React.useCallback(async () => {
try {
const rawAESKey: Uint8Array = await AES.generateKey();
@@ -167,12 +74,13 @@
const ed25519Key: string = await getContentSigningKey();
const url = qrCodeLinkURL(aesKeyAsHexString, ed25519Key);
+ setUnauthorizedDeviceID(ed25519Key);
setQrCodeValue(url);
setDeviceKeys({ deviceID: ed25519Key, aesKey: aesKeyAsHexString });
} catch (err) {
console.error('Failed to generate QR Code:', err);
}
- }, []);
+ }, [setUnauthorizedDeviceID]);
React.useEffect(() => {
void generateQRCode();
@@ -180,29 +88,36 @@
const styles = useStyles(unboundStyles);
return (
- <View style={styles.container}>
- <Text style={styles.heading}>Log in to Comm</Text>
- <Text style={styles.headingSubtext}>
- Open the Comm app on your phone and scan the QR code below
- </Text>
- <QRCode value={qrCodeValue} size={200} />
- <View style={styles.instructionsBox}>
- <Text style={styles.instructionsTitle}>How to find the scanner:</Text>
- <Text style={styles.instructionsStep}>
- <Text>Go to </Text>
- <Text style={styles.instructionsBold}>Profile</Text>
- </Text>
- <Text style={styles.instructionsStep}>
- <Text>Select </Text>
- <Text style={styles.instructionsBold}>Linked devices </Text>
- </Text>
- <Text style={styles.instructionsStep}>
- <Text>Click </Text>
- <Text style={styles.instructionsBold}>Add </Text>
- <Text>on the top right</Text>
+ <>
+ <QRAuthHandler
+ secondaryDeviceID={deviceKeys?.deviceID}
+ aesKey={deviceKeys?.aesKey}
+ performSecondaryDeviceRegistration={performRegistration}
+ />
+ <View style={styles.container}>
+ <Text style={styles.heading}>Log in to Comm</Text>
+ <Text style={styles.headingSubtext}>
+ Open the Comm app on your phone and scan the QR code below
</Text>
+ <QRCode value={qrCodeValue} size={200} />
+ <View style={styles.instructionsBox}>
+ <Text style={styles.instructionsTitle}>How to find the scanner:</Text>
+ <Text style={styles.instructionsStep}>
+ <Text>Go to </Text>
+ <Text style={styles.instructionsBold}>Profile</Text>
+ </Text>
+ <Text style={styles.instructionsStep}>
+ <Text>Select </Text>
+ <Text style={styles.instructionsBold}>Linked devices </Text>
+ </Text>
+ <Text style={styles.instructionsStep}>
+ <Text>Click </Text>
+ <Text style={styles.instructionsBold}>Add </Text>
+ <Text>on the top right</Text>
+ </Text>
+ </View>
</View>
- </View>
+ </>
);
}

File Metadata

Mime Type
text/plain
Expires
Sat, Nov 30, 6:53 AM (19 h, 46 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2600017
Default Alt Text
D11330.id38081.diff (10 KB)

Event Timeline