So I am working on an angular2 application and am trying to generate a JWT after logging in so that a user's profile information can be obtained. I can successfully login and that's when I generate the token. After logging in I route the user to the profile page and that's where I call my api to get the user information. All of this only works after I login and refresh to page.
this.auth_service
.Login(this.email, this.password)
.subscribe(
data => {
this.global_events.change_nav_bar.emit(true)
console.log('logged in successfully: ' + data)
this.router.navigate(['/profile'])
})
// auth_service above calls this method
Login(email, password): Observable<boolean>
{
return this.http
.post('/api/login', JSON.stringify({email: email, password: password}), this.Get_Headers('no_auth'))
.map(Handle_Response)
function Handle_Response(response: Response)
{
let token = response.json() && response.json().token
if(token)
{
this.token = token
localStorage.setItem('current_user', JSON.stringify({ email: email, token: token }))
return true
}
else
return false
}
}
// Then in my profile component I do this. I have experimented with different timeout times.
ngOnInit(): void
{
this.global_events.change_nav_bar.emit(true)
setTimeout(() => this.Get_User_Data(), 10000)
}
I solved this (not really solved but found another solution) by just pulling the token directly from localStorage instead of setting in the authenticationService
Related
I am trying to build a full stack web application using React JS and Spring. I have created an API for Login.
#PostMapping("/users/login")
public Status loginUser(#Valid #RequestBody PortalUser user) {
List<PortalUser> users = userRepository.findAll();
for (PortalUser other : users) {
if (other.equals(user)) {
return Status.SUCCESS;
}
}
return Status.FAILURE;
}
This API checks if user has entered correct login credentials or not. If yes, then it returns Enum as "SUCCESS" and if not then it returns Enum as "FAILURE".
The web API works fine on Postman. I now want to call the same from my frontend but I am unable to do so. Could anyone help me out with the same?
const user_base_url = "http://localhost:8080/users";
class CustomerService{
authenticateUser(user) {
return axios.post(user_base_url+ '/login', user);
}
}
I have created this function to call the API using axios.
validateUser = () => {
let user = {
username: this.state.email,
password: this.state.password
};
authenticateUser(user);
I am unable to proceed after this.
I basically want to authenticate the User when the login button (the Validate User function is called) is pressed.
Any help would be highly appreciated.
You can do something like this
const user_base_url = "http://localhost:8080/users";
class CustomerService{
authenticateUser(user) {
return axios.post(user_base_url+ '/login', user)
.then((res)=>{
if(res.data === 'SUCCESS') {
//user logged in
} else if(res.data === 'FAILURE') {
//login failed
}})
.catch(err) {
console.error(err)
}
}
}
Hi I am using express for backend authentication and these are my sign in functions/controllers on the front end.
export const signInUser = async credentials => {
console.log('this is for the signInUser', credentials)
try {
const resp = await api.post('/sign-in', credentials)
localStorage.setItem('token', resp.data.token)
return resp.data
} catch (error) {
throw error
}
}
onSignIn = event => {
event.preventDefault()
const { history, setUser } = this.props
signInUser(this.state)
.then(res => setUser(res.user))
.then(() => history.push('/Home'))
.catch(error => {
console.error(error)
this.setState({
loginUsername: '',
loginPassword: '',
})
})
}
setUser = user => this.setState({ user })
and this is my sign in controller on the backend
const signIn = async (req, res) => {
try {
console.log('hello' ,req.body);
const { loginUsername, username, loginPassword } = req.body;
const user = await User.findOne({
where: {
username: loginUsername
}
});
console.log('this is the user', user)
if (await bcrypt.compare(loginPassword, user.dataValues.password_digest)) {
const payload = {
id: user.id,
username: user.username,
password: user.password
};
const token = jwt.sign(payload, TOKEN_KEY);
return res.status(201).json({ user, token });
} else {
res.status(401).send("Username or Password is invalid- try again.");
}
} catch (error) {
return res.status(500).json({ error: error.message });
}
};
The issue is the state of the user doesn't persist on refresh but I still have the json webtoken in my local storage and this is an issue when I make post requests and even signing up since I am redirecting to the home page and losing the user state. Any help would be appreciated!
From your tags, I noticed that you are using React, so the solution is simple!
you can have an GlobalAuthManager context for your application that would wrap all the components at the most higher level! after <React.strictMode> like below:
<React.StrictMode>
<GlobalAuthManager.Provider value={{authData}}>
<App />
</GlobalAuthManager.Provider>
</React.StrictMode>
As you might guess, this would be a context! that would provide you your user data to all your components!
The Pattern:
1. Store token:
when your user logins to your app, you would receive a token ( in your response or in response header ), you need to store the token value in localstorage, or more better in cookie storage (there are a lot of articles about it why), one is here.
2. have a /getUserData endpoint in backend:
you need to have a /getUserData endpoint in backend to retrive your user data based on token
3. call /getUserData in app mount:
before every thing in your app, you need to call this endpoint if you find token in localstorage or cookie storage. so if you run this in your componnetDidMount or useEffect(() => { ... }, []), that would work!
4. store your user data and state in context:
after you've called the /getUserData and if you had a valid token(i mean not expired token or not interrupted and edited token) , you will get you user data and what you need to do is that you need to store this in your GlobalAuthManager and provide that in to your Global App component!
after that you have your user data available to you that you can decide to show login or sign up button in your Navbar or disable/enable comment section for example based on your user data!
Wrap up:
So the key is that you have to have a GlobalAuthManager for only one purpose, that before every thing it runs in the top level in your app and gets you your user data based on provided token from localstorage or cookie storage!
after that you can manage your app state based on that your user is logged in or not!
I have a login page. I want my app to redirect the user to the homepage if the login is successful. Then credentials are checked with an API. My problem is, my vue page redirect the users before the credentials are successfully checked.
Seeing similar topic on the vue.js help forum, I understand I am supposed to send the login request, and then wait for the response promise to resolve. I feel like this is what i am doing, but it clearly does not wait for the response to be resolved before redirecting.
Here is my code in my vue page (the script part) . When I click the "signin" button, the onSigninClick() method is called :
import { mapActions, mapGetters } from 'vuex'
export default {
name: 'SignInLayout',
data () {
return {
username: null,
password: null
}
},
computed: {
...mapGetters('TemplaterAuth', [
'logged',
'getUsername',
'getJwtToken'
])
},
methods: {
...mapActions('TemplaterAuth', [
'authenticate'
]),
onSigninClick () {
let creds = {
username: this.username,
password: this.password
}
this.authenticate(creds).then(response => {
console.log(this.getUsername)
console.log(this.getJwtToken)
console.log('logged:')
console.log(this.logged)
this.$router.push('/')
})
}
}
}
and my authenticate() method :
export function authenticate (context, creds) {
let requestConfig = {
headers: {
'Content-Type': 'application/json'
}
}
Vue.http.post(
url + apiPaths.auth,
creds,
requestConfig
).then(response => {
return response.json()
}).then(data => {
context.commit('setUsername', creds.username)
context.commit('setJwtToken', data.token)
}).catch(error => {
console.log('error:')
console.log(error)
})
}
When i click once the login button, my console log shows null for both the username and the jwtToken . Few moments later, the values are updated in the store and then I am able to login.
So, I got my answer on the Vue forum just a few seconds after posting this : I need to return the promise of my authenticate method. So the new code is :
export function authenticate (context, creds) {
let requestConfig = {
headers: {
'Content-Type': 'application/json'
}
}
return Vue.http.post(
url + apiPaths.auth,
creds,
requestConfig
).then(response => {
return response.json()
}).then(data => {
context.commit('setUsername', creds.username)
context.commit('setJwtToken', data.token)
}).catch(error => {
console.log('error:')
console.log(error)
})
}
Source
This looks a bit fishy for me.
First: I hope, you are not using vuex to hold your crentials/token.
Security tip #1, Security tip #2.
I would recommend only setting authenticated:true and use that for your requests.
Then:
Have a look at global guards of the vue router, which could be leveraged, to check, whether the current user is logged in.
Last:
To programatically route to sections of your app, you could use router.push.
So, you have to store, which route the user wanted before the login page and programmatically push this route afterwards.
I'm using the Slim Framework to develop the backend. But I can not find a way to compare the token generated by my login function:
public function login($request, $response){
$key = $this->container['key'];
$email = $request->getParsedBody()['email'];
$senha = $this->salt . $request->getParsedBody()['senha'];
$usuario = $this->em->getRepository(UsuarioEntity::class)->findOneBy(['email' => $email]);
if(empty($usuario) || !password_verify($senha, $usuario->getSenha())) {
return $response->withJson('Usuario sem permissão de acesso', 401);
}
$token = array(
"session" => password_hash($usuario->getId() . 'f*u87', PASSWORD_BCRYPT),
"id" => $usuario->getId(),
"iat" => time(),
"exp" => time() + (60 * 10)
);
$jwt = \Firebase\JWT\JWT::encode($token, $key);
return $response->withJson($jwt, 200);
}
On the front-end (React) I call a JS class that handles all requests. I get and store the token value, but I do not know how to use it to check if user is logged in or not
Requisition.js
axiosPost(funcao,dados){
//A AUTENTICAÇÃO VAI AQUI
return axios.post(config.urlBase + funcao, dados);
}
setToken(token){
this.token = token;
}
getToken(){
return this.token;
}
LoginEmpresa.js(React Component)
login(){
var reqAxios = new Requisicoes();
reqAxios.axiosPost('login',{ email: this.state.email, senha: this.state.senha }).then(res => {
if(res.data){
reqAxios.setToken(res.data);
}else{
[...]
}
})
}
Thanks
You can check if the JWT is valid by doing a request to the backend API.
public function getUser($request, $response){
$user = // GET CURRENT LOGGED IN USER BASED ON THE JWT
if(!$user) {
return $response->withJson('user is not logged in', 401);
}
return $response->withJson($user, 200);
}
On the React part, you can do a request to the API to get the currently logged in user.
If you receive a 200 response with a user -> logged in
If you receive a 401 response -> not logged in
You can use a response interceptor from Axios to check the status code:
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
if (error.status === 401) {
// DELETE YOUR TOKEN
this.removeToken();
}
return Promise.reject(error);
});
Also, I recommend you to store the token in localStorage so that the session of a user won't expire on a page refresh.
setToken(token){
localStorage.setItem('jwt_token', token);
}
getToken(){
return localStorage.getItem('jwt_token');
}
removeToken(){
localStorage.removeItem('jwt_token');
}
As your front end is a React app, on the login response, you should store the token on your app's state. You may have it on the main component of your app or in a redux store, or anywhere else.
It is also good to think about storing the JWT on the localStorage, to ensure the user keeps logged in between multiple tabs on your application.
And if you are using the JWT protocol, you should be configuring your axios instance to send the Authorization HTTP header with the token inside. I don't see it on the piece of code you've provided
I am working on a login function in my Angular 2 app, and after unsuccessfully trying a few authentication methods with our API, I have landed on one that works. Basically, the way it works is like this: if the inputed "username" and "password" are found in our mongoDB collection, the API will send a 200 status. If not, it doesn't. So that's what I'm checking: if I have a username and password inputed that returns a 200 status, then go ahead and login the user. Does this seem like a workable way to handle this? Is checking against a response.status a legitimate way to handle this?
login(username: string, password: string) {
const u = encodeURIComponent(username);
const p = encodeURIComponent(password);
this._url = `https://api.somesite.com/v0/staff/login/${u}/${p}?apikey=somekey`;
console.log(this._url);
return this.http.post(this._url, JSON.stringify({ username: username, password: password }))
.map((response: Response) => {
const user = response.json();
if (user && (response.status = 200)) {
localStorage.setItem('currentUser', JSON.stringify(user));
} else {
console.log('User ' + this.username + ' not recognized by API...');
}
});
}