AWS amplify currentAuthenticatedUser shows unauthenticated status even after doing federatedSignIn - javascript

export const requestFederatedSignOn = async (type) => {
return new Promise((resolve, reject) => {
Auth.federatedSignIn({ provider: type })
.then(() => {
console.log("you have signed in");
authenticatedUserCred()
.then((userData) => {
console.log("you have been authenticated");
resolve(userData);
})
.catch((err) => console.error(err));
})
.catch((error) => reject(error));
});
};
export const authenticatedUserCred = async () => {
return new Promise((resolve, reject) => {
Auth.currentAuthenticatedUser()
.then((user) => {
const userAttributes = user.signInUserSession.idToken.payload;
const tokens = {
accessToken: user.signInUserSession.accessToken.jwtToken,
idToken: user.signInUserSession.idToken.jwtToken,
refreshToken: user.signInUserSession.refreshToken.token,
};
const userData = {
tokens: tokens,
userProfile: userAttributes,
};
resolve(userData);
})
.catch((err) => reject(err));
});
};
Basically, I am able to sign in using federatedSignIn and console log "you have signed in" is outputted but then authenticatedUserCred function for some reason is rejecting the promise. Please someone help me out!!!

Related

Issues reauthenticaing a user with credentials inside of react native app

I have a react native application. I want to be able to reauthenticate a user before submitting the updated email. I am running into the issue of reautneticaing the user.
tried
const saveProfile = () => {
let credential = EmailAuthProvider.credential(user.email, 'hellohello')
user.reauthenticateWithCredential(credential)
.then(() => {
updateEmail(auth.currentUser, newEmail.toString())
.then(() => {
console.log('updated email')
setUser(auth.currentUser)
})
.catch((error) => {
console.log(error)
})
})
.catch((error) => {
console.log(error)
})
}
and
const saveProfile = () => {
let credential = EmailAuthProvider.credential(user.email, 'hellohello')
reauthenticateWithCredential(credential)
.then(() => {
updateEmail(auth.currentUser, newEmail.toString())
.then(() => {
console.log('updated email')
setUser(auth.currentUser)
})
.catch((error) => {
console.log(error)
})
})
.catch((error) => {
console.log(error)
})
}
When i try to update the email with firebase, it says I have to Firebase: Error (auth/requires-recent-login).
error:
undefined is not an object (evaluating 'credential._getReauthenticationResolver')
at node_modules/#firebase/auth/dist/rn/phone-eec7f987.js:5048:20 in _processCredentialSavingMfaContextIfNecessary
at node_modules/#firebase/auth/dist/rn/phone-eec7f987.js:5212:113 in tslib.__generator$argument_1
at node_modules/#firebase/auth/node_modules/tslib/tslib.js:144:21 in step
at node_modules/#firebase/auth/node_modules/tslib/tslib.js:125:60 in
at node_modules/#firebase/auth/node_modules/tslib/tslib.js:118:17 in
at node_modules/#firebase/auth/node_modules/tslib/tslib.js:114:15 in __awaiter
at node_modules/#firebase/auth/dist/rn/phone-eec7f987.js:5333:49 in tslib.__generator$argument_1
at node_modules/#firebase/auth/node_modules/tslib/tslib.js:144:21 in step
at node_modules/#firebase/auth/node_modules/tslib/tslib.js:125:60 in
at node_modules/#firebase/auth/node_modules/tslib/tslib.js:118:17 in
at node_modules/#firebase/auth/node_modules/tslib/tslib.js:114:15 in __awaiter
at src/screens/SettingsScreen.js:158:4 in saveProfile
at src/screens/SettingsScreen.js:186:81 in TouchableOpacity.props.onPress
at node_modules/react-native/Libraries/Pressability/Pressability.js:702:17 in _performTransitionSideEffects
at node_modules/react-native/Libraries/Pressability/Pressability.js:639:6 in _receiveSignal
at node_modules/react-native/Libraries/Pressability/Pressability.js:520:8 in responderEventHandlers.onResponderRelease
The reauthenticateWithCredential() function takes currentUser as first parameter. Try refactoring the code as shown below:
const saveProfile = async () => {
const credential = EmailAuthProvider.credential(user.email, 'hellohello')
// auth.currentUser as first param
await reauthenticateWithCredential(auth.currentUser, credential);
await updateEmail(auth.currentUser, newEmail.toString())
console.log('updated email')
setUser(auth.currentUser)
}

Firebase cloud function to update a record and send a notification

Evening All
Having a few problems performing two actions when a document is created
the below worked until i added the last "then" in the createDocument function where i attempt to send a notification to inform the use via fcm.
exports.createRequest = functions.firestore
.document('requests/{requestId}')
.onCreate((snap, context) => {
var email = snap.data().requestedFromEmail;
checkUserInFirebase(email)
.then((user) => {
//get user profile
return getUserProfile(user.user.uid);
})
.then(userProfile => {
return snap.ref.set({ requestedFromName: userProfile.data().fullName, requestedFromId: userProfile.id }, {merge:true});
})
.then(value=> {
return sendNotification(snap.data().requestedFromId, snap.data().requestedByName);
})
.catch(error => {return error;})
}
)
can anyone see where im going wrong, all the examples im finding send the fcm explicitly from using a exports. Ideally id like to pass the userProfile object through to the send notification function but um not sure how to do that and still set the changes to the document. Full code is below
async function checkUserInFirebase(email) {
return new Promise((resolve) => {
admin.auth().getUserByEmail(email)
.then((user) => {
return resolve({ isError: false, doesExist: true, user });
})
.catch((err) => {
return resolve({ isError: true, err });
});
});
}
async function getUserProfile(uid) {
return admin.firestore()
.collection("users")
.doc(uid)
.get();
}
async function sendNotification(uid, requestedByName) {
const querySnapshot = await db
.collection('users')
.doc(uid)
.collection('tokens')
.get();
const tokens = querySnapshot.docs.map(snap => snap.token);
console.info(tokens);
const payload = {
notification: {
title: 'New Request!',
body: `You received a new request from ${requestedByName}`,
icon: 'your-icon-url',
click_action: 'FLUTTER_NOTIFICATION_CLICK'
}
};
return fcm.sendToDevice(tokens, payload);
}
exports.createRequest = functions.firestore
.document('requests/{requestId}')
.onCreate((snap, context) => {
var email = snap.data().requestedFromEmail;
checkUserInFirebase(email)
.then((user) => {
//get user profile
return getUserProfile(user.user.uid);
})
.then(userProfile => {
return snap.ref.set({ requestedFromName: userProfile.data().fullName, requestedFromId: userProfile.id }, {merge:true});
})
.then(value=> {
return sendNotification(snap.data().requestedFromId, snap.data().requestedByName);
})
.catch(error => {return error;})
}
)
Your funciton needs to return a promise that resolves when all the async work is complete. Right now it returns nothing, which means that Cloud Functions might terminate it up before the work is done. You should return the promise chain:
return checkUserInFirebase(email)
.then((user) => {
//get user profile
return getUserProfile(user.user.uid);
})
.then(userProfile => {
return snap.ref.set({ requestedFromName: userProfile.data().fullName, requestedFromId: userProfile.id }, {merge:true});
})
.then(value=> {
return sendNotification(snap.data().requestedFromId, snap.data().requestedByName);
})
.catch(error => {return error;})
}
Note the return at the start of the whole thing.
See the documentation for more information.

Testing blackboxed APIs in JS/Vue with jest

Well, I have an interesting case...
I'm trying to test a Vuex action that uses the Okta SDK to log in a User. Nothing special there. But at the testing level, I'm stuck trying to catch the idea. I mean, I just want to know if the functions have been called, not anything else, here the code:
The service:
const signIn = async ({ username, password }) => {
const response = await authClient.signIn({ username, password })
if (response.status === 'SUCCESS') {
const tokens = await authClient.token.getWithoutPrompt({
responseType: 'id_token',
sessionToken: response.sessionToken
})
authClient.tokenManager.add('idToken', tokens.tokens.idToken)
return response
}
}
The action:
async logIn({ commit }, { username, password }) {
const loginData = await signIn({ username, password })
commit(mutationTypes.setUserData, {
...loginData.user.profile
})
}
The test:
const authClient = {
signIn() {
return new Promise(resolve => resolve())
},
token: {
getWithoutPrompt() {
return new Promise(resolve => resolve())
}
},
tokenManager: {
add() {
return new Promise(resolve => resolve())
}
}
}
jest.mock('authClient', () => authClient)
it('Auth -> actions.signIn', async () => {
const commit = jest.fn()
const username = 'user'
const password = 'pass'
await actions.signIn({ commit }, { username, password })
expect(authClient.signIn).toHaveBeenCalled()
expect(authClient.token.getWithoutPrompt).toHaveBeenCalled()
expect(authClient.tokenManager.add).toHaveBeenCalled()
expect(commit).toHaveBeenCalled()
})

Redux Actions must be plain objects - error with store setup or action config?

Morning all... getting Actions must be plain objects. Use custom middleware for async actions. with the following action:
export const addHousehold = user => dispatch =>
new Promise(async (resolve, reject) => {
const hasHousehold = await getUsersHouseholdByUserId(user.uid);
if (hasHousehold) {
return reject(
new Error(
'You have already generated a household. Please complete the setup steps.',
),
);
}
return resolve(dispatch(createHousehold(user)));
});
The Promise should be implicitly returned from this function call, shouldn't it? The action is called on click from a component, the call is here:
addHousehold() {
this.props.addHousehold(this.props.user).then(
() => {
this.props
.addOnboardingStages(this.props.user.uid)
.then(res => {}, err => {});
},
err => this.setState({ errors: { generateHouseholdError: err.message } }),
);
}
This is my store setup, using Redux thunk as middleware.
const store = createStore(
rootReducer,
INITIAL_STATE,
applyMiddleware(thunk),
);
UPDATE:
The createHousehold function looks as follows:
const createHousehold = user => {
const { uid } = user;
const householdId = uniqid.time();
return Promise.all([
setHousehold(uid, householdId),
setUser(uid, householdId),
]);
};
const setHousehold = (uid, householdId) => dispatch =>
new Promise((resolve, reject) => {
console.log(uid)
db
.collection('households')
.doc(householdId)
.set(
{
users: [uid],
},
{ merge: true },
)
.then(() => {
resolve();
})
.catch(() => reject());
});
const setUser = (uid, householdId) => dispatch =>
new Promise((resolve, reject) => {
db
.collection('users')
.doc(uid)
.update({
household: householdId,
})
.then(
() => resolve(dispatch(receiveHousehold(householdId))),
err => console.error(err),
);
});
UPDATE 2
Looks like the issue was dispatch wasn't passed in to createHousehold:
const createHousehold = user => dispatch => {
const { uid } = user;
const householdId = uniqid.time();
return Promise.all([
setHousehold(uid, householdId),
setUser(uid, householdId)(dispatch) // here
]);
};
Thank you :)

How to fix TypeError is not a function (testing promises with Jest)

I have a passing test now thanks to the answer here: How to test is chained promises in a jest test?
However I'm still getting an error in the catch part of my test.
I seem to not be able to correctly mock or spy this part in the actions file: .then(res => res.getIdToken())
TEST signIn ERROR => TypeError: res.getIdToken is not a function
The Test
jest.mock('services/firebase', () => new Promise(resolve => resolve({
signInWithEmailAndPassword: () => Promise.resolve({ getIdToken: 'abc123' }),
getIdToken: () => jest.fn(),
signOut: () => jest.fn()
})));
describe('login actions', () => {
let store;
beforeEach(() => {
store = mockStore({});
});
it('signIn should call firebase', () => {
const user = {
email: 'first.last#yum.com',
password: 'abd123'
};
return store.dispatch(signIn(user.email, user.password))
.then(() => {
console.log('TEST signIn SUCCESS');
expect(mockSignIn).toHaveBeenCalled();
expect(store.getActions()).toEqual({
type: USER_ON_LOGGED_IN
});
})
.catch((err) => {
console.log('TEST signIn ERROR =>', err);
});
});
The SignIn actions/Login
// Sign in action
export const signIn = (email, password, redirectUrl = ROUTEPATH_DEFAULT_PAGE) => (dispatch) => {
dispatch({ type: USER_LOGIN_PENDING });
return firebase
.then((auth) => {
console.log('auth =>', auth);
return auth.signInWithEmailAndPassword(email, password);
})
.catch((e) => {
console.error('actions/Login/signIn', e);
// Register a new user
if (e.code === LOGIN_USER_NOT_FOUND) {
dispatch(push(ROUTEPATH_FORBIDDEN));
dispatch(toggleNotification(true, e.message, 'error'));
} else {
dispatch(displayError(true, e.message));
setTimeout(() => {
dispatch(displayError(false, ''));
}, 5000);
throw e;
}
})
// I can't seem to mock this correctly
.then(res => res.getIdToken())
.then((idToken) => {
if (!idToken) {
dispatch(displayError(true, 'Sorry, there was an issue with getting your token.'));
}
dispatch(onCheckAuth(email));
dispatch(push(redirectUrl));
});
};
It looks like the reason why you're getting this error has to do with the data you're mocking through Jest.
Try using jest.fn() to mock your getIdToken as a function, rather than a string:
const mockGetIdToken = jest.fn(() => 'abc123');
jest.mock('services/firebase', () => new Promise(resolve => resolve({
signInWithEmailAndPassword: () => Promise.resolve({ getIdToken: mockGetIdToken }),
getIdToken: mockGetIdToken,
signOut: () => jest.fn()
})));
describe('login actions', () => {
let store;
beforeEach(() => {
store = mockStore({});
});
it('signIn should call firebase', () => {
const user = {
email: 'first.last#yum.com',
password: 'abd123'
};
return store.dispatch(signIn(user.email, user.password))
.then(() => {
console.log('TEST signIn SUCCESS');
expect(mockSignIn).toHaveBeenCalled();
expect(store.getActions()).toEqual({
type: USER_ON_LOGGED_IN
});
})
.catch((err) => {
console.log('TEST signIn ERROR =>', err);
});
});

Categories

Resources