I think I am doing everything correctly. But the program is showing "await is a reserved keyword" in signin function. Here is the code:
import axios from "axios";
const signin = async (username, password) => {
try {
console.log("Sign in function called");
const res = await axios.post(`${process.env.baseURL}/auth/signin`, {
username,
password
});
console.log(res.data);
} catch (error) {
console.log(error);
}
};
const logout = () => {
// remove user from local storage to log user out
localStorage.removeItem("jobmanager_user");
};
export const userService = {
signin,
logout
};
Related
Im trying to refresh the jwt token with cognitoUser.refreshSession, everytime I log in it simply refreshes the login page and asks me to sign in again
I have written up some code from various examples I have found but still struggle to get it going. If anyone has any suggestions I would welcome them:
Code as follows:
import axios from 'axios';
import { Auth } from 'aws-amplify';
const httpClient = axios.create(
{ baseURL: process.env.VUE_APP_API_BASE_URL }
);
const refreshToken = async function() {
try {
const cognitoUser = await Auth.currentAuthenticatedUser();
const currentSession = await Auth.currentSession();
cognitoUser.refreshSession(currentSession.refreshToken, (err, session) => {
console.log('session', err, session);
return Auth.user.signInUserSession.idToken.jwtToken
});
} catch (e) {
console.log('Unable to refresh Token', e);
}
}
httpClient.interceptors.request.use(
config => {
const idToken = refreshToken()
config.headers.Authorization = idToken;
return config
}, error => Promise.reject(error))
export default httpClient;
I followed this tutorial to setup a firebase auth store in vuex (https://www.youtube.com/watch?v=n9cERWIRgMw&list=PL4cUxeGkcC9jveNu1TI0P62Dn9Me1j9tG&index=10)
However, I'm using Nuxt which causes the last step to break.
My file:
import { auth } from "~/plugins/firebase.js";
import {
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut,
onAuthStateChanged,
} from "firebase/auth";
export const state = () => ({
user: null,
authIsReady: false,
});
export const mutations = {
setUser(state, payload) {
state.user = payload;
console.log("user state changed:", state.user);
},
setAuthIsReady(state, payload) {
state.authIsReady = payload;
},
};
export const actions = {
async signup(context, { email, password }) {
console.log("signup action");
const res = await createUserWithEmailAndPassword(auth, email, password);
if (res) {
context.commit("setUser", res.user);
} else {
throw new Error("could not complete signup");
}
},
async login(context, { email, password }) {
console.log("login action");
const res = await signInWithEmailAndPassword(auth, email, password);
if (res) {
context.commit("setUser", res.user);
} else {
throw new Error("could not complete login");
}
},
async logout(context) {
console.log("logout action");
await signOut(auth);
context.commit("setUser", null);
},
};
const unsub = onAuthStateChanged(auth, (user) => {
store.commit("setAuthIsReady", true);
store.commit("setUser", user);
unsub();
});
Error:
Uncaught (in promise) ReferenceError: store is not defined
at eval (index.js?9101:56:1)
at eval (index-6de4cbb9.js?3d11:2453:1)
How do I commit a mutation from here? I tried loads of things, like this.$store, $store etc.
I'm trying to implement my signUp screen here is the signup handle that is being called, the signup page calls, and pass in the value to my context page.
const handleSignup = async () => {
setLoading(true)
const user = { fullname, email, password, profilePhoto};
try {
const createdUser = await firebase.createUser(user)
setUser({ ...createdUser, isLoggedIn: true });
} catch (error) {
console.log("Error #signUp: ", error);
} finally {
setLoading(false)
}
};
the firebaseContext page then takes in the value that has been passed in creates the account
const FirebaseContext = createContext();
if(!firebase.apps.length){
firebase.initializeApp(auth);
}
const db = firebase.firestore();
const Firebase = {
getCurrentUser: () => {
return firebase.auth().currentUser
},
createUser: async (user) => {
try{
await firebase.auth().createUserWithEmailAndPassword(user.email, user.password);
const uid = Firebase.getCurrentUser().uid;
let profilePhotoUrl = "default";
await db.collection("users").doc(uid).set({
fullname: user.fullname,
email: user.email,
profilePhotoUrl
})
if(user.profilePhoto){
profilePhotoUrl = await Firebase.uploadProfilePhoto(user.profilePhoto);
}
delete user.password;
return { ...user, profilePhotoUrl, uid};
}catch(error){
console.log("Error #createUser", error.message);
}
},
Exporting the file
import { FirebaseContext, FirebaseProvider, Firebase } from './FirebaseContext';
import { UserContext, UserProvider } from './UserContext';
export { FirebaseContext, Firebase, FirebaseProvider, UserContext, UserProvider };
Importing the file
import { FirebaseContext } from "../context";
how to test this Api and get 100% score of testing coverage?
const login = async (email, password) => {
axios
.post('https://conduit.productionready.io/api/users/login', {
user: {
email,
password,
},
})
.then((response) => response);
};
Your function is relatively simple : one path, no branching logic, one external call.
All your function do is calling an endpoint through axios.post.
login.js
export const login = async (email, password) => {
/*
* Notice that I added the 'await', else 'async' is useless.
* Else you can directly return the axios.post method.
*/
await axios
.post('https://conduit.productionready.io/api/users/login', {
user: {
email,
password,
},
})
.then((response) => response); // This line is also useless for the moment
};
login.spec.js
import { login } from './login';
// Mock axios, else you will really request the endpoint
jest.mock('axios');
import axios from 'axios';
describe('Login tests', () => {
describe('login function', () => {
const email = 'test#test.com';
const password = 'password';
beforeEach(() => {
/*
* Not necessary for the moment, but will be useful
* to test successful & error response
*/
axios.post.mockResolvedValue({});
});
it('should call endpoint with given email & password', async () => {
await login(email, password);
expect(axios.post).toBeCalledWith(
'https://conduit.productionready.io/api/users/login',
{ user: { email, password } },
);
});
});
});
Notice that you could greatly improve your login function by returning something and handling error with an Authentication Error. Your tests would be more significant :
errors.js
export class DefaultError extends Error {
static STATUS_CODE = 500; // You can change it, it depends how you use it
name = 'DefaultError';
constructor() {
super('Default error, add what you want');
}
}
export class AuthenticationError extends Error {
static STATUS_CODE = 401;
name = 'AuthenticationError';
constructor() {
super('Wrong credentials');
}
}
login.js
import { AuthenticationError, DefaultError } from './errors';
export const login = async (email, password) =>
axios
.post('https://conduit.productionready.io/api/users/login', {
user: {
email,
password,
},
})
.then(response => response.data)
.catch(error => {
// Handles the error how you want it
if (error.status === AuthenticationError.STATUS_CODE) {
throw new AuthenticationError();
}
throw new DefaultError();
});
login.spec.js
import { login } from './login';
import { AuthenticationError, DefaultError } from './errors';
// Mock axios, else you will really request the endpoint
jest.mock('axios');
import axios from 'axios';
describe('Login tests', () => {
describe('login function', () => {
const email = 'test#test.com';
const password = 'password';
describe('with success', () => {
const data = { something: {} };
beforeEach(() => {
axios.post.mockResolvedValue({ data });
});
it('should call endpoint with given email & password', async () => {
await login(email, password);
expect(axios.post).toBeCalledWith(
'https://conduit.productionready.io/api/users/login',
{ user: { email, password } },
);
});
it('should return response data', async () => {
const response = await login(email, password);
expect(response).toStrictEqual(data);
});
});
describe('with error', () => {
describe('status 401', () => {
beforeEach(() => {
axios.post.mockRejectedValue({ status: 401 });
});
it('should throw AuthenticationError', async () => {
await expect(login(email, password)).rejects.toThrow(AuthenticationError);
});
});
describe('other status', () => {
beforeEach(() => {
axios.post.mockRejectedValue({});
});
it('should throw DefaultError', async () => {
await expect(login(email, password)).rejects.toThrow(DefaultError);
});
});
});
});
});
We could go further but I think you got the point. Btw, you don't need to split the tests as I did, I just enjoy being able to group the describe by the mocks needed and making little & readable tests.
I'm trying to build Google Authentication in React + Redux + Firebase. And I want to create user after google authentication. But this.props.createUser() is not working after Redirects on Firebase.
TypeError: Cannot read property 'props' of undefined
app/src/components/home.js
import React, { Component } from "react";
import { connect } from "react-redux";
import firebase from "../config/firebase";
import { createUser } from "../actions";
class Home extends Component {
constructor(props) {
super(props);
this.state = {
user: null
};
}
async componentWillMount() {
console.log("this", this.props);
firebase.auth().onAuthStateChanged(user => {
this.setState({ user });
if (user) {
this.props.history.push("/user/settings");
}
});
firebase
.auth()
.getRedirectResult()
.then(function(result) {
if (result.credential) {
var token = result.credential.accessToken;
console.log("token", token);
}
var user = result.user;
// Successfully got a user but type error in below
this.props.createUser(user);
})
.catch(function(error) {
console.log("error", error);
});
}
onLogin = () => {
const provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithRedirect(provider);
};
render() {
return (
<button className="btn btnPrimary" onClick={this.onLogin}>
<span>Google Signin</span>
</button>
);
}
}
function mapStateToProps({ user }) {
return { user: user };
}
export default connect(mapStateToProps, { createUser })(Home);
app/src/actions/index.js
import firebase from "../config/firebase";
export function createUser(user) {
console.log('user', user)
}
Since you declare the callback with the function keyword, this inside the function refers to the function itself and not to the class on which you declared the componentWillMount method.
The simplest solution is to use fat arrow notation, as you do elsewhere already:
firebase
.auth()
.getRedirectResult()
.then((result) => {
if (result.credential) {
var token = result.credential.accessToken;
console.log("token", token);
}
var user = result.user;
// Successfully got a user but type error in below
this.props.createUser(user);
})
Also see this answer on the cause of the problem, and other solutions: How to access the correct `this` inside a callback?
You need to use an arrow function here:
firebase
.auth()
.getRedirectResult()
.then(function(result) {
if (result.credential) {
var token = result.credential.accessToken;
console.log("token", token);
}
var user = result.user;
// Successfully got a user but type error in below
this.props.createUser(user);
})
.catch(function(error) {
console.log("error", error);
});
should be:
firebase
.auth()
.getRedirectResult()
.then((result) => {
if (result.credential) {
var token = result.credential.accessToken;
console.log("token", token);
}
var user = result.user;
// Successfully got a user but type error in below
this.props.createUser(user);
})
.catch(function(error) {
console.log("error", error);
});
The function() {} keyword is eating your this value.