I have implemented Amplify's Auth authentication in my react native app, and set up userpool identity pool to use PubSub.
The problem i'm facing is in the identity pool I've setup Identity providers as the user pool i'm signing into, and in the identity pool there is a role for Authenticated users and Unauthenticated users, after logging into the user pool I'll have to use the pubsub feature, but when I signin, I'll get [NotAuthorizedException] identity [identityId] is forbidden, the identity id is the id that's visible in my identity pool, also in the error console, I assume that the identity is set by the identity pool after the user signs in successfully, also if I sign in, the user will be assigned as an Authenticated user in the identity pool, but to test, I removed the identity provider from the identity pool, that means now if I sign in to the application using the same user pool, the identity pool does not recognise the user based on the user pool, rather it'll just assign the user/ identity as unauthenticated user, now to test I set the same role for both authenticated and unauthenticated users, with the identity providers set to the user pool I'll get the error, but with the unautheticated user/guest user with the same role which has access to the AWS IoT Core (by removing the identity provider in the identity pool), I'm able to access the aws iot core, which means I can now publish and subscribe, based on research, and Amazon Q's suggestions, I think I am missing in the Amplify's documentation is to pass the appropriate tokens to the identity pool from the user pool after signing in, share the references/ documentations if possible, also, is this the right approach of what I'm thinking?
Let me know. Thanks in advance!.
//index.ts
import { registerRootComponent } from "expo";
import App from "./App";
import { AWSIoTProvider } from "@aws-amplify/pubsub";
import { Amplify, Auth } from "aws-amplify";
Amplify.configure({
Auth: {
identityPoolId: "us-west-2:identity-pool-id",
region: "us-west-2",
userPoolId: "us-west-2_user-pool-id,
userPoolWebClientId: "user-pool-app-client-id",
mandatorySignIn: false,
authenticationFlowType: "USER_PASSWORD_AUTH",
signUpVerificationMethod: "code",
},
});
Amplify.addPluggable(
new AWSIoTProvider({
aws_pubsub_region: "us-west-2",
aws_pubsub_endpoint:
"wss://domain-ats.iot.us-west-2.amazonaws.com/mqtt",
})
);
export const currentConfig = Auth.configure();
import { Auth, PubSub } from "aws-amplify";
import { useEffect, useState } from "react";
import {
Button,
ScrollView,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View,
} from "react-native";
import { currentConfig } from ".";
function SignUp({ onSignUpSuccess }: any) {
const [form, setForm] = useState({
username: "",
password: "",
email: "",
phoneNumber: "",
confirmationCode: "",
});
const [isConfirming, setIsConfirming] = useState(false);
function handleChange(field: string, value: string) {
setForm((prev) => ({ ...prev, [field]: value }));
}
async function handleSignUp() {
try {
await Auth.signUp({
username: form.username,
password: form.password,
attributes: { email: form.email, phone_number: form.phoneNumber },
});
setIsConfirming(true);
} catch (error) {
console.log("Error signing up:", error);
}
}
async function handleConfirmSignUp() {
try {
await Auth.confirmSignUp(form.username, form.confirmationCode);
onSignUpSuccess();
} catch (error) {
console.log("Error confirming sign up:", error);
}
}
return (
<View style={styles.container}>
<Text style={styles.header}>
{isConfirming ? "Confirm Sign Up" : "Sign Up"}
</Text>
{!isConfirming ? (
<>
<CustomInput
placeholder="Username"
onChangeText={(value: string) => handleChange("username", value)}
/>
<CustomInput
placeholder="Email"
onChangeText={(value: string) => handleChange("email", value)}
/>
<CustomInput
placeholder="Password"
secureTextEntry
onChangeText={(value: string) => handleChange("password", value)}
/>
<CustomInput
placeholder="Phone Number"
onChangeText={(value: string) => handleChange("phoneNumber", value)}
/>
<CustomButton title="Sign Up" onPress={handleSignUp} />
</>
) : (
<>
<CustomInput
placeholder="Confirmation Code"
onChangeText={(value: string) =>
handleChange("confirmationCode", value)
}
/>
<CustomButton title="Confirm" onPress={handleConfirmSignUp} />
</>
)}
</View>
);
}
function SignIn({ onSignInSuccess }: any) {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
async function handleSignIn() {
try {
const user = await Auth.signIn(username, password);
console.log({ currentConfig });
const currentSession = await Auth.currentUserCredentials();
console.log(currentSession, "current user creds");
onSignInSuccess(user);
} catch (error) {
console.log("Error signing in:", error);
}
}
return (
<View style={styles.container}>
<Text style={styles.header}>Sign In</Text>
<CustomInput
placeholder="Username"
value={username}
onChangeText={setUsername}
/>
<CustomInput
placeholder="Password"
secureTextEntry
value={password}
onChangeText={setPassword}
/>
<CustomButton title="Sign In" onPress={handleSignIn} />
</View>
);
}
function Home({ onSignOut }: any) {
const [subscription, setSubscription] = useState<any>(null);
const [publishTopic, setPublishTopic] = useState("welds");
const [subscribeTopic, setSubscribeTopic] = useState("welds");
const [message, setMessage] = useState("");
const [subscribedMessages, setSubscribedMessages] = useState<any[]>([]);
useEffect(() => {
setupIoT();
return () => {
if (subscription) {
subscription.unsubscribe();
}
};
}, []);
const setupIoT = async () => {
try {
const newSubscription = PubSub.subscribe(subscribeTopic).subscribe({
next: (data) => {
console.log("Message received:", data);
setSubscribedMessages((prevMessages) => [
...prevMessages,
data.value,
]);
},
error: (error) => console.error("Subscription error:", error),
complete: () => console.log("Subscription completed"),
});
setSubscription(newSubscription);
} catch (error) {
console.log("IoT setup error:", error);
}
};
const handlePublishTopicChange = (newTopic: string) => {
setPublishTopic(newTopic);
if (subscription) {
subscription.unsubscribe();
}
setupIoT();
};
const handleSubscribeTopicChange = (newTopic: string) => {
setSubscribeTopic(newTopic);
if (subscription) {
subscription.unsubscribe();
}
setupIoT();
};
const publishMessage = async () => {
try {
await PubSub.publish(publishTopic, { msg: message });
console.log("Message published successfully");
} catch (err) {
console.log("Publish error:", err);
}
};
const clearMessages = () => {
setSubscribedMessages(() => []);
};
return (
<View style={styles.container}>
<View style={styles.headerPortion}>
<Text style={styles.homeHeader}>Welcome to Home Page!</Text>
<View style={styles.signOutButton}>
<Button title="Sign Out" onPress={onSignOut} />
</View>
</View>
<View style={styles.labelInput}>
<Text>Subscribe from : </Text>
<TextInput
style={[styles.input, styles.customInput]}
placeholder="Enter topic to subscribe"
value={subscribeTopic}
onChangeText={handleSubscribeTopicChange}
/>
</View>
<View style={styles.labelInput}>
<Text>Publish to : </Text>
<TextInput
style={[styles.input, styles.customInput]}
placeholder="Enter topic to publish"
value={publishTopic}
onChangeText={handlePublishTopicChange}
/>
</View>
<TextInput
style={[styles.input]}
placeholder="Type your message"
value={message}
onChangeText={setMessage}
/>
<CustomButton title="Publish Message" onPress={publishMessage} />
<View style={styles.clearHeader}>
<Text style={styles.subscribedHeader}>Subscribed Messages:</Text>
<TouchableOpacity style={styles.clearButton} onPress={clearMessages}>
<Text style={styles.clearButtonText}>Clear</Text>
</TouchableOpacity>
</View>
<ScrollView style={styles.messageContainer}>
{subscribedMessages.length > 0 ? (
subscribedMessages.map((msg, index) => (
<Text key={index} style={styles.message}>
{JSON.stringify(msg)}
</Text>
))
) : (
<Text style={styles.noMessages}>No messages received yet.</Text>
)}
</ScrollView>
</View>
);
}
export default function App() {
const [user, setUser] = useState(null);
const [isSignUpMode, setIsSignUpMode] = useState(false);
async function handleSignOut() {
try {
const credentials = await Auth.currentCredentials();
await Auth.signOut({ global: true });
setUser(null);
} catch (error) {
console.log("Error during sign out:", error);
}
}
return (
<View style={{ flex: 1 }}>
{user ? (
<Home onSignOut={handleSignOut} />
) : (
<View style={styles.container}>
<TouchableOpacity onPress={() => setIsSignUpMode(!isSignUpMode)}>
<Text style={styles.switchText}>
{isSignUpMode ? "Switch to Sign In" : "Switch to Sign Up"}
</Text>
</TouchableOpacity>
{isSignUpMode ? (
<SignUp
onSignUpSuccess={async () =>
setUser(await Auth.currentAuthenticatedUser())
}
/>
) : (
<SignIn onSignInSuccess={setUser} />
)}
</View>
)}
</View>
);
}
function CustomInput({ ...props }) {
return <TextInput style={styles.input} {...props} />;
}
function CustomButton({ title, onPress }: any) {
return (
<TouchableOpacity style={styles.button} onPress={onPress}>
<Text style={styles.buttonText}>{title}</Text>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
customInput: { flex: 1 },
labelInput: { display: "flex", flexDirection: "row" },
clearHeader: {
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
},
clearButton: {
backgroundColor: "dodgerblue",
width: 100,
display: "flex",
flexDirection: "row",
justifyContent: "center",
alignContent: "center",
alignItems: "center",
borderRadius: 20,
},
clearButtonText: {
color: "white",
},
headerPortion: {
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
marginBottom: 20,
},
buttonText: { color: "#fff", fontWeight: "600", fontSize: 16 },
button: {
width: "100%",
padding: 15,
backgroundColor: "#007bff",
borderRadius: 8,
alignItems: "center",
marginVertical: 10,
},
header: { fontSize: 24, fontWeight: "bold", marginBottom: 20, color: "#333" },
switchText: {
color: "#007bff",
marginBottom: 15,
textDecorationLine: "underline",
},
container: {
flex: 1,
padding: 16,
backgroundColor: "#f5f5f5",
},
input: {
height: 40,
borderColor: "#ccc",
borderWidth: 1,
borderRadius: 8,
paddingHorizontal: 10,
marginBottom: 10,
backgroundColor: "#fff",
},
signOutButton: {
display: "flex",
justifyContent: "flex-end",
width: 120,
backgroundColor: "blue",
marginBottom: 10,
},
homeHeader: {
fontWeight: "bold",
fontSize: 24,
marginBottom: 10,
},
subscribedHeader: {
marginTop: 20,
fontWeight: "bold",
fontSize: 16,
},
messageContainer: {
maxHeight: 900,
marginTop: 10,
padding: 10,
backgroundColor: "#e9e9e9",
borderRadius: 8,
},
message: {
paddingVertical: 5,
borderBottomWidth: 1,
borderBottomColor: "#ccc",
},
noMessages: {
color: "#666",
fontStyle: "italic",
},
});