Okta authentication, how to use the response.params? - expo

We are trying to use Expo authentication with Okta as stated here:
https://docs.expo.dev/guides/authentication/#okta
Expo has very good documentation for lot's of stuff, but for the Okta authentication unfortunately we could not sort out how to use the library in a correct way.
Currently, with lot's of suffering (mostly because of the ambiguity in Okta's configuration pages), we came to a certain point where the following code correctly responds the code parameter. This is the exact same part from Expo documentation:
React.useEffect(() => {
if (response?.type === 'success') {
const { code } = response.params;
}
}, [response]);
But unfortunately we could not find any method how we can use the parameter code to get the scope information, email, name, etc...
Can anybody guide us how we can use the object code to retrieve these data? (The Okta documentation is not clear for this either, so we are stuck.)
Edit 1:
The response has the following structure:
response: {
"type": "success",
"error": null,
"url": "http://localhost:19006/?code=fUMjE4kBX2QZXXXXXX_XXXXXXXMQ084kEPrTqDa9FTs&state=3XXXXXXXXz",
"params": {
"code": "fUMjE4kBX2QZXXXXXX_XXXXXXXMQ084kEPrTqDa9FTs",
"state": "3XXXXXXXXz"
},
"authentication": null,
"errorCode": null
}
Edit 2:
Calling exchangeCodeAsync also yields errors.
Code:
const tokenRequestParams = {
code: code,
clientId: config.okta.clientId,
redirectUri: oktaRedirectUri,
extraParams: {
code_verifier: authRequest.codeVerifier
},
}
const tokenResult = await exchangeCodeAsync(tokenRequestParams, discovery);
Error:
TokenRequest.ts:205 Uncaught (in promise) Error: Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The authorization server MAY return an HTTP 401 (Unauthorized) status code to indicate which HTTP authentication schemes are supported. If the client attempted to authenticate via the "Authorization" request header field, the authorization server MUST respond with an HTTP 401 (Unauthorized) status code and include the "WWW-Authenticate" response header field matching the authentication scheme used by the client.
More info: Client authentication failed. Either the client or the client credentials are invalid.
at AccessTokenRequest.<anonymous> (TokenRequest.ts:205:1)
at Generator.next (<anonymous>)
at asyncGeneratorStep (asyncToGenerator.js:3:1)
at _next (asyncToGenerator.js:22:1)
PS: I asked the same question to the Expo forums also here. If we can solve there, I plan to reflect to here also for wider audience. (The method can be related with Okta rather than Expo itself.)

there are two ways to use Okta in React Native
1. By Restful APIs, using fetch/Axios
2. By Using Native SDK
By Restful APIs, using fetch/Axios
here is the full code of okta using restful
import React, { useState } from "react";
import {
ScrollView,
StyleSheet,
Text,
View,
TouchableOpacity,
Platform,
} from "react-native";
import {
useAutoDiscovery,
useAuthRequest,
makeRedirectUri,
exchangeCodeAsync,
} from "expo-auth-session";
import { maybeCompleteAuthSession } from "expo-web-browser";
import axios from "axios";
const oktaConfig = {
okta_issuer_url: "",
okta_client_id: "",
okta_callback_url: "com.okta.<OKTA_DOMAIN>:/callback",
};
export default App = (props) => {
const useProxy = true;
if (Platform.OS === "web") {
maybeCompleteAuthSession();
}
const discovery = useAutoDiscovery(oktaConfig.okta_issuer_url);
// When promptAsync is invoked we will get back an Auth Code
// This code can be exchanged for an Access/ID token as well as
// User Info by making calls to the respective endpoints
const [authRequest, response, promptAsync] = useAuthRequest(
{
clientId: oktaConfig.okta_client_id,
scopes: ["openid", "profile"],
redirectUri: makeRedirectUri({
native: oktaConfig.okta_callback_url,
useProxy,
}),
},
discovery
);
async function oktaCognitoLogin() {
const loginResult = await promptAsync({ useProxy });
ExchangeForToken(loginResult, authRequest, discovery);
}
return (
<View style={styles.container}>
<View style={styles.buttonContainer}>
<TouchableOpacity
style={styles.equalSizeButtons}
onPress={() => oktaCognitoLogin()}
>
<Text style={styles.buttonText}>Okta Login</Text>
</TouchableOpacity>
</View>
<ScrollView>
{response && <Text>{JSON.stringify(response, null, 2)}</Text>}
</ScrollView>
</View>
);
};
this is how, we can get exchange token and then get user info by using restful api
//After getting the Auth Code we need to exchange it for credentials
async function ExchangeForToken(response, authRequest, discovery) {
// React hooks must be used within functions
const useProxy = true;
const expoRedirectURI = makeRedirectUri({
native: oktaConfig.okta_callback_url,
useProxy,
})
const tokenRequestParams = {
code: response.params.code,
clientId: oktaConfig.okta_client_id,
redirectUri: expoRedirectURI,
extraParams: {
code_verifier: authRequest.codeVerifier
},
}
const tokenResult = await exchangeCodeAsync(
tokenRequestParams,
discovery
)
const creds = ExchangeForUser(tokenResult)
const finalAuthResult = {
token_res : tokenResult,
user_creds : creds
}
console.log("Final Result: ", finalAuthResult)
}
this is how we can get user info by using restful api
async function ExchangeForUser(tokenResult) {
const accessToken = tokenResult.accessToken;
const idToken = tokenResult.idToken;
//make an HTTP direct call to the Okta User Info endpoint of our domain
const usersRequest = `${oktaConfig.okta_issuer_url}/v1/userinfo`
const userPromise = await axios.get(usersRequest, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
console.log(userPromise, "user Info");
}
const styles = StyleSheet.create({
container: {
margin: 10,
marginTop: 20,
},
buttonContainer: {
flexDirection: "row",
alignItems: "center",
margin: 5,
},
equalSizeButtons: {
width: "50%",
backgroundColor: "#023788",
borderColor: "#6df1d8",
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
padding: 9,
borderWidth: 1,
shadowColor: "#6df1d8",
shadowOpacity: 8,
shadowRadius: 3,
shadowOffset: {
height: 0,
width: 0,
},
},
buttonText: {
color: "#ffffff",
fontSize: 16,
},
});
Reference Code
By Using Native SDK
for native SDK, you can use okta-react-native package like this
Login Screen
import React from 'react';
import {
Alert,
Button,
StyleSheet,
TextInput,
View,
ActivityIndicator
} from 'react-native';
import {
signIn,
introspectIdToken
} from '#okta/okta-react-native';
export default class CustomLogin extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
username: '',
password: '',
};
}
async componentDidMount() {
}
signInCustom = () => {
this.setState({ isLoading: true });
signIn({ username: this.state.username, password: this.state.password })
.then(() => {
introspectIdToken()
.then(idToken => {
this.props.navigation.navigate('ProfilePage', { idToken: idToken, isBrowserScenario: false });
}).finally(() => {
this.setState({
isLoading: false,
username: '',
password: '',
});
});
})
.catch(error => {
// For some reason the app crashes when only one button exist (only with loaded bundle, debug is OK) 🤦‍♂️
Alert.alert(
"Error",
error.message,
[
{
text: "Cancel",
onPress: () => console.log("Cancel Pressed"),
style: "cancel"
},
{ text: "OK", onPress: () => console.log("OK Pressed") }
]
);
this.setState({
isLoading: false
});
});
}
render() {
if (this.state.isLoading) {
return (
<View style={styles.container}>
<ActivityIndicator size="large" />
</View>
);
}
return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder='Username'
onChangeText={input => this.setState({ username: input })}
testID="username_input"
/>
<TextInput
style={styles.input}
placeholder='Password'
onChangeText={input => this.setState({ password: input })}
testID="password_input"
/>
<Button
onPress={this.signInCustom}
title="Sign in"
testID='sign_in_button'
/>
<View style={styles.flexible}></View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
input: {
height: 40,
width: '80%',
margin: 12,
borderWidth: 1,
padding: 10,
},
flexible: {
flex: 1,
}
});
Profile Screen
import React from 'react';
import {
Text,
Button,
StyleSheet,
TextInput,
View,
} from 'react-native';
import {
signOut,
revokeAccessToken,
revokeIdToken,
clearTokens,
} from '#okta/okta-react-native';
export default class ProfilePage extends React.Component {
constructor(props) {
super(props);
this.state = {
idToken: props.route.params.idToken,
isBrowserScenario: props.route.params.isBrowserScenario
};
}
logout = () => {
if (this.state.isBrowserScenario == true) {
signOut().then(() => {
this.props.navigation.popToTop();
}).catch(error => {
console.log(error);
});
}
Promise.all([revokeAccessToken(), revokeIdToken(), clearTokens()])
.then(() => {
this.props.navigation.popToTop();
}).catch(error => {
console.log(error);
});
}
render() {
return (
<View style={styles.container}>
<Text testID="welcome_text">Welcome back, {this.state.idToken.preferred_username}!</Text>
<Button
onPress={this.logout}
title="Logout"
testID="logout_button"
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Reference Code

Related

React Native AWS S3 upload

I'm using react-native-image-picker and react-native-s3-upload to upload images to s3 bucket.
RNS3.put(file, AWSOptions)
.then((response) => {
if (response.status !== 201) {
throw new Error('Failed to upload image to S3');
}
})
.catch(function (error) {
console.log('Error', JSON.stringify(error));
throw error;
});
and I get a response like this without image been uploaded.
Object {
"headers": Object {},
"status": 0,
"text": "Stream Closed",
}
I have exactly the same issue with a similar library called react-native-aws3.
It seems related to the new React Native version 0.62.2, because I've tried to upgrade today (my previous version was 0.61.5) and that library worked before.
I don't know if both libraries use a common API which now is broken in the new RN, but I suggest you to downgrade in the meanwhile.
I had the same issue and tried for days to fix it. This is what I came up with.
Make sure you follow the amplify guide and set up amplify init, amplify add auth and then do amplify push
Then do amplify add storage and follow the guide here (https://docs.amplify.aws/lib/storage/upload/q/platform/js). Then copy this code.
import Amplify, { Storage } from 'aws-amplify'
import config from './src/aws-exports'
// import awsconfig from './aws-exports';
// Might need to switch line 7 to awsconfig
Amplify.configure(config)
import { StatusBar } from 'expo-status-bar';
import React, { useState, useEffect } from 'react';
import { Button, Image, View, Platform, StyleSheet, Text } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
function App() {
const [image, setImage] = useState(null)
useEffect(() => {
(async () => {
if (Platform.OS !== 'web') {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== 'granted') {
alert('Sorry, we need camera roll permissions to make this work!');
}
}
})();
}, []);
const pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
console.log(result)
async function pathToImageFile(data) {
try {
const response = await fetch(data);
const blob = await response.blob();
await Storage.put(`customer/images`, blob, {
contentType: 'image/jpeg', // contentType is optional
});
} catch (err) {
console.log('Error uploading file:', err);
}
}
// later
pathToImageFile(result.uri);
}
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button title="Pick an image from camera roll" onPress={pickImage} />
{image && <Image source={{ uri: image }} style={{ width: 200, height: 200 }} />}
<Button title="Upload image" onPress={() => {alert(image)}} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
export default withAuthenticator(App)```
I found an alternative module (aws-sdk) to upload files to the S3 bucket with RN 62 version.
Also, I have used base64-arraybuffer and react-native-fs modules well.
const s3bucket = new S3({
accessKeyId: XXXXXXXXXX,
secretAccessKey: XXXXXXXXXXXX,
Bucket: XXXXXXXXX,
signatureVersion: 'v4',
});
let contentType = 'image/jpeg';
let contentDeposition = 'inline;filename="' + file.name + '"';
const base64 = await fs.readFile(file.uri, 'base64');
const arrayBuffer = decode(base64);
s3bucket.createBucket(() => {
const params = {
Bucket: XXXXXXXXXXX,
Key: file.name,
Body: arrayBuffer,
ContentDisposition: contentDeposition,
ContentType: contentType,
};
s3bucket.upload(params, (err, data) => {
if (err) {
console.log('error in callback');
console.log(err);
setSpinner(false);
}
console.log('success');
console.log(data.Location);
setImageUrl(data.Location);
setSpinner(false);
});
});
The problem is with RN >= 0.62
You can either roll back to version < 0.62 or you can follow what I did.
I solved it by commenting out line number 43 in android/app/src/debug/java/**/ReactNativeFlipper.java
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
NetworkingModule.setCustomClientBuilder(
new NetworkingModule.CustomClientBuilder() {
#Override
public void apply(OkHttpClient.Builder builder) {
// builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); <---- This line
}
});
client.addPlugin(networkFlipperPlugin);
client.start();
You will need to rebuild the app after doing this.
Hope this helps.
AWS S3 upload is a powerful service provided by React Native AWS amplify. The issue here might be caused by the React Native version or improper configuration of the package. You can check some amazing articles out there that will definitely help you integrate React Native AWS amplify properly based on your project configurations and make the AWS S3 upload working properly as well.

Invalid hook call. Hooks can only be called inside of the body of a function

I was trying to develop a login page with the Django rest framework as the backend. The backend is working perfectly whereas I can't even set up react js. I am getting an error in the Index.js file of react. It tells "Invalid hook call. Hooks can only be called inside of the body of a function component"
This is what the error I get
App.js
import React from 'react';
import './App.css';
import Paperbase from './Layout/Paperbase'
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Login from './Layout/Login/Login'
import Register from './Layout/Register/Register'
export function App() {
return (
<BrowserRouter>
<Switch>
<Route path="/dashboard" render={() => <Paperbase /> } />
<Route path="/account/login" render={() =><Login />} />
<Route path="/account/register" render={() => <Register />} />
</Switch>
</BrowserRouter>
)
}
export default App
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import store from './store';
import { Provider } from 'react-redux';
import { render } from 'react-dom';
ReactDOM.render(
(<Provider store={store}>
<App/>
</Provider>),
document.getElementById('root') || document.createElement('div') // for testing purposes
);
serviceWorker.unregister();
Login.js
import React from 'react';
import Avatar from '#material-ui/core/Avatar';
import Button from '#material-ui/core/Button';
import CssBaseline from '#material-ui/core/CssBaseline';
import TextField from '#material-ui/core/TextField';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Checkbox from '#material-ui/core/Checkbox';
import Grid from '#material-ui/core/Grid';
import LockOutlinedIcon from '#material-ui/icons/LockOutlined';
import Typography from '#material-ui/core/Typography';
import Container from '#material-ui/core/Container';
import { withStyles } from '#material-ui/core/styles';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { login } from '../../actions/auth';
const styles = theme => ({
'#global': {
body: {
backgroundColor: theme.palette.common.white,
},
},
paper: {
marginTop: theme.spacing(25),
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.primary.light,
},
form: {
width: '100%', // Fix IE 11 issue.
marginTop: theme.spacing(1),
},
submit: {
margin: theme.spacing(3, 0, 2),
backgroundColor: theme.palette.primary.light,
},
});
class SignIn extends React.Component {
state = {
email: '',
password: '',
};
static propTypes = {
login: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool,
};
onSubmit = (e) => {
e.preventDefault();
this.props.login(this.state.email, this.state.password);
};
onChange = (e) => this.setState({ [e.target.name]: e.target.value });
render() {
const { classes } = this.props;
const { email, password } = this.state;
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<form className={classes.form} onSubmit={this.onSubmit}>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="email"
label="Email Address"
name="email"
autoComplete="email"
autoFocus
onChange={this.onChange}
value={email}
/>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
autoComplete="current-password"
onChange={this.onChange}
value={password}
/>
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Sign In
</Button>
<Grid container>
</Grid>
</form>
</div>
</Container>
);
}
}
const mapStateToProps = (state) => ({
isAuthenticated: state.auth.isAuthenticated,
});
export default (withStyles(styles)(SignIn));
authreducer.js
import {
USER_LOADED,
USER_LOADING,
AUTH_ERROR,
LOGIN_SUCCESS,
LOGIN_FAIL,
LOGOUT_SUCCESS,
REGISTER_SUCCESS,
REGISTER_FAIL,
} from '../actions/types';
const initialState = {
token: localStorage.getItem('token'),
isAuthenticated: null,
isLoading: false,
user: null,
};
export default function (state = initialState, action) {
switch (action.type) {
case USER_LOADING:
return {
...state,
isLoading: true,
};
case USER_LOADED:
return {
...state,
isAuthenticated: true,
isLoading: false,
user: action.payload,
};
case LOGIN_SUCCESS:
case REGISTER_SUCCESS:
localStorage.setItem('token', action.payload.token);
return {
...state,
...action.payload,
isAuthenticated: true,
isLoading: false,
};
case AUTH_ERROR:
case LOGIN_FAIL:
case LOGOUT_SUCCESS:
case REGISTER_FAIL:
localStorage.removeItem('token');
return {
...state,
token: null,
user: null,
isAuthenticated: false,
isLoading: false,
};
default:
return state;
}
}
authactions.js
import axios from 'axios';
import {
USER_LOADED,
USER_LOADING,
LOGIN_SUCCESS,
LOGOUT_SUCCESS,
REGISTER_SUCCESS,
} from './types';
// CHECK TOKEN & LOAD USER
export const loadUser = () => (dispatch, getState) => {
// User Loading
dispatch({ type: USER_LOADING });
axios
.get('http://localhost:8000/api/auth/user', tokenConfig(getState))
.then((res) => {
dispatch({
type: USER_LOADED,
payload: res.data,
});
})
.catch((err) => {
console.log(err)
});
};
// LOGIN USER
export const login = (email, password) => (dispatch) => {
// Headers
const config = {
headers: {
'Content-Type': 'application/json',
},
};
const body = JSON.stringify({ email, password });
axios
.post('http://localhost:8000/api/auth/login', body, config)
.then((res) => {
dispatch({
type: LOGIN_SUCCESS,
payload: res.data,
});
})
.catch((err) => {
console.log(err.response.data)
});
};
// REGISTER USER
export const register = ({ username, password, email }) => (dispatch) => {
// Headers
const config = {
headers: {
'Content-Type': 'application/json',
},
};
// Request Body
const body = JSON.stringify({ username, email, password });
axios
.post('http://localhost:8000/api/auth/register', body, config)
.then((res) => {
dispatch({
type: REGISTER_SUCCESS,
payload: res.data,
});
})
.catch((err) => {
console.log(err.response.data)
});
};
// LOGOUT USER
export const logout = () => (dispatch, getState) => {
axios
.post('http://localhost:8000/api/auth/logout/', null, tokenConfig(getState))
.then((res) => {
dispatch({ type: 'CLEAR_LEADS' });
dispatch({
type: LOGOUT_SUCCESS,
});
})
.catch((err) => {
console.log(err.response.data)
});
};
// Setup config with token - helper function
export const tokenConfig = (getState) => {
// Get token from state
const token = getState().auth.token;
// Headers
const config = {
headers: {
'Content-Type': 'application/json',
},
};
// If token, add to headers config
if (token) {
config.headers['Authorization'] = `Token ${token}`;
}
return config;
};
Based on my experience, this error often occurs due to the fact that one of the packages in package.json (often it's React) is in dependencies (not in devDependencies). And this version conflicts with yours.

What is the format for using get() from AWS S3?

I am currently making an application using react-native, expo, and AWS. I use Cognito for authentification and I have it set up such that I can successfully mutate and query a database in DynamoDB.
I am now attempting to write code such that I can store the pictures for the users of my application in S3 and store the path to that picture in DynamoDB. However, upon running the following:
const result2 = await Storage.get('test.txt')
console.log(result2)
The log gives me a link that takes me to what appears to be an xml file that reads:
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<Error>
<Code>InvalidToken</Code>
<Message>
The provided token is malformed or otherwise invalid.
</Message>
<Token-0>
AgoJb3JpZ2luX2VjEA4aCXVzLWVhc3QtMiJIMEYCIQD/DyNL3qxQ3XrXBXsOlH2ojEks6sfdN9Vp7rPT5K9JFQIhAI2lEAmu6ivQIQKNtAo1VqFuZgdld5S/DrfcElNsVDo/Kq8FCOf//////////wEQABoMODM3Njc2NjA0ODkyIgwj9ikFX4w7POTaQlEqgwXkzZRq7kCqo8W0YaAD6fT7sJ4nok4nxzBO58frPZ4a1nSCESuwq/VShJZMQAMDkDPKci4vV v aJMhPOJXvtRjkoIQ5lGaCQAqYMXDeU/RFOJosmekNZ 0vZGu/X9KWfcNFhJaz7WW5FDl3N3u BGQdBzUjNhCrl01HMpKyL5F0HFB1mq4l H9d FyvPD4 LStyNOXYkGKf7X34hEZdccRkbjDyzfG4hBmv64JGiJfgh5 dD6IlNdNsqkw7N1Uw26Q7mf3erhOrwJwLIDyYC77LW3HU9uLqxQBH0MiTxJBcWW47BT4xENuSHZ136zRazgjHYcin Sr1NyCKwI4e2BbJ//Fudi Jnjf/QDPgYmSrpOO/0p5YGejpPYoZ6cqFy7V8xuI82YK/CZTc5hCOBMvo7NyPa2lsGkDXcfh IknmVEH5OXHhM0FPGM/U6L0ikBUYJgqqiVqByxdrUyYt F6FxapyCKgWcBCXwQUq28KoFvhIqXFbc5hlkGxFdUvIK2WWtMpDntcvU6hgivXoRRGKh880vkQV6bQsF3AaniH/El0EcI5jGF5aiciNZo9wNuMKMsA8w7bpT hBDETHeGOd3lenGNvgazdwWKnnhqN3QqwaSPFjd0Q41wNYOH8ySmP0S0/UbYCTIwRDx1o4FX6UI2THnS8nOvbtEG32QHDpqY3gpL00tCLGO4S9BFtkIC2TbhvX7jfF3bAbfvjEVOFhENT0K v OYq02Ig4dlMa4DdnPexEmy666R8Pbf 7fTCP2wzWDXjfxJjxkKx8gfsTmUOgDgUPb2p8azxSXzJr1Rg5 zhhX0DuVepg5z66HQS23RwAhIkOrXEzi95nPbd9NAOMNPw0OwFOrMB4lBUn04vbQAqDiKOe91AZR64351xwrn0jp5zZ YwumK9Tk/3rY/ N2ibYoiHdfOM3Wg4Ucm2 qCDWtJIkbvCAXlhjrpUZLmV6dJATJ417OzlUlJfsK04y7K8uNrGSFwbWYw3/gblaggupxQc7TrpmhKCIXHVIdZZmawMH5vfULkhHQL/FiQKdt13znIZ/hHGs1jc3cC9Z NmhMIXtufNlIGvKmWzNCq8LegxNzoZtlXqVWM=
</Token-0>
<RequestId>5186B48D259D1B01</RequestId>
<HostId>
RJptwOZEArdnFf7Zmk7Od8oFWh68GeR8ey/O13M39JuVxSScYyKXVVVsXwa+37Wus1M/DHttry8=
</HostId>
</Error>
For reference, the rest of my App.js file reads:
/*
General
*/
import React from 'react';
import { Platform, Component, StyleSheet, Text, View, TextInput, Button, TouchableOpacity } from 'react-native';
/*
Amplify
*/
import Amplify from '#aws-amplify/core'
import config from './src/aws-exports'
Amplify.configure({
...config,
Analytics: { //Needed to disable possible promise reject error from analytics
disabled: true
}
});
/*
Authentification
*/
import Auth from '#aws-amplify/auth'
import { withAuthenticator } from 'aws-amplify-react-native'
/*
Database
*/
import API, { graphqlOperation } from '#aws-amplify/api'
import {Storage} from 'aws-amplify'
import * as queries from './src/graphql/queries';
import * as mutations from './src/graphql/mutations';
import * as subscriptions from './src/graphql/subscriptions';
import {v4 as uuid} from 'uuid';
class App extends React.Component {
state = {
name: "",
User: []
}
onChangeText = (key, val) => {
this.setState({ [key]: val })
}
addUser = async event => {
const { name, User } = this.state
event.preventDefault()
const information = {
username: name,
}
const result = await API.graphql(graphqlOperation(mutations.createUser, {input: information}))
const newUser = result.data.createUser
const updatedUser = [newUser, ...User]
this.setState({ User: updatedUser, name: "" })
/*
Adding Files Test
*/
const result2 = await Storage.get('test.txt')
console.log(result2)
}
render() {
return (
<View style={styles.container}>
<TextInput
style={styles.input}
value={this.state.name}
onChangeText={val => this.onChangeText("name", val)}
placeholder='Add a User'
/>
<TouchableOpacity onPress={this.addUser} style={styles.buttonContainer}>
<Text style={styles.buttonText}>Add +</Text>
</TouchableOpacity>
</View>
);
}
}
export default withAuthenticator(App, { includeGreetings: true })
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
paddingHorizontal: 10,
paddingTop: 50
},
input: {
height: 50,
borderBottomWidth: 2,
borderBottomColor: "blue",
marginVertical: 10
},
buttonContainer: {
backgroundColor: "#34495e",
marginTop: 10,
marginBottom: 10,
padding: 10,
borderRadius: 5,
alignItems: "center"
},
buttonText: {
color: "#fff",
fontSize: 24
}
})
What would this error mean? Am I simply giving get() the wrong type of input? I do have a file called "test.txt" in the top level of my S3.

How can I rename the captured image in react native begore uploading to aws s3?

I am beginner in react native. I want to rename the image before uploading to AWS s3. Till uploading to AWS s3 I have done. But I am not getting how to rename the image before uploading.
For example, my captured image name is : image-3bcf276c-cab1-4400-83a6-8f92971977c8.jpg . I want to change it like : b514c19662714234_geolocation_222222222222222.png
How can I do this.
My file uploading code is,
import React, {Component} from 'react';
import {Platform, StyleSheet,Alert, Text,TouchableOpacity, View} from 'react-native';
import { Dropdown } from 'react-native-material-dropdown';
import ImagePicker from 'react-native-image-picker';
import DeviceInfo from 'react-native-device-info';
import { RNS3 } from 'react-native-aws3';
export default class App extends Component<Props> {
static navigationOptions = {
title: 'Check In',
};
constructor(props) {
super(props);
this.state = {
deviceId: '',
isCameraVisiable: false,
latitude: null,
longitude: null,
error: null,
serverTime: null,
file :'',
saveImages : []
}
}
handleSubmit(){
var date = new Date();
var time = date.getTime();
console.log(time);
// var data = {}
// data['time'] = time;
Alert.alert('You cliked on send!');
}
getServerTime() {
fetch('http://worldclockapi.com/api/json/utc/now')
.then((response) => response.json())
.then((responseJson) => {
if (responseJson) {
this.setState({
serverTime: responseJson
})
}
console.log(responseJson);
})
.catch((error) => {
console.error(error);
});
}
componentDidMount() {
navigator.geolocation.getCurrentPosition(
(position) => {
this.setState({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
error: null,
});
},
(error) => this.setState({ error: error.message }),
{ enableHighAccuracy: true, timeout: 20000 },
);
console.log(this.state.longitude);
console.log(this.state.error);
}
takePic(){
ImagePicker.launchCamera({},(responce)=>{
console.log(responce);
const file ={
uri : responce.uri,
name : responce.fileName,
method: 'POST',
path : responce.path,
type : responce.type,
notification: {
enabled: true
}
}
console.log('fffffffffffffffffffffffffffffffffffffffffffffff');
console.log(file);
this.state.saveImages.push(file);
console.log('=============********************================');
console.log(this.state.saveImages);
})
}
_upload=(saveImages)=>{
const config ={
keyPrefix :'uploads/',
bucket : 's3merahkee',
region :'***********',
accessKey:'************',
secretKey :'**************',
successActionStatus :201
}
this.state.saveImages.map((image) => {
RNS3.put(image,config)
.then((responce) => {
console.log('=============********************================');
console.log(saveImages);
Alert.alert('You cliked on send!');
});
});
}
render() {
return (
<View style={styles.container}>
<View style={styles.Camera}>
<TouchableOpacity onPress={this.takePic.bind(this)}>
<Text>Take Picture</Text>
</TouchableOpacity>
</View>
<View style={styles.Send}>
<TouchableOpacity onPress={() => this._upload()}>
<Text>Send</Text>
</TouchableOpacity>
<View>
<Text>Latitude: {this.state.latitude}</Text>
<Text>Longitude: {this.state.longitude}</Text>
<Text>DeviceUniqueId: {DeviceInfo.getUniqueID()}</Text>
{this.state.error ? <Text>Error: {this.state.error}</Text> : null}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
Dropdown :{
},
Camera :{
justifyContent: 'center',
alignItems: 'center',
marginTop : 20,
backgroundColor : '#48a4ff',
alignItems : 'center',
padding : 1,
borderWidth : 1,
borderColor : '#48a4ff',
},
Send :{
justifyContent: 'center',
alignItems: 'center',
marginTop : 20,
backgroundColor : '#48a4ff',
alignItems : 'center',
padding : 3,
borderWidth : 1,
borderColor : '#48a4ff',
}
});
I have done it in a simple way.Instead of writing name : responce.fileName, I mentioned which name I want to give to it i.e
name : DeviceInfo.getUniqueID()+'_'+this.state.latitude+'_'+this.state.longitude+'.jpg',
//simply name='new_image.png';

Extracting Token from Django AuthTokenSerializer with React native

I am trying to extract a authorization token from the restframework of Django authtoken and can't seem to get the response token from the request. By using Postman I get the token with status 200. But when I try to do this with Fetch in my code I get status 200 a different response. Response from POST request
From the server's perspective I get the POST request with 200 response.I believe it's just formatting of the response but I can't seem to figure out how.
Below is the code for my Login Form.
import React from 'react';
import {View,Text, TextInput, TouchableOpacity, StyleSheet} from 'react-native';
import {createStackNavigator} from 'react-navigation';
const fetch_url = 'http://192.168.0.17:8000/api/api-auth/';
export default class LoginForm extends React.Component {
constructor(props){
super(props);
this.loginHTTPRequest = this.loginHTTPRequest.bind(this);
this.state = {
username: '',
password:'',
respons:'',
error:null,
loading: false,
}
}
loginHTTPRequest(){
if(this.state.username != '' && this.state.password != ''){
this.setState({
loading:true,
});
fetch(fetch_url,{
method:'POST',
headers:{
Accept:'application/json',
'Content-Type':'application/json',
},
body: JSON.stringify({
username:'admin',
password:'admin12345',
}),
})
.then((response)=>{
if (response.status >= 200 && response.status < 300) {
//Where I want to extract the token from the response
console.log(response);
}})
.catch((error) => {
this.setState({
error:error,
loading:false,
});
});
}
}
render(){
return (
<View style={styles.container}>
<TextInput
style={styles.input}
autoCapitalize = 'none'
autoCorrect={false}
returnKeyType='next'
placeholder = 'Username'
onChangeText={(text)=> this.setState({username:text})}
/>
<TextInput
style={styles.input}
autoCapitalize = 'none'
autoCorrect={false}
returnKeyType='go'
placeholder = 'Password'
secureTextEntry
onChangeText={(text)=> this.setState({password:text})}
/>
<TouchableOpacity
style={styles.buttonContainer}
onPress={this.loginHTTPRequest}
>
<Text
style={styles.buttonText}
>
LOGIN
</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
padding: 10,
},
input: {
height:60,
backgroundColor: '#d1d1d1',
color: 'black',
marginBottom: 10,
padding: 10,
},
buttonContainer:{
backgroundColor:'#F9CF00',
paddingVertical: 15,
},
buttonText:{
color: 'black',
textAlign: 'center',
fontWeight: '700',
marginBottom:20,
}
});