feat: add casdoor web login. (#6)
* feat: add casdoor web login. * fix: remove clientSecret and redirectPath, add Alert box for empty input * Update DefaultCasdoorSdkConfig.js --------- Co-authored-by: hsluoyz <hsluoyz@qq.com>
This commit is contained in:
parent
3f927d14c6
commit
a6c1d662d8
14
App.js
14
App.js
|
@ -17,14 +17,24 @@ import {PaperProvider} from "react-native-paper";
|
|||
import NavigationBar from "./NavigationBar";
|
||||
import {NavigationContainer} from "@react-navigation/native";
|
||||
import Header from "./Header";
|
||||
import {UserProvider} from "./UserContext";
|
||||
import {CasdoorServerProvider} from "./CasdoorServerContext";
|
||||
|
||||
export default function App() {
|
||||
const App = () => {
|
||||
const [userInfo, setUserInfo] = React.useState(null);
|
||||
const [casdoorServer, setCasdoorServer] = React.useState(null);
|
||||
return (
|
||||
<CasdoorServerProvider value={{casdoorServer, setCasdoorServer}} >
|
||||
<UserProvider value={{userInfo, setUserInfo}} >
|
||||
<NavigationContainer>
|
||||
<PaperProvider>
|
||||
<Header />
|
||||
<NavigationBar />
|
||||
</PaperProvider>
|
||||
</NavigationContainer>
|
||||
</UserProvider>
|
||||
</CasdoorServerProvider>
|
||||
|
||||
);
|
||||
}
|
||||
};
|
||||
export default App;
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import React, {useEffect} from "react";
|
||||
import {WebView} from "react-native-webview";
|
||||
import {View} from "react-native";
|
||||
import {Portal} from "react-native-paper";
|
||||
import SDK from "casdoor-react-native-sdk";
|
||||
import UserContext from "./UserContext";
|
||||
import PropTypes from "prop-types";
|
||||
import EnterCasdoorSdkConfig from "./EnterCasdoorSdkConfig";
|
||||
import CasdoorServerContext from "./CasdoorServerContext";
|
||||
// import {LogBox} from "react-native";
|
||||
// LogBox.ignoreAllLogs();
|
||||
|
||||
let sdk = null;
|
||||
const CasdoorLoginPage = ({onWebviewClose}) => {
|
||||
CasdoorLoginPage.propTypes = {
|
||||
onWebviewClose: PropTypes.func.isRequired,
|
||||
};
|
||||
const [casdoorLoginURL, setCasdoorLoginURL] = React.useState("");
|
||||
const {setUserInfo} = React.useContext(UserContext);
|
||||
const [showConfigPage, setShowConfigPage] = React.useState(true);
|
||||
const {casdoorServer} = React.useContext(CasdoorServerContext);
|
||||
const handleHideConfigPage = () => {
|
||||
setShowConfigPage(false);
|
||||
};
|
||||
const getCasdoorSignInUrl = async() => {
|
||||
const signinUrl = await sdk.getSigninUrl();
|
||||
setCasdoorLoginURL(signinUrl);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (casdoorServer) {
|
||||
sdk = new SDK(casdoorServer);
|
||||
getCasdoorSignInUrl();
|
||||
}
|
||||
}, [casdoorServer]);
|
||||
|
||||
const onNavigationStateChange = async(navState) => {
|
||||
if (navState.url.startsWith(casdoorServer.redirectPath)) {
|
||||
onWebviewClose();
|
||||
const token = await sdk.getAccessToken(navState.url);
|
||||
const userInfo = sdk.JwtDecode(token);
|
||||
setUserInfo(userInfo);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Portal>
|
||||
<View style={{flex: 1}}>
|
||||
{showConfigPage && <EnterCasdoorSdkConfig onClose={handleHideConfigPage} onWebviewClose={onWebviewClose} />}
|
||||
{!showConfigPage && casdoorLoginURL !== "" && (
|
||||
<WebView
|
||||
source={{uri: casdoorLoginURL}}
|
||||
onNavigationStateChange={onNavigationStateChange}
|
||||
style={{flex: 1}}
|
||||
mixedContentMode="always"
|
||||
javaScriptEnabled={true}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</Portal>
|
||||
);
|
||||
};
|
||||
export const CasdoorLogout = () => {
|
||||
sdk.clearState();
|
||||
};
|
||||
export default CasdoorLoginPage;
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
|
||||
const CasdoorServerContext = React.createContext();
|
||||
export const CasdoorServerProvider = CasdoorServerContext.Provider;
|
||||
export const CasdoorServerConsumer = CasdoorServerContext.Consumer;
|
||||
export default CasdoorServerContext;
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
const DefaultCasdoorSdkConfig = {
|
||||
serverUrl: "https://door.casdoor.com",
|
||||
clientId: "b800a86702dd4d29ec4d",
|
||||
appName: "app-example",
|
||||
organizationName: "casbin",
|
||||
redirectPath: "http://casdoor-app",
|
||||
signinPath: "/api/signin",
|
||||
};
|
||||
export default DefaultCasdoorSdkConfig;
|
|
@ -38,6 +38,7 @@ export default function EnterAccountDetails({onClose, onEdit, placeholder}) {
|
|||
placeholder={placeholder}
|
||||
value={description}
|
||||
onChangeText={(text) => setDescription(text)}
|
||||
autoCapitalize="none"
|
||||
style={{borderWidth: 3, borderColor: "white", margin: 10, width: 230, height: 50, borderRadius: 5, fontSize: 18, color: "gray", paddingLeft: 10}}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
@ -48,8 +48,10 @@ export default function EnterAccountDetails({onClose, onAdd}) {
|
|||
<View style={{flexDirection: "row", alignItems: "center"}}>
|
||||
<IconButton icon="account-details" size={35} />
|
||||
<TextInput
|
||||
label="Description"
|
||||
placeholder="Description"
|
||||
value={description}
|
||||
autoCapitalize="none"
|
||||
onChangeText={(text) => setDescription(text)}
|
||||
style={{borderWidth: 3, borderColor: "white", margin: 10, width: 230, height: 50, borderRadius: 5, fontSize: 18, color: "gray", paddingLeft: 10}}
|
||||
/>
|
||||
|
@ -58,8 +60,10 @@ export default function EnterAccountDetails({onClose, onAdd}) {
|
|||
<View style={{flexDirection: "row", alignItems: "center"}}>
|
||||
<IconButton icon="account-key" size={35} />
|
||||
<TextInput
|
||||
label="Secret code"
|
||||
placeholder="Secret code"
|
||||
value={secretCode}
|
||||
autoCapitalize="none"
|
||||
onChangeText={(text) => setSecretCode(text)}
|
||||
secureTextEntry
|
||||
style={{borderWidth: 3, borderColor: "white", margin: 10, width: 230, height: 50, borderRadius: 5, fontSize: 18, color: "gray", paddingLeft: 10}}
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import React, {useState} from "react";
|
||||
import {Alert, Text, View} from "react-native";
|
||||
import {Button, IconButton, Portal, TextInput} from "react-native-paper";
|
||||
import DefaultCasdoorSdkConfig from "./DefaultCasdoorSdkConfig";
|
||||
import CasdoorServerContext from "./CasdoorServerContext";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const EnterCasdoorSdkConfig = ({onClose, onWebviewClose}) => {
|
||||
EnterCasdoorSdkConfig.propTypes = {
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
||||
const {setCasdoorServer} = React.useContext(CasdoorServerContext);
|
||||
|
||||
const [CasdoorSdkConfig, setCasdoorSdkConfig] = useState({
|
||||
serverUrl: "",
|
||||
clientId: "",
|
||||
appName: "",
|
||||
organizationName: "",
|
||||
redirectPath: "http://casdoor-app",
|
||||
signinPath: "/api/signin",
|
||||
});
|
||||
|
||||
const handleInputChange = (key, value) => {
|
||||
setCasdoorSdkConfig({...CasdoorSdkConfig, [key]: value});
|
||||
};
|
||||
|
||||
const closeConfigPage = () => {
|
||||
onClose();
|
||||
onWebviewClose();
|
||||
};
|
||||
const handleSave = () => {
|
||||
if (
|
||||
!CasdoorSdkConfig.serverUrl ||
|
||||
!CasdoorSdkConfig.clientId ||
|
||||
!CasdoorSdkConfig.appName ||
|
||||
!CasdoorSdkConfig.organizationName ||
|
||||
!CasdoorSdkConfig.redirectPath
|
||||
) {
|
||||
Alert.alert("Please fill in all the fields!");
|
||||
return;
|
||||
}
|
||||
setCasdoorServer(CasdoorSdkConfig);
|
||||
onClose();
|
||||
};
|
||||
const handleUseDefault = () => {
|
||||
setCasdoorServer(DefaultCasdoorSdkConfig);
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<Portal>
|
||||
<View style={{flex: 1, justifyContent: "center", alignItems: "center", backgroundColor: "white"}}>
|
||||
<View style={{top: -60, flex: 1, justifyContent: "center", alignItems: "center", backgroundColor: "white"}}>
|
||||
<Text style={{fontSize: 24, marginBottom: 5}}>Casdoor server</Text>
|
||||
<TextInput
|
||||
label="Endpoint"
|
||||
value={CasdoorSdkConfig.serverUrl}
|
||||
onChangeText={(text) => handleInputChange("serverUrl", text)}
|
||||
autoCapitalize="none"
|
||||
style={{
|
||||
borderWidth: 3,
|
||||
borderColor: "white",
|
||||
margin: 10,
|
||||
width: 300,
|
||||
height: 50,
|
||||
borderRadius: 5,
|
||||
fontSize: 18,
|
||||
color: "gray",
|
||||
paddingLeft: 10,
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="ClientID"
|
||||
value={CasdoorSdkConfig.clientId}
|
||||
onChangeText={(text) => handleInputChange("clientId", text)}
|
||||
autoCapitalize="none"
|
||||
style={{
|
||||
borderWidth: 3,
|
||||
borderColor: "white",
|
||||
margin: 10,
|
||||
width: 300,
|
||||
height: 50,
|
||||
borderRadius: 5,
|
||||
fontSize: 18,
|
||||
color: "gray",
|
||||
paddingLeft: 10,
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="appName"
|
||||
value={CasdoorSdkConfig.appName}
|
||||
onChangeText={(text) => handleInputChange("appName", text)}
|
||||
autoCapitalize="none"
|
||||
style={{
|
||||
borderWidth: 3,
|
||||
borderColor: "white",
|
||||
margin: 10,
|
||||
width: 300,
|
||||
height: 50,
|
||||
borderRadius: 5,
|
||||
fontSize: 18,
|
||||
color: "gray",
|
||||
paddingLeft: 10,
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="organizationName"
|
||||
value={CasdoorSdkConfig.organizationName}
|
||||
onChangeText={(text) => handleInputChange("organizationName", text)}
|
||||
autoCapitalize="none"
|
||||
style={{
|
||||
borderWidth: 3,
|
||||
borderColor: "white",
|
||||
margin: 10,
|
||||
width: 300,
|
||||
height: 50,
|
||||
borderRadius: 5,
|
||||
fontSize: 18,
|
||||
color: "gray",
|
||||
paddingLeft: 10,
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={handleSave}
|
||||
style={{
|
||||
backgroundColor: "#E6DFF3",
|
||||
borderRadius: 5,
|
||||
margin: 10,
|
||||
alignItems: "center",
|
||||
position: "absolute",
|
||||
top: 600,
|
||||
width: 300,
|
||||
height: 50,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Text style={{fontSize: 21, width: 300, color: "black"}}>Confirm</Text>
|
||||
</Button>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={handleUseDefault}
|
||||
style={{
|
||||
backgroundColor: "#E6DFF3",
|
||||
borderRadius: 5,
|
||||
margin: 10,
|
||||
alignItems: "center",
|
||||
position: "absolute",
|
||||
top: 660,
|
||||
width: 300,
|
||||
}}
|
||||
>
|
||||
<Text style={{fontSize: 18, width: 300, color: "black"}}>Use Casdoor Demo Site</Text>
|
||||
</Button>
|
||||
<IconButton icon={"close"} size={30} onPress={closeConfigPage} style={{position: "absolute", top: 120, right: -30}} />
|
||||
</View>
|
||||
</View>
|
||||
</Portal>
|
||||
);
|
||||
};
|
||||
export default EnterCasdoorSdkConfig;
|
61
Header.js
61
Header.js
|
@ -13,14 +13,65 @@
|
|||
// limitations under the License.
|
||||
|
||||
import * as React from "react";
|
||||
import {Appbar, Avatar, Text} from "react-native-paper";
|
||||
import {Appbar, Avatar, Button, Menu, Text} from "react-native-paper";
|
||||
import UserContext from "./UserContext";
|
||||
import {View} from "react-native";
|
||||
import CasdoorLoginPage, {CasdoorLogout} from "./CasdoorLoginPage";
|
||||
|
||||
const Header = () => (
|
||||
const Header = () => {
|
||||
const {userInfo, setUserInfo} = React.useContext(UserContext);
|
||||
const [showLoginPage, setShowLoginPage] = React.useState(false);
|
||||
const [menuVisible, setMenuVisible] = React.useState(false);
|
||||
const openMenu = () => setMenuVisible(true);
|
||||
const closeMenu = () => setMenuVisible(false);
|
||||
const handleMenuLogoutClicked = () => {
|
||||
handleCasdoorLogout();
|
||||
closeMenu();
|
||||
};
|
||||
|
||||
const handleCasdoorLogin = () => {
|
||||
setShowLoginPage(true);
|
||||
};
|
||||
const handleCasdoorLogout = () => {
|
||||
CasdoorLogout();
|
||||
setUserInfo(null);
|
||||
};
|
||||
const handleHideLoginPage = () => {
|
||||
setShowLoginPage(false);
|
||||
};
|
||||
return (
|
||||
<View>
|
||||
<Appbar.Header style={{height: 40}}>
|
||||
<Appbar.Content title="Casdoor" />
|
||||
<Avatar.Image size={32} source={{uri: "https://cdn.casbin.org/img/social_casdoor.png"}} style={{marginRight: 10, backgroundColor: "transparent"}} />
|
||||
<Text style={{marginRight: 10}} variant="titleMedium">Admin</Text>
|
||||
<Menu
|
||||
visible={menuVisible}
|
||||
anchor={
|
||||
<Button
|
||||
style={{marginRight: 10, backgroundColor: "transparent", height: 40}}
|
||||
onPress={userInfo === null ? handleCasdoorLogin : openMenu}
|
||||
>
|
||||
{
|
||||
userInfo === null ?
|
||||
null :
|
||||
<Avatar.Image
|
||||
size={32}
|
||||
source={{uri: userInfo.avatar}}
|
||||
style={{marginRight: 10, backgroundColor: "transparent"}}
|
||||
/>
|
||||
}
|
||||
<Text style={{marginRight: 10}} variant="titleMedium">
|
||||
{userInfo === null ? "Login" : userInfo.name}
|
||||
</Text>
|
||||
</Button>
|
||||
}
|
||||
onDismiss={closeMenu}
|
||||
>
|
||||
<Menu.Item onPress={() => handleMenuLogoutClicked()} title="Logout" />
|
||||
</Menu>
|
||||
</Appbar.Header>
|
||||
);
|
||||
{showLoginPage && <CasdoorLoginPage onWebviewClose={handleHideLoginPage} />}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
|
|
|
@ -15,13 +15,36 @@
|
|||
import * as React from "react";
|
||||
import {Button} from "react-native-paper";
|
||||
import {View} from "react-native";
|
||||
import CasdoorLoginPage, {CasdoorLogout} from "./CasdoorLoginPage";
|
||||
import UserContext from "./UserContext";
|
||||
|
||||
const SettingPage = () => {
|
||||
const [showLoginPage, setShowLoginPage] = React.useState(false);
|
||||
const {userInfo, setUserInfo} = React.useContext(UserContext);
|
||||
const handleCasdoorLogin = () => {
|
||||
setShowLoginPage(true);
|
||||
};
|
||||
const handleCasdoorLogout = () => {
|
||||
CasdoorLogout();
|
||||
setUserInfo(null);
|
||||
};
|
||||
const handleHideLoginPage = () => {
|
||||
setShowLoginPage(false);
|
||||
};
|
||||
|
||||
export default function SettingPage() {
|
||||
return (
|
||||
<View>
|
||||
<Button style={{marginTop: "50%", marginLeft: "20%", marginRight: "20%"}} icon="login" mode="contained" onPress={() => {}}>
|
||||
Login with Casdoor
|
||||
<Button
|
||||
style={{marginTop: "50%", marginLeft: "20%", marginRight: "20%"}}
|
||||
icon={userInfo === null ? "login" : "logout"}
|
||||
mode="contained"
|
||||
onPress={userInfo === null ? handleCasdoorLogin : handleCasdoorLogout}
|
||||
>
|
||||
{userInfo === null ? "Login with Casdoor" : "Logout"}
|
||||
</Button>
|
||||
{showLoginPage && <CasdoorLoginPage onWebviewClose={handleHideLoginPage} />}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default SettingPage;
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
|
||||
const UserContext = React.createContext();
|
||||
export const UserProvider = UserContext.Provider;
|
||||
export const UserConsumer = UserContext.Consumer;
|
||||
export default UserContext;
|
File diff suppressed because it is too large
Load Diff
|
@ -12,6 +12,7 @@
|
|||
"@expo/webpack-config": "^19.0.0",
|
||||
"@react-navigation/bottom-tabs": "^6.5.8",
|
||||
"@react-navigation/native": "^6.1.7",
|
||||
"casdoor-react-native-sdk": "^1.1.0",
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
"expo": "~49.0.8",
|
||||
"expo-barcode-scanner": "^12.5.3",
|
||||
|
@ -20,16 +21,19 @@
|
|||
"prop-types": "^15.8.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-native": "0.72.4",
|
||||
"react-native": "^0.72.5",
|
||||
"react-native-gesture-handler": "^2.12.1",
|
||||
"react-native-paper": "^5.10.3",
|
||||
"react-native-web": "~0.19.6",
|
||||
"react-native-webview": "13.2.2",
|
||||
"totp-generator": "^0.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
"@babel/eslint-parser": "^7.18.9",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@types/react": "~18.2.14",
|
||||
"@types/react-native": "^0.72.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"eslint": "8.22.0",
|
||||
"eslint-import-resolver-babel-module": "^5.3.2",
|
||||
|
|
Loading…
Reference in New Issue