Skip to content

Commit

Permalink
Merge pull request #42 from displaynone:manual-code
Browse files Browse the repository at this point in the history
feat: allow users to add tokens manually
  • Loading branch information
displaynone authored Apr 29, 2023
2 parents eea1554 + 88ef2a5 commit de3991a
Show file tree
Hide file tree
Showing 73 changed files with 550 additions and 142 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ It is a security protocol used to generate a unique password that changes every

**Shield Authenticator** is an open-source project that aims to provide an alternative to commercial two-factor authentication (2FA) apps like Google Authenticator or Authy. It uses the TOTP protocol to generate one-time passwords, just like these apps do. However, because it is open source, users can review the code and ensure that there are no hidden backdoors or vulnerabilities that could be exploited by attackers. Shield Authenticator is free to use and can be installed on Android mobile devices.

![](./docs/app.png)
![](./docs/captures/en/capture-06.png)

## Developers

Expand Down Expand Up @@ -55,4 +55,8 @@ act --secret-file .env --env-file .env -j preview

##### More info

- [List of 2FA sites](https://2fa.directory/)
- [List of 2FA sites](https://2fa.directory/)

## Screenshots

[English](./docs/captures/en/screenshots.md) | [Español](./docs/captures/es/screenshots.md) | [Italiano](./docs/captures/it/screenshots.md) | [Deutsch](./docs/captures/de/screenshots.md) | [العربية](./docs/captures/ar/screenshots.md) | [中文](./docs/captures/zh/screenshots.md) | [Français](./docs/captures/fr/screenshots.md)
218 changes: 186 additions & 32 deletions app/qr/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,39 @@ import { BarCodeEvent, BarCodeScanner } from 'expo-barcode-scanner';
import { Camera } from 'expo-camera';
import { useRouter } from 'expo-router';
import React, { FC, useEffect, useState } from 'react';
import { Dimensions, Linking, StyleSheet, View } from 'react-native';
import {
Dimensions,
Linking,
Pressable,
ScrollView,
StyleSheet,
View,
} from 'react-native';
import {
ActivityIndicator,
Button,
MD3Theme,
TextInput,
useTheme,
} from 'react-native-paper';
import { ThemeProp } from 'react-native-paper/lib/typescript/src/types';
import BottomSheet from '../../src/components/BottomSheet';
import Section from '../../src/components/Section';
import colors from '../../src/constants/colors';
import { CameraPermissionIcon } from '../../src/icons/CameraPermission';
import { EditIcon } from '../../src/icons/Edit';
import { QrIcon } from '../../src/icons/Qr';
import { ScanQRIcon } from '../../src/icons/ScanQR';
import Text from '../../src/ui/Text';
import { parseOtpUri } from '../../src/util/parseOtpUri';
import { OtpRecord } from '../../src/types';

const QRScanner: FC = () => {
const [hasPermission, setHasPermission] = useState<boolean>();
const [showCamera, setShowCamera] = useState(false);
const [manually, setManually] = useState(false);
const [token, setToken] = useState('');
const [tokenName, setTokenName] = useState('');
const { push } = useRouter();
const theme = useTheme();
const styles = getStyles(theme);
Expand All @@ -40,6 +56,19 @@ const QRScanner: FC = () => {
}
};

const handleTokenManually = () => {
const params: OtpRecord = {
type: 'totp',
algorithm: 'SHA1',
digits: 6,
period: 30,
label: tokenName,
issuer: tokenName,
secret: token,
};
push({ pathname: '/qr/info', params });
};

const resetPermissions = () => {
Linking.openSettings();
};
Expand Down Expand Up @@ -69,48 +98,145 @@ const QRScanner: FC = () => {
</View>
<View style={styles.divider}>
<Button mode="contained" onPress={() => resetPermissions()}>
<Trans>Enable camera</Trans>
<Text>
<Trans>Enable camera</Trans>
</Text>
</Button>
</View>
</Section>
);
}

const inputTheme: ThemeProp = {
colors: {
background: colors.light,
primary: colors.dark,
},
roundness: 4,
};

return (
<Section title={t`Add a new site`} showBack>
<Text size="bodyLarge" variant={'secondary'} numberOfLines={2}>
<Trans>
Scan your website's 2FA QR code using your device's camera
</Trans>
</Text>
{showCamera && (
<>
<View style={styles.cameraContainer}>
<Camera
onBarCodeScanned={handleBarCodeScanned}
style={styles.camera}
ratio="1:1"
/>
</View>
<Text size="bodyMedium" variant={'secondary'} numberOfLines={2}>
<View style={styles.wrapper}>
<ScrollView style={styles.scrolling}>
<Text size="bodyLarge" variant={'secondary'} numberOfLines={4}>
<Trans>
The QR code can be easily recognized by simply pointing your
device's camera at it
You can either use your device's camera to scan the 2FA QR code on
the site, or manually enter the provided token and a name
</Trans>
</Text>
</>
)}
{!showCamera && (
<>
<ScanQRIcon
width={Dimensions.get('screen').width - 48}
height={Dimensions.get('screen').width - 48}
/>
<Button mode="contained" onPress={() => setShowCamera(true)}>
<Trans>Scan QR code</Trans>
</Button>
</>
)}
{showCamera && (
<View>
<View style={styles.cameraContainer}>
<Camera
onBarCodeScanned={handleBarCodeScanned}
style={styles.camera}
ratio="1:1"
/>
</View>
<Text size="bodyMedium" variant={'secondary'} numberOfLines={2}>
<Trans>
The QR code can be easily recognized by simply pointing your
device's camera at it
</Trans>
</Text>
<View style={styles.cancel}>
<Button
mode="contained-tonal"
onPress={() => setShowCamera(false)}
>
<Trans>Cancel</Trans>
</Button>
</View>
</View>
)}
{!showCamera && !manually && (
<View>
<ScanQRIcon
width={Dimensions.get('screen').width - 48}
height={Dimensions.get('screen').width - 48}
/>
</View>
)}
{manually && (
<>
<View>
<Text
variant={['marginless', 'secondary', 'bold']}
size="labelLarge"
>
<Trans>Token name</Trans>
</Text>
<TextInput
value={tokenName}
onChangeText={text => setTokenName(text)}
mode="outlined"
theme={inputTheme}
/>
</View>
<View style={styles.formSpacer}>
<Text
variant={['marginless', 'secondary', 'bold']}
size="labelLarge"
>
<Trans>Token</Trans>
</Text>
<TextInput
value={token}
onChangeText={text => setToken(text)}
mode="outlined"
theme={inputTheme}
/>
</View>
<View style={styles.formSpacer}>
<View style={styles.formSpacer}>
<Button
mode="contained"
onPress={() => handleTokenManually()}
>
<Text variant={['marginless', 'tertiary']}>
<Trans>Save site</Trans>
</Text>
</Button>
</View>
</View>
</>
)}
</ScrollView>
<BottomSheet>
<Pressable
onPress={() => {
setShowCamera(true);
setManually(false);
}}
>
<View style={styles.button}>
<View style={styles.icon}>
<QrIcon color={colors.primary} width={24} height={24} />
</View>
<Text variant={['tertiary', 'marginless']} size="bodyLarge">
<Trans>Scan QR code</Trans>
</Text>
</View>
</Pressable>
<Pressable
onPress={() => {
setShowCamera(false);
setManually(true);
}}
style={styles.spacer}
>
<View style={styles.button}>
<View style={styles.icon}>
<EditIcon color={colors.primary} width={24} height={24} />
</View>
<Text variant={['tertiary', 'marginless']} size="bodyLarge">
<Trans>Add token manually</Trans>
</Text>
</View>
</Pressable>
</BottomSheet>
</View>
</Section>
);
};
Expand All @@ -131,6 +257,34 @@ const getStyles = (theme: MD3Theme) =>
divider: {
marginTop: 24,
},
button: {
flexDirection: 'row',
padding: 8,
borderRadius: 4,
alignItems: 'center',
},
icon: {
margin: 0,
marginRight: 16,
padding: 4,
borderRadius: 4,
backgroundColor: colors.light,
},
wrapper: {
flex: 1,
},
scrolling: {
flex: 0,
},
cancel: {
paddingBottom: 24,
},
spacer: {
marginTop: 4,
},
formSpacer: {
marginTop: 24,
},
});

export default QRScanner;
4 changes: 3 additions & 1 deletion app/qr/info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ const QRInfo: FC = () => {
push('/qr');
}}
>
<Trans>Scan another code</Trans>
<Text>
<Trans>Scan another code</Trans>
</Text>
</Button>
</Container>
);
Expand Down
4 changes: 3 additions & 1 deletion app/settings/export.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ const SettingsExport: FC = () => {
/>
<View style={styles.buttonContainer}>
<Button mode="contained" onPress={() => downloadStringAsFile()}>
<Trans>Generate file</Trans>
<Text variant={['tertiary', 'marginless']}>
<Trans>Generate file</Trans>
</Text>
</Button>
</View>
</Section>
Expand Down
Binary file removed docs/app.png
Binary file not shown.
Binary file added docs/captures/ar/capture-01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/ar/capture-02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/ar/capture-03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/ar/capture-04.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/ar/capture-05.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/ar/capture-06.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions docs/captures/ar/screenshots.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Screenshots

![](./capture-06.png)
![](./capture-05.png)
![](./capture-04.png)
![](./capture-03.png)
![](./capture-02.png)
![](./capture-01.png)
Binary file added docs/captures/de/capture-01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/de/capture-02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/de/capture-03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/de/capture-04.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/de/capture-05.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/de/capture-06.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions docs/captures/de/screenshots.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Screenshots

![](./capture-06.png)
![](./capture-05.png)
![](./capture-04.png)
![](./capture-03.png)
![](./capture-02.png)
![](./capture-01.png)
Binary file added docs/captures/en/capture-01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/en/capture-02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/en/capture-03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/en/capture-04.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/en/capture-05.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/en/capture-06.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions docs/captures/en/screenshots.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Screenshots

![](./capture-06.png)
![](./capture-05.png)
![](./capture-04.png)
![](./capture-03.png)
![](./capture-02.png)
![](./capture-01.png)
Binary file added docs/captures/es/capture-01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/es/capture-02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/es/capture-03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/es/capture-04.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/es/capture-05.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/captures/es/capture-06.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions docs/captures/es/screenshots.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Screenshots

![](./capture-06.png)
![](./capture-05.png)
![](./capture-04.png)
![](./capture-03.png)
![](./capture-02.png)
![](./capture-01.png)
Binary file added docs/captures/fr/capture-01.png
Binary file added docs/captures/fr/capture-02.png
Binary file added docs/captures/fr/capture-03.png
Binary file added docs/captures/fr/capture-04.png
Binary file added docs/captures/fr/capture-05.png
Binary file added docs/captures/fr/capture-06.png
8 changes: 8 additions & 0 deletions docs/captures/fr/screenshots.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Screenshots

![](./capture-06.png)
![](./capture-05.png)
![](./capture-04.png)
![](./capture-03.png)
![](./capture-02.png)
![](./capture-01.png)
Binary file added docs/captures/it/capture-01.png
Binary file added docs/captures/it/capture-02.png
Binary file added docs/captures/it/capture-03.png
Binary file added docs/captures/it/capture-04.png
Binary file added docs/captures/it/capture-05.png
Binary file added docs/captures/it/capture-06.png
8 changes: 8 additions & 0 deletions docs/captures/it/screenshots.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Screenshots

![](./capture-06.png)
![](./capture-05.png)
![](./capture-04.png)
![](./capture-03.png)
![](./capture-02.png)
![](./capture-01.png)
Binary file added docs/captures/zh/capture-01.png
Binary file added docs/captures/zh/capture-02.png
Binary file added docs/captures/zh/capture-03.png
Binary file added docs/captures/zh/capture-04.png
Binary file added docs/captures/zh/capture-05.png
Binary file added docs/captures/zh/capture-06.png
8 changes: 8 additions & 0 deletions docs/captures/zh/screenshots.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Screenshots

![](./capture-06.png)
![](./capture-05.png)
![](./capture-04.png)
![](./capture-03.png)
![](./capture-02.png)
![](./capture-01.png)
Loading

0 comments on commit de3991a

Please sign in to comment.