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