I am trying to navigate back to the LogIn screen as soon as logout.js is hit.
At one point this seemed to work and then stopped and I can't work out why.
Here is my code:
import React, { Component } from 'react';
import { Text, View, Button } from 'react-native';
import * as SecureStore from 'expo-secure-store';
import { globalStyles } from '../styles/global';
export default class App extends Component {
componentDidMount() {
(async () => {
const token = await SecureStore.getItemAsync('token');
// Your fetch code
this.setState({loaded:false, error: null});
let url = 'https://www.example.com/api/auth/logout';
let h = new Headers();
h.append('Authorization', `Bearer ${token}`);
h.append('Content-Type', 'application/json');
h.append('X-Requested-With', 'XMLHttpRequest');
let req = new Request(url, {
headers: h,
method: 'GET'
});
fetch(req)
.then(response=>response.json())
//.then(this.showData)
.catch(this.badStuff)
})();
this.deleteToken()
}
badStuff = (err) => {
this.setState({loaded: true, error: err.message});
}
deleteToken() {
(async () => {
const token = await SecureStore.deleteItemAsync('token')
});
this.goToLogInScreen()
}
goToLogInScreen() {
this.props.navigation.navigate('LogIn')
}
render() {
return (
<View style={globalStyles.container}>
<Button
title="Go to Details"
onPress={() => this.props.navigation.navigate('LogIn')}
/>
</View>
);
}
}
I need the code to:
Send the command to the API to log out
Delete the token from SecureStore
Navigate to the LogIn screen (containing the log in form).
Related
Trying to achieve the following scenario
Client clicks a button and it opens the Expo web browser with a URL e.g wwww.example.com/test
User does something and is redirected to a URL like wwww.example.com/success
The app recognizes the URL and auto-closes the web browser
It correctly opens the web browser but nothing happens afterward when I go to wwww.example.com/success.
I'm not getting any errors and with the iOS Expo preview, I get no console.log triggers, but with the Web Expo preview, I get generic logging.
Code below
import React, { useState } from 'react';
import { Button, StyleSheet, Text, View } from 'react-native';
import * as Linking from 'expo-linking';
import * as WebBrowser from 'expo-web-browser';
export const WebVerification = () => {
const [redirectData, setRedirectData] = useState(null);
const _handleRedirect = (event) => {
console.log('handle Request is called')
let data = Linking.parse(event.url);
console.log(data)
if( data.url === "wwww.example.com/success") {
WebBrowser.dismissBrowser();
}
setRedirectData(data);
};
const _openBrowserAsync = async () => {
try {
_addLinkingListener();
let result = await WebBrowser.openBrowserAsync(
`wwww.example.com/test`
);
} catch (error) {
alert(error);
console.log(error);
}
};
const _addLinkingListener = () => {
Linking.addEventListener("url", _handleRedirect);
};
const _removeLinkingListener = () => {
Linking.removeEventListener("url", _handleRedirect);
};
const _maybeRenderRedirectData = () => {
console.log("check RenderRedirect" + redirectData)
if (!redirectData) {
return;
}
return (
<Text style={{ marginTop: 30 }}>
{JSON.stringify(redirectData)}
</Text>
);
};
return (
<View>
<Text>Redirect Example</Text>
<Button onPress={_openBrowserAsync} title="Open Browser" />
{_maybeRenderRedirectData()}
</View>
);
};
I wanted to implement login with google feature on my React native app. I'm using expo-cli and I used expo authSession for this.
LoginScreen.js
import * as React from "react";
import * as WebBrowser from "expo-web-browser";
import * as Google from "expo-auth-session/providers/google";
import { Button, View, Text, TouchableOpacity } from "react-native";
WebBrowser.maybeCompleteAuthSession();
export default function LoginScreen() {
const [googleAccessToken, setGoogleAccessToken] = React.useState(null);
const [userInfo, setUserInfo] = React.useState(false);
const [request, response, promptAsync] = Google.useAuthRequest({
expoClientId: "###",
iosClientId: "###",
androidClientId: "###",
webClientId: "###",
});
React.useEffect(() => {
if (response?.type === "success") {
const { authentication } = response;
setGoogleAccessToken(authentication.accessToken);
fetchUserInfo();
}
}, [response]);
const fetchUserInfo = () => {
if (googleAccessToken) {
fetch("https://www.googleapis.com/oauth2/v3/userinfo", {
headers: { Authorization: `Bearer ${googleAccessToken}` },
})
.then((response) => response.json())
.then((userInfoObj) => {
setUserInfo(userInfoObj);
console.log(userInfoObj);
})
.catch((err) => {
console.log(err);
});
}
};
return (
<View>
<Button
disabled={!request}
title="Login Here"
onPress={() => {
promptAsync();
}}
/>
{userInfo ? <Text>{userInfo.email}</Text> : <Text>Nope</Text>}
</View>
);
}
But once, I click LOGIN HERE button and login with google account it doesn't set userInfo state in the first time. I have to click LOGIN HERE button again to login. Even though I have authenticated with google, it doesn't set userInfo state in the first time. But once I click LOGIN HERE again and after continue the process, then it works. How can I solve this issue?
Also I'm getting this warning as well.
Warning
EventEmitter.removeListener('url', ...): Method has been deprecated. Please instead use `remove()` on the subscription returned by `EventEmitter.addListener`.
at node_modules/react-native/Libraries/vendor/emitter/_EventEmitter.js:164:4 in EventEmitter#removeListener
at node_modules/react-native/Libraries/EventEmitter/NativeEventEmitter.js:108:4 in removeListener
at node_modules/react-native/Libraries/Linking/Linking.js:57:4 in removeEventListener
at node_modules/expo-web-browser/build/WebBrowser.js:354:4 in _stopWaitingForRedirect
at node_modules/expo-web-browser/build/WebBrowser.js:347:31 in _openAuthSessionPolyfillAsync
I found the answer for this question by myself. You need to put fetchUserInfo() inside useEffect.
import * as React from "react";
import * as WebBrowser from "expo-web-browser";
import * as Google from "expo-auth-session/providers/google";
import { Button, View, Text, TouchableOpacity } from "react-native";
WebBrowser.maybeCompleteAuthSession();
export default function LoginScreen() {
const [googleAccessToken, setGoogleAccessToken] = React.useState(null);
const [userInfo, setUserInfo] = React.useState(false);
const [request, response, promptAsync] = Google.useAuthRequest({
expoClientId: "###",
iosClientId: "###",
androidClientId: "###",
webClientId: "###",
});
React.useEffect(() => {
const fetchUserInfo = () => {
if (googleAccessToken) {
fetch("https://www.googleapis.com/oauth2/v3/userinfo", {
headers: { Authorization: `Bearer ${googleAccessToken}` },
})
.then(response => response.json())
.then(userInfoObj => {
setUserInfo(userInfoObj);
console.log(userInfoObj);
})
.catch(err => {
console.log(err);
});
}
};
if (response?.type === "success") {
const { authentication } = response;
setGoogleAccessToken(authentication.accessToken);
fetchUserInfo();
}
}, [response]);
return (
<View>
<Button
disabled={!request}
title="Login Here"
onPress={() => {
promptAsync();
}}
/>
{userInfo ? <Text>{userInfo.email}</Text> : <Text>Nope</Text>}
</View>
);
}
I have a simple react native application with a WebView component for displaying my website based on php. So it works. But, when I login and close the application (on IOS), the session state was loose and I need login again. I know that all webview data like cookies cleared when the application close. So I need AsyncStorage to save all cookies to application storage and get it when application open for passing to headers.
Here's my code:
import React, {Component} from 'react';
import AsyncStorage from '#react-native-community/async-storage';
import CookieManager from '#react-native-community/cookies';
import {SafeAreaView, StyleSheet, Alert} from 'react-native';
import {WebView} from 'react-native-webview';
let domain = "[https]://mysite.com";
export default class WebViewScreen extends Component {
constructor(props) {
super(props);
this.currentUrl = domain;
this.myWebView = React.createRef();
this.state = {
isReady: false,
cookiesString: '',
};
}
jsonCookiesToCookieString = (json) => {
let cookiesString = '';
for (let [key, value] of Object.entries(json)) {
cookiesString += `${key}=${value.value}; `;
}
return cookiesString;
};
UNSAFE_componentWillMount() {
this.provideMeSavedCookies()
.then(async (savedCookies) => {
let cookiesString = this.jsonCookiesToCookieString(savedCookies);
const PHPSESSID = await AsyncStorage.getItem('PHPSESSID');
if (PHPSESSID) {
cookiesString += `PHPSESSID=${PHPSESSID};`;
}
this.setState({cookiesString, isReady: true});
})
.catch((e) => {
this.setState({isReady: true});
});
}
onLoadEnd = (syntheticEvent) => {
let successUrl = `${domain}/dashboard.php`;
if (this.currentUrl === successUrl) {
CookieManager.getAll(true).then((res) => {
AsyncStorage.setItem('savedCookies', JSON.stringify(res));
if (res.PHPSESSID) {
AsyncStorage.setItem('PHPSESSID', res.PHPSESSID.value);
}
});
}
};
onNavigationStateChange = (navState) => {
this.currentUrl = navState.url;
};
provideMeSavedCookies = async () => {
try {
let value = await AsyncStorage.getItem('savedCookies');
if (value !== null) {
return Promise.resolve(JSON.parse(value));
}
} catch (error) {
return {}
}
};
render() {
const {cookiesString, isReady} = this.state;
if (!isReady) {
return null;
}
return (
<SafeAreaView>
<WebView
ref={this.myWebView}
source={{
uri: domain,
headers: {
Cookie: cookiesString,
},
}}
allowsBackForwardNavigationGestures
originWhitelist={['*']}
scalesPageToFit
useWebKit
onLoadEnd={this.onLoadEnd}
onNavigationStateChange={this.onNavigationStateChange}
sharedCookiesEnabled={true}
javaScriptEnabled={true}
domStorageEnabled={true}
/>
</SafeAreaView>
);
}
}
I missed all styles for most clearly and understanding code
So, I login and close the application. Then open again. It works only for one request, but not for multiple or when navigation. When I'm trying go to another page I loose my session and redirect to login page. If I change domain variable from mysite.com to mysite.com/my-profile where /my-profile page only for authorized users, it works too. My headers work only for first request page. How to fix it? How save the session after closing the app for each pages, not only for domain value?
Here is my code:, the auth webview open but then when i click allow with my account i can see in console i receive the access token but with an error, i would just like to receive the url in my AuthWebView.js then i can parse it:
Can't open url: epicture://status#access_token=94de672d6210f6ceecfe07efd5d62728c96b8e33&expires_in=315360000&token_type=bearer&refresh_token=cfb46e86a33f4a0cd92e1596335ac21393d13251&account_username=nmateotest&account_id=140192947
code:
import React, { Component } from "react";
import { WebView } from "react-native-webview";
import { Linking } from "react-native";
import { API_URL, API_CLIENT_ID } from "#env";
import APIToken from "./../api/APIToken";
export default class AuthWebView extends Component {
navigate = async (url) => {
if (url) {
console.log(url);
url = url.replace("#", "&");
let params = {};
const regex = /([^&=]+)=([^&]*)/g;
let m;
while ((m = regex.exec(url))) {
parameters[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
}
await APIToken.saveToken("BearerToken", parameters.access_token);
await APIToken.saveToken("username", parameters.account_username);
this.props.navigation.navigate("App");
}
};
componentDidMount() {
Linking.getInitialURL().then((url) => {
this.navigate(url);
});
}
render() {
return (
<WebView
source={{
uri:
API_URL +
"oauth2/authorize?client_id=" +
API_CLIENT_ID +
"&response_type=token",
}}
/>
);
}
So I'm trying to figure out the best way to display a Toast error and success function when the API call fires from redux.
My line of thinking: Create action for the API call. If successful, then I want the screen to change to the home screen. If it fails, then display the message in a Toast.
Here's what some of my actions look like:
export function getTokenAPI(username, password) {
return async function action(dispatch) {
try {
dispatch({ type: t.AUTH_GET_TOKEN });
dispatch(setLoading(true));
const { data } = await API.authGetToken(username, password);
const { success } = data;
if (success) {
const { access_token, refresh_token } = data;
dispatch(setAccessToken(access_token));
dispatch(setRefreshToken(refresh_token));
await dispatch(setLoading(false));
} else if (!success) {
const { errorMessage } = data;
throw Error(errorMessage);
}
} catch (e) {
dispatch(setError(e.message));
dispatch(setLoading(false));
}
};
}
The setError action sets the error key to true and sets the errorMessage. Here's what my screen looks like:
import React from 'react';
import { Container, View, Toast } from 'native-base';
import styles from './styles';
import { connect } from 'react-redux';
import { authActions } from '_ducks/auth';
const LoginScreen = props => {
const { getToken, navigation } = props;
const { navigate } = navigation;
const navigateToHome = () => navigate('Home');
const handleLogin = async () => {
const { error, errorMessage } = props;
await getToken('sample', 'pass123');
if (error) {
Toast.show({
text: errorMessage,
buttonText: 'kay',
});
} else {
navigateToHome();
}
};
return (
<Container>
<View style={styles.container}>
<LoginButton onPress={handleLogin} />
</View>
</Container>
);
};
const mapDispatchToProps = dispatch => ({
getToken: () => dispatch(authActions.getTokenAPI()),
});
const mapStateToProps = state => ({
isLoading: state.authReducer.isLoading,
error: state.authReducer.error,
errorMessage: state.authReducer.errorMessage,
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(LoginScreen);
So if there's an error, then display the toast. If it's successful, navigate to the home screen. Essentially, error will not be true quick enough to make the check within handleLogin work appropriately.
Any recommendations on the pattern or process? Should I be using a useEffect hook here?