I've been trying to figure out how to fix my authentication using Ruby on Rails and React. I have been able to login but it automatically logs out whenever I refresh the page. What am I missing here?
My SesssionsController: Checks credentials and also logout feature included
class SessionsController < ApplicationController
def create
user = User.find_by(username: params[:username])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
render json: user, status: :created
else
render json: {error: {login: "invalid username or password"}}, status: :unauthorized
end
end
def destroy
session.delete :user_id
head :no_content
end
end
Login Frontend (React): This is where users enters their credentials and send it to my sessions controller to check if the credential is valid.
import React, { useState } from 'react';
import {Link, useNavigate} from 'react-router-dom';
import './style/login.css';
function Login({setUser}) {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const navigate = useNavigate();
function handleSubmit(e) {
e.preventDefault();
fetch("http://localhost:3000/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ username:username, password:password }),
})
.then((r) => r.json())
.then((user) => setUser(user));
navigate("/")
}
return (
<div className='login_wrapper'>
<h1>Login</h1>
<form className='login_form' onSubmit={handleSubmit}>
<input
type="text"
value={username}
placeholder="Username"
onChange={(e) => setUsername(e.target.value)}
/><br/>
<input
type="password"
value={password}
placeholder="Password"
onChange={(e) => setPassword(e.target.value)}
/><br/>
<button className="login_btn" type="submit">Login</button>
</form>
<br/>
<div><Link to='/signup'>First time? Register Now!</Link></div>
</div>
);
}
export default Login
App.js: storing user data in useState
import './App.css';
import React, { useEffect, useState } from 'react';
import { Route, Routes} from "react-router-dom";
import Home from './components/Home';
import Login from './components/Login';
import Signup from './components/Signup';
import Navbar from './components/Navbar';
function App() {
const [user, setUser] = useState(null);
useEffect(() => {
fetch("http://localhost:3000/auth")
.then((r) => {
if (r.ok) {
r.json()
.then((user) => setUser(user));
}
});
}, []);
return(
<>
<Navbar user={user} setUser={setUser} />
<Routes>
<Route exact path="/" element={<Home user={user}/>}></Route>
<Route exact path="/login" element={<Login setUser={setUser} />}></Route>
<Route exact path="/signup" element={<Signup setUser={setUser}/>}></Route>
</Routes>
</>
)
}
export default App
When I log in, it doesn't save any sessions to the browser - why is this? Here's the image:
CodePudding user response:
My react is quite rusty, but you might find these helpful:
- https://www.justinweiss.com/articles/how-rails-sessions-work/
- https://medium.com/@austinrhoads/session-store-with-react-rails-api-116a9efe3c30
- https://medium.com/@altanner/react-user-authentication-with-rails-sessions-and-redux-194b5d31fe5a
Googling some variation of rails app with react not setting session cookie
will probably also get you where you need to go.
CodePudding user response:
I personally prefer using Devise for authentication, since it is easy to set up and also easily customizable.
If you want, you can pass current_user
(Devise's authentication variable, which is the currently logged in user) as a prop to your React components. Alternatively, you can pass instance variables to each component, and possibly pass them conditionally depending on authentication status.
For example, if you want to show posts and comments to authenticated users, but non-authenticated users should only see the first comment on a post, you could do the following on your controller:
@posts = Post.all
if current_user
@comments = Comment.all
else
@comments = Comment.first
end