Home > database >  User authentication using Rails and React
User authentication using Rails and React

Time:05-23

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:

React front-end

CodePudding user response:

My react is quite rusty, but you might find these helpful:

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
  • Related