Tutorial 3: Authentication, Session, and Cookies
Platform-Based Programming (CSGE602022) — Organized by the Faculty of Computer Science Universitas Indonesia, Odd Semester 2023/2024
Learning Objectives
After completing this tutorial, students are expected to be able to:
- Understand the basic concept of authentication in web development.
- Understand the roles and functions of cookies and sessions in web development.
- Understand how cookies and sessions work in web development.
- Implement cookies and sessions in a web project.
Introduction to HTTP
HTTP (HyperText Transfer Protocol) is a protocol in communication between a client and a server. HTTP is stateless, which means that each transaction/activity is treated as an entirely new transaction/activity. Therefore, no previous data is stored for the current transaction/activity.
Here are some basic concepts about HTTP:
Client/Server: Interaction is carried out between client or server. The client is the party who makes the request and the server is the party who provides the response.
Stateless: Each activity (request or response) is independent, not stored in previous activities.
OSI Layer/Model: The Open Systems Interconnection (OSI) model describes the 7 layers that computer systems use to communicate over networks. The 7-layer OSI model consists of Application Layer, Presentation Layer, Session Layer, Transport Layer, Network Layer, Data Link Layer, and Physical Layer.
Application Layer: In the OSI Model mentioned above, websites operate at the application layer. Meanwhile, the request/response process occurs at the transport layer which generally uses the TCP protocol which determines how data is transmitted. The application layer is not concerned with what the transport layer does (how data is sent, processed, etc.) because the application layer only focuses on request and response.
Other OSI layers will be taught in the Computer Networks course. You can look for them yourself if you are curious. 😉
Client Actions Method: There are methods used by the client when making a request, for example: GET, POST, PUT, DELETE, etc. More detailed explanation can be found here.
Server Status Code: This is the status code provided by the server for a request on a web page. Example: 200 (OK), 404 (Page Not Found), 500 (Internal Server Error), etc. A more detailed explanation can be found here.
Headers: These are small pieces of information sent along with the request and response. This information is useful as additional data used to process request/response. For example, in the headers, there is
content-type:json, indicating that the requested/sent content type isjson. Headers also storecookiesdata.
Introduction to Cookies & Session
All communication between the client and server is done through the HTTP protocol, a stateless protocol. This means that one state is not related to another (independent). This requires the computer on client side to establish a TCP connection to the server every time it makes a request.
Without a persistent connection between the client and server, the software on each side (endpoint) cannot rely solely on the TCP connection to hold a state or hold session state.
What does holding a state mean?
Suppose that you want to access a page A on a website that requires users to be logged in. Then, you log in to the website and successfully access page A. When you want to move to page B on the same website, without a process of holding a state, you will be prompted to log in again. This will happen every time you access a different page on the same website.
The process of informing "who" is currently logged in and storing that information is the basis of a session (a semi-permanent exchange of information). It is difficult to make HTTP hold a state (because HTTP is a stateless protocol). Therefore, other techniques are needed to address this issue, namely cookies and sessions.
How to hold a state?
One of the widely used methods for holding a state is storing a session ID as a cookie in the client's browser. Session ID can be considered as a token (line of characters) that identifies a unique session of a website. Instead of storing the client's information as a cookie (e.g. username, name, password, etc.), only the session ID is stored.
Beside as a cookie, the session ID is also stored in a data structure on the website's server side. The data structure also stores the corresponding client's information. This approach of storing client's information is much safer instead of directly storing the information as a cookie. Furthermore, this approach is considered as the "correct" way to store a bunch of client's information. This is due to the size limit of a cookie, which is 4KB of data.
But how is session ID used to hold a state exactly? Imagine that you already logged in to the website and your session ID is stored in your browser as a cookie. To hold a state in the stateless HTTP, the browser usually will attach stored cookies to the HTTP request. Therefore, every time the server processes an incoming request, it can read the session ID in the request's cookie. After some processing, the server will find the information related to the session ID in the memory or database, and then returning the requested data.
The main difference that needs to be remembered is cookie's data is stored on the client side, while session's data is stored on the server side. For more detailed explanation about stateless, stateful, cookie, and session can be read here.
Below is a table briefly explaining the differences between cookies, sessions, and local storage.
| Cookies | Local Storage | Sessions | |
|---|---|---|---|
| Capacity | 4 KB | 5 MB | 5 MB |
| Browser Technology | HTML4/HTML5 | HTML5 | HTML5 |
| Accessibility | All window | All window | Same tab |
| Expiry | Manually configured | No expiration | When tab is closed |
Below are some videos that can enrich your knowledge about this material:
Tutorial: Creating Registration Form and Function
Note: In this tutorial, you will use your project created in previous tutorial.
In the previous tutorial, we have created a form to add products. Easy, right? In this tutorial, we will restrict the access to the main page. Therefore, only logged in users can access the page. Other user who wants to access the main page have to log in first to gain access.
Start the virtual environment.
Open
views.pyinside themainfolder and create a function calledregisterthat acceptsrequestas a parameter.Add imports for
redirect,UserCreationForm, danmessagesat the beginning of the file.from django.shortcuts import redirect
from django.contrib.auth.forms import UserCreationForm
from django.contrib import messagesCode Explanation:
UserCreationFormis a built-in form that eases us to create a user registration form. With this form, we can create a registration form without needing to code it from scratch.Add this code inside the
registerfunction. This piece of code will create a registration form automatically. If a registration data is submitted, a user account will be created.def register(request):
form = UserCreationForm()
if request.method == "POST":
form = UserCreationForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, 'Your account has been successfully created!')
return redirect('main:login')
context = {'form':form}
return render(request, 'register.html', context)Code Explanation:
form = UserCreationForm(request.POST)is used to create a newUserCreationFormbased on the data inrequest.POST.form.is_valid()is used to validate the form data.form.save()is used to create a new data (in this case, a new user) from the form.messages.success(request, 'Your account has been successfully created!')is used to show a message to the user after an action.return redirect('main:show_main')is used to redirect the page after the form is successfully saved.
Create a new HTML file named
register.htmlinside themain/templatesfolder. Fill the file with:{% extends 'base.html' %}
{% block meta %}
<title>Register</title>
{% endblock meta %}
{% block content %}
<div class = "login">
<h1>Register</h1>
<form method="POST" >
{% csrf_token %}
<table>
{{ form.as_table }}
<tr>
<td></td>
<td><input type="submit" name="submit" value="Daftar"/></td>
</tr>
</table>
</form>
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endblock content %}Open
urls.pyinside themainfolder and import the function you created previously.from main.views import registerAdd a new path url to
urlpatternsto access the imported function....
path('register/', register, name='register'),
...
We have created an account registration form and a register mechanism. Next, we will create a login form.
Tutorial: Creating a Login Function
Open
views.pyinside themainfolder and create a function calledlogin_userthat accepts arequestas a parameter.Add imports for
authenticateandloginat the beginning of the file.from django.contrib.auth import authenticate, loginCode Explanation:
The imported functions
authenticateandloginare used to authenticate and log in the user if the authentication is success. Read more here.Add this code inside the
loginfunction. This piece of code is used to authenticate a user.def login_user(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
return redirect('main:show_main')
else:
messages.info(request, 'Sorry, incorrect username or password. Please try again.')
context = {}
return render(request, 'login.html', context)Code Explanation:
authenticate(request, username=username, password=passwordis used to authenticate a user using the provided username and password. The username and password is sent through the request.Create a new HTML file named
login.htmlinside themain/templatesfolder. Fill the file with:{% extends 'base.html' %}
{% block meta %}
<title>Login</title>
{% endblock meta %}
{% block content %}
<div class = "login">
<h1>Login</h1>
<form method="POST" action="">
{% csrf_token %}
<table>
<tr>
<td>Username: </td>
<td><input type="text" name="username" placeholder="Username" class="form-control"></td>
</tr>
<tr>
<td>Password: </td>
<td><input type="password" name="password" placeholder="Password" class="form-control"></td>
</tr>
<tr>
<td></td>
<td><input class="btn login_btn" type="submit" value="Login"></td>
</tr>
</table>
</form>
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
Don't have an account yet? <a href="{% url 'main:register' %}">Register Now</a>
</div>
{% endblock content %}Open
urls.pyinside themainfolder and import the previously created function.from main.views import login_userAdd a new path url to
urlpatternsto access the imported function....
path('login/', login_user, name='login'),
...
We have created a user login form and login mechanism. Next, we will create a logout mechanism and add a logout button on the main page.
Tutorial: Creating a Logout Function
Open
views.pyinside themainfolder and create a function calledlogout_userthat accepts arequestas a parameter.Add import
logoutat the beginning of the file.from django.contrib.auth import logoutAdd this code inside the
logoutfunction. This piece of code is used to log out a user.def logout_user(request):
logout(request)
return redirect('main:login')Code Explanation:
logout(request)is used to delete the currently logged in user's session.return redirect('main:login')is used to redirect the page to the login page.
Open the
main.htmlinside themain/templates.Add this code after the Add New Product hyperlink tag.
...
<a href="{% url 'main:logout' %}">
<button>
Logout
</button>
</a>
...Open
urls.pyinside themainfolder and import the previously created function.from main.views import logout_userAdd a new path url to
urlpatternsto access the previously imported function....
path('logout/', logout_user, name='logout'),
...
Congrats! You have successfully created a logout mechanism and completed the authentication system in this project!
Tutorial: Restricting Access to The Main Page
Open the
views.pyfile in themainsubdirectory and add thelogin_requiredimport at the top.from django.contrib.auth.decorators import login_requiredCode Explanation:
The code
from django.contrib.auth.decorators import login_requiredis used to require users to log in before accessing the web page.Add the
@login_required(login_url='/login')code above theshow_mainfunction to restrict access to the main page only to authenticated users....
@login_required(login_url='/login')
def show_main(request):
...
After restricting access to the main page, run your Django project using the python manage.py runserver command and open http://localhost:8000/ in your favorite browser to see the results. You should be redirected to the login page instead of the main product list page if you are not logged in.
Tutorial: Using Data from Cookies
Now, we will explore the use of cookies by adding a "last login" feature and displaying it on the main page.
Log out first if you are currently logged into your Django application.
Open the
views.pyfile in themainsubdirectory and add imports forHttpResponseRedirect,reverse, anddatetimeat the very top.import datetime
from django.http import HttpResponseRedirect
from django.urls import reverseIn the
login_userfunction, modify the code inside theif user is not Noneblock as follows:...
if user is not None:
login(request, user)
response = HttpResponseRedirect(reverse("main:show_main"))
response.set_cookie('last_login', str(datetime.datetime.now()))
return response
...Code Explanation:
login(request, user)is used to log in the user.response = HttpResponseRedirect(reverse("main:show_main"))creates a response object.response.set_cookie('last_login', str(datetime.datetime.now()))creates a 'last_login' cookie and adds it to the response.
In the
show_mainfunction, add the'last_login': request.COOKIES['last_login']code to the context variable as shown below:context = {
'name': 'Pak Bepe', # Your name
'class': 'PBP A', # Your PBP class
'products': products,
'last_login': request.COOKIES['last_login'],
}Code Explanation:
'last_login': request.COOKIES['last_login']is used to add the 'last_login' cookie data to the response, which will be displayed on the web page.Modify the
logout_userfunction as follows:def logout_user(request):
logout(request)
response = HttpResponseRedirect(reverse('main:login'))
response.delete_cookie('last_login')
return responseCode Explanation
response.delete_cookie('last_login')is used to delete thelast_logincookie when the user logs out.Open the
main.htmlfile and add the following code between the table and logout button to display the 'last login' data:...
<h5>Last login session: {{ last_login }}</h5>
...Refresh the login page (or run your Django project with the
python manage.py runservercommand if you haven't already) and try logging in. Your 'last login' data should appear on the main page.To view the
last_logincookie data, you can use the browser's developer tools and go to the "Application" or "Storage" section. Click on "Cookies", and you will see the available cookies. In addition tolast_loginyou can also findsessionidandcsrftokencookies. Here's an example of what it looks like:
If you log out and check the cookie history, the previously created cookie will be deleted and recreated when you log in again.
Before proceeding to the next tutorial, try to create one account on your website.
Tutorial: Connecting the Product Model to User Model
You need to follow the tutorials sequentially from the beginning before proceeding with the following sections. If you don't follow the tutorials in order, we cannot guarantee the avoidance of errors beyond the scope of this tutorial.
Finally, we will link each Product object to the user who created it, so that authorized users can only see the products they have created. For that, follow these steps:
Open the
models.pyfile in themainsubdirectory and add the following code below the code for importing models:...
from django.contrib.auth.models import User
...In the existing
Productmodel, add the following code:class Product(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
...Code Explanation:
This code is used to establish a relationship where each product is associated with one user. In other words, each product belongs to a specific user. For a more in-depth explanation of
ForeignKeyin Django, you can refer to this documentation.In the
create_productfunction, modify the code as follows:def create_product(request):
form = ProductForm(request.POST or None)
if form.is_valid() and request.method == "POST":
product = form.save(commit=False)
product.user = request.user
product.save()
return HttpResponseRedirect(reverse('main:show_main'))
...Code Explanation:
Using
commit=Falseprevents Django from immediately saving the object created from the form to the database. This allows us to modify the object before saving it. In this case, we set theuserfield to theUserobject associated with the currently logged-in user, indicating that the product belongs to that user.Modify the
show_mainfunction as follows:def show_main(request):
products = Product.objects.filter(user=request.user)
context = {
'name': request.user.username,
...
...Code Explanation:
- This code is used to display
Productobjects associated with the currently logged-in user. It filters the objects to only retrieve those where theuserfield matches the logged-in user. - The
request.user.usernamecode is used to display the username of the logged-in user on the main page.
- This code is used to display
Save all changes and run the migrations for the model using
python manage.py makemigrations.You may encounter an error during the migration process. Choose option
1to set a default value for theuserfield in all existing rows in the database.
Type
1again to set theuserto ID 1 (which we created earlier) for the existing models.
Run
python manage.py migrateto apply the migrations made in the previous steps.
Run your Django project using the python manage.py runserver command and open http://localhost:8000/ in your browser to see the results. Try creating a new account and logging in. You will notice that products created with a previous account are not displayed on the main page when logged in with a new account. This means you have successfully connected the Product objects to the User who created them.
Closing
Congratulations! You have successfully completed Tutorial 3. 😄
After completing all the tutorials above, you should now have a better understanding of using forms, authentication, sessions, and cookies in the Django framework.
After completing this tutorial, your web page should look like this.
At the end of this tutorial, your
templatesdirectory inmainsubdirectory should look like this:
Before performing this step, ensure that your local directory structure is correct. Next, perform
add,commit, andpushto update your GitHub repository.Run the following commands to
add,commit, andpush:git add .
git commit -m "<commit_message>"
git push -u origin <your_main_branch>- Replace
<commit_message>with your desired message. For example:git commit -m "Completed tutorial 2". - Replace
<your_main_branch>with your main branch name. For example:git push -u origin mainorgit push -u origin master.
- Replace
Contributors
- Rayhan Putra Randi
- Anindya Lokeswara
- Kade Satrya Noto Sadharma
- Alfredo Austin
- Alya Azhar Agharid
- Aidah Novallia Putri (EN Translator)
- Bonaventura Galang (EN Translator)
- Ferry (EN Translator)
Credits
This tutorial was developed based on PBP Odd 2023 and PBP Even 2023 written by the 2023 Platform-Based Programming Teaching Team. All tutorials and instructions included in this repository are designed so that students who are taking Platform-Based Programming courses can complete the tutorials during lab sessions.