Skip to content

Commit

Permalink
feat: support getting token from clipboard (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
IZUMI-Zu authored Nov 13, 2024
1 parent c593330 commit ad4b879
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 58 deletions.
3 changes: 3 additions & 0 deletions CasdoorLoginPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ function CasdoorLoginPage({onWebviewClose, initialMethod}) {
onWebviewClose();
}}
onLogin={handleQRLogin}
onError={(message) => {
notify("error", {params: {title: "Error", description: message}});
}}
/>
),
webview: casdoorLoginURL && !token && (
Expand Down
30 changes: 21 additions & 9 deletions QRScanner.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {Camera, CameraView, scanFromURLAsync} from "expo-camera";
import * as ImagePicker from "expo-image-picker";
import PropTypes from "prop-types";

const QRScanner = ({onScan, onClose}) => {
const QRScanner = ({onScan, onClose, children}) => {
const [hasPermission, setHasPermission] = useState(null);

useEffect(() => {
Expand Down Expand Up @@ -73,14 +73,25 @@ const QRScanner = ({onScan, onClose}) => {
onPress={onClose}
style={{position: "absolute", top: 30, right: 5}}
/>
<Button
icon="image"
mode="contained"
onPress={pickImage}
style={{position: "absolute", bottom: 20, alignSelf: "center"}}
>
Choose Image
</Button>
<View style={{
position: "absolute",
bottom: 40,
left: 20,
right: 20,
flexDirection: "row",
justifyContent: children ? "space-evenly" : "center",
gap: 16,
}}>
<Button
icon="image"
mode="contained"
onPress={pickImage}
style={children ? {flex: 1} : {width: 200}}
>
Choose Image
</Button>
{children}
</View>
</Portal>
</View>
);
Expand All @@ -89,6 +100,7 @@ const QRScanner = ({onScan, onClose}) => {
QRScanner.propTypes = {
onScan: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
children: PropTypes.node,
};

export default QRScanner;
79 changes: 63 additions & 16 deletions ScanLogin.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,44 +14,91 @@

import React from "react";
import PropTypes from "prop-types";
import * as Clipboard from "expo-clipboard";
import QRScanner from "./QRScanner";
import {Button} from "react-native-paper";

const ScanQRCodeForLogin = ({onClose, showScanner, onLogin, onError}) => {
const handleClipboardPaste = async() => {
const text = await Clipboard.getStringAsync();
if (!isValidLoginQR(text)) {
onError?.("Invalid QR code format");
return;
}

const loginInfo = parseLoginQR(text);
if (!loginInfo) {
onError?.("Missing required fields: serverUrl and accessToken");
return;
}

onLogin(loginInfo);
onClose();
};

const ScanQRCodeForLogin = ({onClose, showScanner, onLogin}) => {
const handleScan = (type, data) => {
if (isValidLoginQR(data)) {
const loginInfo = parseLoginQR(data);
onLogin(loginInfo);
onClose();
if (!isValidLoginQR(data)) {
onError?.("Invalid QR code format");
return;
}

const loginInfo = parseLoginQR(data);
if (!loginInfo) {
onError?.("Missing required fields: serverUrl and accessToken");
return;
}

onLogin(loginInfo);
onClose();
};

const isValidLoginQR = (data) => {
return data.startsWith("casdoor-app://login?");
};

const parseLoginQR = (data) => {
const url = new URL(data);
const params = new URLSearchParams(url.search);

return {
// clientId: params.get("clientId"),
// appName: params.get("appName"),
// organizationName: params.get("organizationName"),
serverUrl: params.get("serverUrl"),
accessToken: params.get("accessToken"),
};
try {
const url = new URL(data);
const params = new URLSearchParams(url.search);

const serverUrl = params.get("serverUrl");
const accessToken = params.get("accessToken");

if (!serverUrl || !accessToken) {
throw new Error("Missing required fields");
}

return {
serverUrl,
accessToken,
};
} catch (error) {
return null;
}
};

if (!showScanner) {
return null;
}

return <QRScanner onScan={handleScan} onClose={onClose} />;
return (
<QRScanner onScan={handleScan} onClose={onClose}>
<Button
icon="clipboard"
mode="contained"
onPress={handleClipboardPaste}
style={{flex: 1}}
>
Paste QR Code
</Button>
</QRScanner>
);
};

ScanQRCodeForLogin.propTypes = {
onClose: PropTypes.func.isRequired,
onLogin: PropTypes.func.isRequired,
onError: PropTypes.func,
showScanner: PropTypes.bool.isRequired,
};

Expand Down
70 changes: 40 additions & 30 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
"casdoor-react-native-sdk": "1.1.0",
"drizzle-orm": "^0.33.0",
"eslint-plugin-import": "^2.28.1",
"expo": "~51.0.38",
"expo": "~51.0.39",
"expo-asset": "~10.0.10",
"expo-camera": "~15.0.16",
"expo-crypto": "~13.0.2",
"expo-dev-client": "~4.0.28",
"expo-dev-client": "~4.0.29",
"expo-document-picker": "~12.0.2",
"expo-drizzle-studio-plugin": "^0.0.2",
"expo-font": "~12.0.10",
Expand Down Expand Up @@ -56,7 +56,8 @@
"react-native-web": "~0.19.6",
"react-native-webview": "13.8.6",
"totp-generator": "^0.0.14",
"zustand": "^4.5.4"
"zustand": "^4.5.4",
"expo-clipboard": "~6.0.3"
},
"verifyConditions": [
"semantic-release-expo",
Expand Down

0 comments on commit ad4b879

Please sign in to comment.