Intro
User authentication is used in many web applications. Companies like Google, Meta, Amazon, and other companies all use user authentication to keep track of user information such as Usernames, ID's, passwords, and many other things. It's important to learn user authentication for company projects or personal ones.
How it works
User authentication is simply the process of identifying the user and accessing their information. Imagine a user entering a site like Facebook. They log in to their account then once they're in they can view all their personal information like posts, likes, and profile information. Then once they're done, they log out and cannot view their personal information until they log back in. While the user is browsing through Facebook user authentication happening in the background. At the start, the user is introduced to a login page where they first have to enter the correct information for their account. If the information is incorrect, Facebook cannot authenticate the user. Once the user enters the correct information Facebook logs them in their account, the application keeps the user's sensitive information until the user logs back out.
Getting Started with login
The first thing we need is a view to handle the login route in our backend. We can make a class called that handles POST requests for /login:
class Login(Resource):
def post(self):
username = request.get_json().get('username')
password = request.get_json().get('password')
user = User.query.filter_by(username=username).first()
if user and user.authenticate(password):
session['user_id'] = user.id
return user.to_dict(), 200
else:
return {'error': 'Unauthorized'}, 401
api.add_resource(Login, '/login')
Let's break down the code. class Login(Resource): This code tells us that we are making a class called "Login" that inherits "Resource". Whenever a class inherits Resource it lets us know that the class will be handling HTTP requests. Next we have:
def post(self):
username = request.get_json().get('username')
password = request.get_json().get('password')
This tells us that we will be using a post method and it gets JSON data that has username and password parameters. Lastly, we have the authentication part:
user = User.query.filter_by(username=username).first()
if user and user.authenticate(password):
session['user_id'] = user.id
return user.to_dict(), 200
else:
return {'error': 'Unauthorized'}, 401
We first look in our database and see if we can authenticate a user that has the username from the JSON data with this line of code:
user = User.query.filter_by(username=username).first()
Then if we find that the user is in our database and the user's password matches, we set the session to the user's id and return the user's information along with a status code 200. But if we cannot find the user we will return a JSON response with an error and a status code of 401. To finish with our backend, we add
api.add_resource(Login, '/login')
This code means that if the user tries to access the endpoint /login it will run the class.
Now onto our frontend:
function Login({ onLogin }) {
const [username, setUsername] = useState("");
function handleSubmit(e) {
e.preventDefault();
fetch("/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ username }),
})
.then((r) => r.json())
.then((user) => onLogin(user));
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<button type="submit">Login</button>
</form>
The Login component first creates a username state variable and a setUsername function to update the state. Next, we can take a look at the handleSubmit(e) function. First, we use e.preventDefault(); to prevent the default behavior of the event. Then we can fetch our /login endpoint:
fetch("/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ username }),
})
This code tells us that we will be making a POST request that expects the header Content-Type to be JSON data. Then the body: JSON.stringify({ username }) converts the username state variable into JSON string. .then((r) => r.json()) then parses the JSON data. Once the JSON is parsed, .then((user) => onLogin(user)); calls the onLogin function prop with a user argument.
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<button type="submit">Login</button>
</form>
);
Lastly, the Login function will return this form that takes in the username input and calls the handleSubmit function once the <button type="submit">Login</button> is pressed.
How to stay logged in
To stay logged in we can make this simple route:
class CheckSession(Resource):
def get(self):
user = User.query.filter(User.id == session.get('user_id')).first()
if user:
return user.to_dict()
else:
return {'message': '401: Not Authorized'}, 401
api.add_resource(CheckSession, '/check_session')
The CheckSession class is similar to the Login class we made in our backend except instead of defining a post method we will be defining a get method. We first look if the user is in our database then if we find the user we can either return a user dictionary or return a message that says "Not Authorized" with a status code of 401 if the user was not found in the database.
Finishing with logout
The last thing to do now is log out. When you've finished browsing through a website the last thing to do to keep others from accessing your information is to logout. We can log out by creating this route:
class Logout(Resource):
session['user_id'] = None
return jsonify({'message': '204: No Content'}), 204
api.add_resource(Logout, '/logout')
As you can see it's very simple. This class sets the session's user_id to None and returns a message that says "No Content" and a status code of 204. This means that there will be no user in the current session. Now let's make the front end part:
function Logout({ onLogout }) {
function handleLogout() {
fetch("/logout", {
method: "DELETE",
}).then(() => onLogout());
}
return (
<header>
<button onClick={handleLogout}>Logout</button>
</header>
);
}
This code is essentially the opposite of logging in. We first send a fetch request to the /logout endpoint with the method of DELETE. After the request is made we use .then and call the onLogout function prop. Lastly, we need to make a button that will call the handleLogout function and once this button is pressed the user will officially be logged out.
Conclusion
To be a software developer, it is crucial to learn how User authentication works. It is used in many applications to protect users such as yourself from breaches of private and sensitive data as well as keep your information secure. By understanding how user authentication works, you have added one more skill to your arsenal as a software developer.
Resources
Flatiron School Resources:
Top comments (0)