AWS Amplify + React Authentication Issue (Not getting JWT value after Auth.signIn() on Sign) - reactjs

hope you all are well. I am working on a React project with a serverless backend in AWS Amplify. Facing an issue with the authentication which is blocking me to use admin action queries. The problem is the following:
I am getting "Uncaught (in promise) No current user" or "Uncaught (in promise) The user is not authenticated" after using the Auth.SignIn() method. I need to get the JWT value from Auth.currentAuthenticatedUser() and sign in correctly in order to perform administrative tasks (which require authentication).
The code for this is:
try {
const user = await Auth.signIn(username, password);
console.log(user); //prints the CognitoUser object.
console.log(Auth.currentAuthenticatedUser()); //throws any of the two errors mentioned above.
} catch (error) {
console.log(error);
}
When I implemented this previously, I was able to get the JWT token with the following method after authentication. That function is:
export const getJWT = async () => {
try{
const userCurrentSession = await Auth.currentSession();
const accessToken = userCurrentSession.getAccessToken();
const JWTvalue = accessToken.getJwtToken();
return JWTvalue;
}catch(error){console.log(error)}
};
I would be highly obliged if someone helps in resolving this problem. Thanks in advance.

Replace the lines inside of try block with the following:
const user = await Auth.currentAuthenticatedUser();
const JWTvalue = user.signInUserSession.getAccessToken().getJwtToken();
return JWTvalue;

Related

How to store secret keys in react native?

I have a third party api secret key and according to react native documentation I should not be storing it using .env
What is a better way of doing it to protect it from reverse engineering and other attacks?
Please share an example /tutorial with your answer as this is a new topic for me.
Finally, I found the answer to these kinds of questions. Use the STS AssumeRole or AssumeRoleWithWebIdentity (requires a JavaScript Web Token). The former does not have a way to check AuthN, so it is not secure on the frontend. But the latter has a slot for JWT token, which you can verify against an OpenID Connect Provider for AuthN.
https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-sts/index.html
In the latter case, you setup an IAM role and an OIDC Provider to verify the token (aka. your user is who they say they are). This depends on your organization.
Cognito basically does the same thing, except it helps you by handling all this logic on the backend as a service.
react-native-keychain is the best option to store credentials in React-Native mobile apps.
Save credentials
import * as Keychain from 'react-native-keychain';
const Login = props => {
const username = 'username';
const password = 'password';
await Keychain.setGenericPassword(username, password);
}
Get Credentials
import * as Keychain from 'react-native-keychain';
const Screen = props => {
const getCredentials = async () => {
try {
const credentials = await Keychain.getGenericPassword();
}
} catch (error) {
console.log(error);
}
}
}
Remove credentials
import * as Keychain from 'react-native-keychain';
const Logout = props => {
const removeCredentials = async () => {
try {
const credentials = await Keychain.resetGenericPassword();
}
} catch (error) {
console.log(error);
}
}
}
To store sensitive data in effective way, we can use keystore file for Android, Keychain service for iOS in React Native.
For more detail, please refer below link:
https://reactnative.dev/docs/security

MSAL: InteractionRequiredAuthError: no_tokens_found: No refresh token found in the cache. Please sign-in

Here's the bit of code that I was working on. I am using MSAL for two SSO apps on same domain for example https://some-domain.com/app1 and https://some-domain.com/app2 and please see the code snippet below.
App 1 seems to be fine it allows user to sign in correctly.However, on app2 when I reload the page it throws an error
MSAL: InteractionRequiredAuthError: no_tokens_found: No refresh token
found in the cache. Please sign-in.
I have used instance.acquireTokenRedirect,acquireTokenSilent and identityInstance.loginRedirect() but nothing seemed to work. Any ideas please share. Thanks.
const [userName, setUsername] = useState<string | undefined>()
useEffect(() => {
const fetchDetaiils = async () => {
if (inProgress === InteractionStatus.None) {
try {
const signedInUser = identityInstance.getAllAccounts()[0]
const resp = await identityInstance.acquireTokenSilent({
scopes: ['user.read'],
account,
})
const token: Token = resp?.idTokenClaims
setUsername(token.email)
} catch (err: unknown) {
if (err instanceof Error) {
console.log(err)
if (err?.name === 'InteractionRequiredAuthError') {
// await instance.acquireTokenRedirect(loginRequest)
}
}
}
}
}
fetchDetaiils()
As described in these Microsoft Docs, SSO between apps requires the use of either the login_hint or sid (session ID) parameters in the silent request.
The values of login_hint and sid can be extracted from the ID Token that is obtained in App 1. For more information, please consult the MSAL Browser Login Docs

NextJS / React - how to avoid multiple requests to server?

I'm writing a little side project in which I have a question about how to design my frontend authorization flow.
Stack is: Next.js / Express / MDB
Here's the issue with the authorization: In my app there are pages that should be only served to instructors.
When the app mounts I have a call to my backend checking for a stored cookie and authorize the user. The returned user is stored in a Context.
authContext.js
useEffect(() => {
checkUserLoggedIn();
}, []);
...
const checkUserLoggedIn = async () => {
const res = await fetch(`${process.env.NEXT_PUBLIC_CLIENT}/user`);
const data = await res.json();
if (res.status === 200) {
setUser(data.user);
} else {
setUser(null);
}
};
That works perfectly fine.
But now comes the part where I struggle.
The protected pages are wrapped by a component that checks again if the user is a) authenticated and b) authorized.
wrapInstructor.js
const CheckInstructor = ({ token, children }) => {
const [ok, setOk] = useState(false);
const [login, setLogin] = useState(null);
useEffect(async () => {
try {
const user = await fetch(
`${process.env.NEXT_PUBLIC_APIURL}/me`,
{
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
}
);
const { data, error } = await user.json();
if (error) {
toast.error("Auf diese Seite können nur eingeloggte User zugreifen");
setLogin(false);
}
if (data && data.role.includes("INSTRUCTOR")) {
setOk(true);
setLogin(true);
}
} catch (error) {
toast.error(error);
setLogin(false);
}
}, []);
return !ok ? (
<>{login === false ? <NotLoggedIn /> : <Spinner />}</>
) : (
<>{children}</>
);
};
export default CheckInstructor;
Now here comes the problem:
When a user mounts the app on a path that is protected the app fires off two authentication requests simultaniously.
The first one gets a valid response (the request from context). The second one from the wrapper gets a 304 status code which indicates that the response has not changed since the last request.
But since the wrapper expects a valid response the content of the protected pages will not show.
How can I cope with this problem? (NOTE: I'm in development mode, tested from localhost)
My ideas were:
The wrapper component does not make another call to the server - the user in the context is the single source of truth and the wrapper only checks the already stored user --> Question: Is this a safe practice? Can the store (React Context or Redux) be manipulated by the user which would make my app unsafe?
In the wrapper component also show the children components if the status code is 304 AND a user is already stored in the context
Write another endpoint in the backend so both requests are sent to different routes (I would concider this bad practice because DRY)
I'm a bit lost here - can you help me to clear my thoughts?
Thanks a lot!
There's no need to visit the same endpoint twice to check if a user is authorized to visit a certain page. What you store in auth context would be enough. Just make sure to update the value whenever a role or permissions change.
Regarding your security concern, you shouldn't consider any client app safe. Even if you add an actual endpoint call on every protected page, there's still a way to call the endpoints directly(curl, postman, you name it).
The problem should be solved by introducing authorization checks on every protected API route. This way you would never worry about corrupted clients at all.

Login with Facebook in react native return "The App_id in the input_token did not match the Viewing App"

I come to you after hours of research.
I have created my facebook account with the application I am working on and also my firebase account that I have linked through the OAuth redirection URI to the configuration of your Facebook application.
But I always get the same mistake. Do you have any leads? Knowing that my APP_ID is the same in the code, on facebook developers and Firebase. And that I redirected the URI of firebase in facebook developpers.
Here is my code :
async function loginWithFacebook(){
await Facebook.initializeAsync({
appId : '1027709424451081'});
const {type,token} =
await Facebook.logInWithReadPermissionsAsync({
permissions:['public_profile'],
});
if (type === 'success') {
const credential = firebase.auth.FacebookAuthProvider.credential(token);
Firebase.auth().signInWithCredential(credential)
.then(user => { // All the details about user are in here returned from firebase
console.log('Logged in successfully', user)
})
.catch((error) => {
console.log('Error occurred ', error)
});
}
}
Thanks in advance for all suggestions.
When using the function "signInWithCredentials" you need to pass the auth also which you will get by
const auth = getAuth()
and then after getting the credentials
signInWithCredential(auth, credential)

An error occurred when I get a data from firestore in React app

I'm developing a task management app with Firebase and React.
Current Code
export const login = (type, email, password) => dispatch => {
dispatch({type: "LOGIN"});
switch (type) {
case "EMAIL": {
// Logging in using Firebase Auth. It goes successfully.
firebase.auth().signInWithEmailAndPassword(email, password).then(result => {
const db = firebase.firestore();
const uid = result.user.uid
// Getting user name from Firestore. The fllowing error occurs here.
let name;
db.collection("users").get().then(querySnapshot => {
querySnapshot.forEach(doc => {
name = doc.name;
dispatch({type: "LOGIN_SUCCEEDED", uid: uid, name: name,});
dispatch(push("/"))
});
}).catch(err => {
dispatch({type: "LOGIN_REJECTED", error: err});
});
}).catch(err => {
dispatch({type: "LOGIN_REJECTED", error: err.message});
});
break;
}
default: {}
}
Only the code of the relevant part here.
Other codes are in Github.
Auth(Firebase)
Firestore
The error occured
[2020-06-07T03:54:57.645Z] #firebase/firestore: Firestore (7.15.0): Could not reach Cloud Firestore backend. Connection failed 1 times. Most recent error: FirebaseError: [code=unknown]: Fetching auth token failed: getToken aborted due to token change.
This typically indicates that your device does not have a healthy Internet connection at the moment. The client will operate in offline mode until it is able to successfully connect to the backend. index.js:1
I tried this to solve the error:
Check the network => It is healthy. I can see any pages on the internet and even connect to firebase auth in same program.
Use older virsion Firebase => nothing changed.
This is caused because the user is already logged, do something like this and will work:
if (!firebase.auth().currentUser) {
await firebase.auth().signInWithEmailAndPassword(email, password);
}
Obs: To use await, you will need a async function
Hope it help :)

Resources