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)