This project involves learning NodeJS basics concept covered;
- Using NodeJS modules
- Reading files
- Creating HTTP servers
- Using Express JS
- Testing with Mocha.
- Using Nodemon.
In the file 0-console.js
, create a function named displayMessage
that prints in STDOUT
the string argument.
bob@dylan:~$ cat 0-main.js
const displayMessage = require('./0-console');
displayMessage("Hello NodeJS!");
bob@dylan:~$ node 0-main.js
Hello NodeJS!
bob@dylan:~$
Repo:
- GitHub repository:
alx-backend-javascript
- Directory:
0x05-Node_JS_basic
- File:
0-console.js
# Create solution file.
touch 0-console.js
chmod +x 0-console.js
# Lint.
npm run lint 0-console.js --fix
# Test.
touch tests/0-main.js
chmod +x tests/0-main.js
node tests/0-main.js
Create a program named 1-stdin.js
that will be executed through command line:
- It should display the message
Welcome to Holberton School, what is your name?
(followed by a new line) - The user should be able to input their name on a new line
- The program should display
Your name is: INPUT
- When the user ends the program, it should display
This important software is now closing
(followed by a new line)
Requirements:
- Your code will be tested through a child process, make sure you have everything you need for that
bob@dylan:~$ node 1-stdin.js
Welcome to Holberton School, what is your name?
Bob
Your name is: Bob
bob@dylan:~$
bob@dylan:~$ echo "John" | node 1-stdin.js
Welcome to Holberton School, what is your name?
Your name is: John
This important software is now closing
bob@dylan:~$
Repo:
- GitHub repository:
alx-backend-javascript
- Directory:
0x05-Node_JS_basic
- File:
1-stdin.js
# Create solution file.
touch 1-stdin.js
chmod +x 1-stdin.js
# Lint.
npm run lint 1-stdin.js --fix
# Test.
node 1-stdin.js
echo "John" | node 1-stdin.js
Using the database database.csv
(provided in project description), create a function countStudents
in the file 2-read_file.js
- Create a function named
countStudents
. It should accept a path in argument - The script should attempt to read the database file synchronously
- If the database is not available, it should throw an error with the text
Cannot load the database
- If the database is available, it should log the following message to the console
Number of students: NUMBER_OF_STUDENTS
- It should log the number of students in each field, and the list with the following format:
Number of students in FIELD: 6. List: LIST_OF_FIRSTNAMES
- CSV file can contain empty lines (at the end) - and they are not a valid student!
bob@dylan:~$ cat 2-main_0.js
const countStudents = require('./2-read_file');
countStudents("nope.csv");
bob@dylan:~$ node 2-main_0.js
2-read_file.js:9
throw new Error('Cannot load the database');
^
Error: Cannot load the database
...
bob@dylan:~$
bob@dylan:~$ cat 2-main_1.js
const countStudents = require('./2-read_file');
countStudents("database.csv");
bob@dylan:~$ node 2-main_1.js
Number of students: 10
Number of students in CS: 6. List: Johann, Arielle, Jonathan, Emmanuel, Guillaume, Katie
Number of students in SWE: 4. List: Guillaume, Joseph, Paul, Tommy
bob@dylan:~$
Repo:
- GitHub repository:
alx-backend-javascript
- Directory:
0x05-Node_JS_basic
- File:
2-read_file.js
# Create solution file.
touch 0-console.js
chmod +x 0-console.js
# Lint.
npm run lint 0-console.js --fix
# Test.
touch tests/2-main_0.js tests/2-main_1.js
chmod +x tests/2-main_0.js tests/2-main_1.js
node tests/2-main_0.js
node tests/2-main_1.js
Score: 0.0% (Checks completed: 0.0%)
Update SessionAuth
class:
Create an instance method def user_id_for_session_id(self, session_id: str = None) -> str:
that returns a User
ID based on a Session ID:
- Return
None
ifsession_id
isNone
- Return
None
ifsession_id
is not a string - Return the value (the User ID) for the key
session_id
in the dictionaryuser_id_by_session_id
. - You must use
.get()
built-in for accessing in a dictionary a value based on key
Now you have 2 methods (create_session
and user_id_for_session_id
) for storing and retrieving a link between a User
ID and a Session ID.
bob@dylan:~$ cat main_2.py
#!/usr/bin/env python3
""" Main 2
"""
from api.v1.auth.session_auth import SessionAuth
sa = SessionAuth()
user_id_1 = "abcde"
session_1 = sa.create_session(user_id_1)
print("{} => {}: {}".format(user_id_1, session_1, sa.user_id_by_session_id))
user_id_2 = "fghij"
session_2 = sa.create_session(user_id_2)
print("{} => {}: {}".format(user_id_2, session_2, sa.user_id_by_session_id))
print("---")
tmp_session_id = None
tmp_user_id = sa.user_id_for_session_id(tmp_session_id)
print("{} => {}".format(tmp_session_id, tmp_user_id))
tmp_session_id = 89
tmp_user_id = sa.user_id_for_session_id(tmp_session_id)
print("{} => {}".format(tmp_session_id, tmp_user_id))
tmp_session_id = "doesntexist"
tmp_user_id = sa.user_id_for_session_id(tmp_session_id)
print("{} => {}".format(tmp_session_id, tmp_user_id))
print("---")
tmp_session_id = session_1
tmp_user_id = sa.user_id_for_session_id(tmp_session_id)
print("{} => {}".format(tmp_session_id, tmp_user_id))
tmp_session_id = session_2
tmp_user_id = sa.user_id_for_session_id(tmp_session_id)
print("{} => {}".format(tmp_session_id, tmp_user_id))
print("---")
session_1_bis = sa.create_session(user_id_1)
print("{} => {}: {}".format(user_id_1, session_1_bis, sa.user_id_by_session_id))
tmp_user_id = sa.user_id_for_session_id(session_1_bis)
print("{} => {}".format(session_1_bis, tmp_user_id))
tmp_user_id = sa.user_id_for_session_id(session_1)
print("{} => {}".format(session_1, tmp_user_id))
bob@dylan:~$
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 AUTH_TYPE=session_auth ./main_2.py
abcde => 8647f981-f503-4638-af23-7bb4a9e4b53f: {'8647f981-f503-4638-af23-7bb4a9e4b53f': 'abcde'}
fghij => a159ee3f-214e-4e91-9546-ca3ce873e975: {'a159ee3f-214e-4e91-9546-ca3ce873e975': 'fghij', '8647f981-f503-4638-af23-7bb4a9e4b53f': 'abcde'}
---
None => None
89 => None
doesntexist => None
---
8647f981-f503-4638-af23-7bb4a9e4b53f => abcde
a159ee3f-214e-4e91-9546-ca3ce873e975 => fghij
---
abcde => 5d2930ba-f6d6-4a23-83d2-4f0abc8b8eee: {'a159ee3f-214e-4e91-9546-ca3ce873e975': 'fghij', '8647f981-f503-4638-af23-7bb4a9e4b53f': 'abcde', '5d2930ba-f6d6-4a23-83d2-4f0abc8b8eee': 'abcde'}
5d2930ba-f6d6-4a23-83d2-4f0abc8b8eee => abcde
8647f981-f503-4638-af23-7bb4a9e4b53f => abcde
bob@dylan:~$
[:point_right: api/v1/auth/session_auth.py](api/v1/auth/session_auth.py
Score: 0.0% (Checks completed: 0.0%)
Update api/v1/auth/auth.py
by adding the method def session_cookie(self, request=None):
that returns a cookie value from a request:
- Return
None
ifrequest
isNone
- Return the value of the cookie named
_my_session_id
fromrequest
- the name of the cookie must be defined by the environment variableSESSION_NAME
- You must use
.get()
built-in for accessing the cookie in the request cookies dictionary - You must use the environment variable
SESSION_NAME
to define the name of the cookie used for the Session ID
In the first terminal:
bob@dylan:~$ cat main_3.py
#!/usr/bin/env python3
""" Cookie server
"""
from flask import Flask, request
from api.v1.auth.auth import Auth
auth = Auth()
app = Flask(__name__)
@app.route('/', methods=['GET'], strict_slashes=False)
def root_path():
""" Root path
"""
return "Cookie value: {}\n".format(auth.session_cookie(request))
if __name__ == "__main__":
app.run(host="0.0.0.0", port="5000")
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 AUTH_TYPE=session_auth SESSION_NAME=_my_session_id ./main_3.py
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
....
In a second terminal:
bob@dylan:~$ curl "http://0.0.0.0:5000"
Cookie value: None
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000" --cookie "_my_session_id=Hello"
Cookie value: Hello
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000" --cookie "_my_session_id=C is fun"
Cookie value: C is fun
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000" --cookie "_my_session_id_fake"
Cookie value: None
bob@dylan:~$
Score: 0.0% (Checks completed: 0.0%)
Update the @app.before_request
method in api/v1/app.py
:
- Add the URL path
/api/v1/auth_session/login/
in the list of excluded paths of the methodrequire_auth
- this route doesn’t exist yet but it should be accessible outside authentication - If
auth.authorization_header(request)
andauth.session_cookie(request)
returnNone
,abort(401)
In the first terminal:
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 AUTH_TYPE=session_auth SESSION_NAME=_my_session_id python3 -m api.v1.app
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
....
In a second terminal:
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/status"
{
"status": "OK"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/auth_session/login" # not found but not "blocked" by an authentication system
{
"error": "Not found"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users/me"
{
"error": "Unauthorized"
}
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users/me" -H "Authorization: Basic Ym9iQGhidG4uaW86SDBsYmVydG9uU2Nob29sOTgh" # Won't work because the environment variable AUTH_TYPE is equal to "session_auth"
{
"error": "Forbidden"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users/me" --cookie "_my_session_id=5535d4d7-3d77-4d06-8281-495dc3acfe76" # Won't work because no user is linked to this Session ID
{
"error": "Forbidden"
}
bob@dylan:~$
Score: 0.0% (Checks completed: 0.0%)
Update SessionAuth
class:
Create an instance method def current_user(self, request=None):
(overload) that returns a User
instance based on a cookie value:
- You must use
self.session_cookie(...)
andself.user_id_for_session_id(...)
to return the User ID based on the cookie_my_session_id
- By using this User ID, you will be able to retrieve a
User
instance from the database - you can useUser.get(...)
for retrieving aUser
from the database.
Now, you will be able to get a User based on his session ID.
In the first terminal:
bob@dylan:~$ cat main_4.py
#!/usr/bin/env python3
""" Main 4
"""
from flask import Flask, request
from api.v1.auth.session_auth import SessionAuth
from models.user import User
""" Create a user test """
user_email = "[email protected]"
user_clear_pwd = "fake pwd"
user = User()
user.email = user_email
user.password = user_clear_pwd
user.save()
""" Create a session ID """
sa = SessionAuth()
session_id = sa.create_session(user.id)
print("User with ID: {} has a Session ID: {}".format(user.id, session_id))
""" Create a Flask app """
app = Flask(__name__)
@app.route('/', methods=['GET'], strict_slashes=False)
def root_path():
""" Root path
"""
request_user = sa.current_user(request)
if request_user is None:
return "No user found\n"
return "User found: {}\n".format(request_user.id)
if __name__ == "__main__":
app.run(host="0.0.0.0", port="5000")
bob@dylan:~$
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 AUTH_TYPE=session_auth SESSION_NAME=_my_session_id ./main_4.py
User with ID: cf3ddee1-ff24-49e4-a40b-2540333fe992 has a Session ID: 9d1648aa-da79-4692-8236-5f9d7f9e9485
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
....
In a second terminal:
bob@dylan:~$ curl "http://0.0.0.0:5000/"
No user found
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/" --cookie "_my_session_id=Holberton"
No user found
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/" --cookie "_my_session_id=9d1648aa-da79-4692-8236-5f9d7f9e9485"
User found: cf3ddee1-ff24-49e4-a40b-2540333fe992
bob@dylan:~$
Score: 0.0% (Checks completed: 0.0%)
Create a new Flask view that handles all routes for the Session authentication.
In the file api/v1/views/session_auth.py
, create a route POST /auth_session/login
(= POST /api/v1/auth_session/login
):
- Slash tolerant (
/auth_session/login
==/auth_session/login/
) - You must use
request.form.get()
to retrieveemail
andpassword
parameters - If
email
is missing or empty, return the JSON{ "error": "email missing" }
with the status code400
- If
password
is missing or empty, return the JSON{ "error": "password missing" }
with the status code400
- Retrieve the
User
instance based on theemail
- you must use the class methodsearch
ofUser
(same as the one used for theBasicAuth
)- If no
User
found, return the JSON{ "error": "no user found for this email" }
with the status code404
- If the
password
is not the one of theUser
found, return the JSON{ "error": "wrong password" }
with the status code401
- you must useis_valid_password
from theUser
instance - Otherwise, create a Session ID for the
User
ID:- You must use
from api.v1.app import auth
- WARNING: please import it only where you need it - not on top of the file (can generate circular import - and break first tasks of this project) - You must use
auth.create_session(..)
for creating a Session ID - Return the dictionary representation of the
User
- you must useto_json()
method from User - You must set the cookie to the response - you must use the value of the environment variable
SESSION_NAME
as cookie name - tip
- You must use
- If no
In the file api/v1/views/__init__.py
, you must add this new view at the end of the file.
In the first terminal:
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 AUTH_TYPE=session_auth SESSION_NAME=_my_session_id python3 -m api.v1.app
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
....
In a second terminal:
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/auth_session/login" -XGET
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>405 Method Not Allowed</title>
<h1>Method Not Allowed</h1>
<p>The method is not allowed for the requested URL.</p>
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/auth_session/login" -XPOST
{
"error": "email missing"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/auth_session/login" -XPOST -d "[email protected]"
{
"error": "password missing"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/auth_session/login" -XPOST -d "[email protected]" -d "password=test"
{
"error": "no user found for this email"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/auth_session/login" -XPOST -d "[email protected]" -d "password=test"
{
"error": "wrong password"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/auth_session/login" -XPOST -d "[email protected]" -d "password=fake pwd"
{
"created_at": "2017-10-16 04:23:04",
"email": "[email protected]",
"first_name": null,
"id": "cf3ddee1-ff24-49e4-a40b-2540333fe992",
"last_name": null,
"updated_at": "2017-10-16 04:23:04"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/auth_session/login" -XPOST -d "[email protected]" -d "password=fake pwd" -vvv
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to 0.0.0.0 (127.0.0.1) port 5000 (#0)
> POST /api/v1/auth_session/login HTTP/1.1
> Host: 0.0.0.0:5000
> User-Agent: curl/7.54.0
> Accept: */*
> Content-Length: 42
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 42 out of 42 bytes
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: application/json
< Set-Cookie: _my_session_id=df05b4e1-d117-444c-a0cc-ba0d167889c4; Path=/
< Access-Control-Allow-Origin: *
< Content-Length: 210
< Server: Werkzeug/0.12.1 Python/3.4.3
< Date: Mon, 16 Oct 2017 04:57:08 GMT
<
{
"created_at": "2017-10-16 04:23:04",
"email": "[email protected]",
"first_name": null,
"id": "cf3ddee1-ff24-49e4-a40b-2540333fe992",
"last_name": null,
"updated_at": "2017-10-16 04:23:04"
}
* Closing connection 0
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users/me" --cookie "_my_session_id=df05b4e1-d117-444c-a0cc-ba0d167889c4"
{
"created_at": "2017-10-16 04:23:04",
"email": "[email protected]",
"first_name": null,
"id": "cf3ddee1-ff24-49e4-a40b-2540333fe992",
"last_name": null,
"updated_at": "2017-10-16 04:23:04"
}
bob@dylan:~$
Now you have an authentication based on a Session ID stored in cookie, perfect for a website (browsers love cookies).
Score: 0.0% (Checks completed: 0.0%)
Update the class SessionAuth
by adding a new method def destroy_session(self, request=None):
that deletes the user session / logout:
- If the
request
is equal toNone
, returnFalse
- If the
request
doesn’t contain the Session ID cookie, returnFalse
- you must useself.session_cookie(request)
- If the Session ID of the request is not linked to any User ID, return
False
- you must useself.user_id_for_session_id(...)
- Otherwise, delete in
self.user_id_by_session_id
the Session ID (as key of this dictionary) and returnTrue
Update the file api/v1/views/session_auth.py
, by adding a new route DELETE /api/v1/auth_session/logout
:
- Slash tolerant
- You must use
from api.v1.app import auth
- You must use
auth.destroy_session(request)
for deleting the Session ID contains in the request as cookie:- If
destroy_session
returnsFalse
,abort(404)
- Otherwise, return an empty JSON dictionary with the status code 200
- If
In the first terminal:
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 AUTH_TYPE=session_auth SESSION_NAME=_my_session_id python3 -m api.v1.app
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
....
In a second terminal:
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/auth_session/login" -XPOST -d "[email protected]" -d "password=fake pwd" -vvv
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to 0.0.0.0 (127.0.0.1) port 5000 (#0)
> POST /api/v1/auth_session/login HTTP/1.1
> Host: 0.0.0.0:5000
> User-Agent: curl/7.54.0
> Accept: */*
> Content-Length: 42
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 42 out of 42 bytes
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: application/json
< Set-Cookie: _my_session_id=e173cb79-d3fc-4e3a-9e6f-bcd345b24721; Path=/
< Access-Control-Allow-Origin: *
< Content-Length: 210
< Server: Werkzeug/0.12.1 Python/3.4.3
< Date: Mon, 16 Oct 2017 04:57:08 GMT
<
{
"created_at": "2017-10-16 04:23:04",
"email": "[email protected]",
"first_name": null,
"id": "cf3ddee1-ff24-49e4-a40b-2540333fe992",
"last_name": null,
"updated_at": "2017-10-16 04:23:04"
}
* Closing connection 0
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users/me" --cookie "_my_session_id=e173cb79-d3fc-4e3a-9e6f-bcd345b24721"
{
"created_at": "2017-10-16 04:23:04",
"email": "[email protected]",
"first_name": null,
"id": "cf3ddee1-ff24-49e4-a40b-2540333fe992",
"last_name": null,
"updated_at": "2017-10-16 04:23:04"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/auth_session/logout" --cookie "_my_session_id=e173cb79-d3fc-4e3a-9e6f-bcd345b24721"
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>405 Method Not Allowed</title>
<h1>Method Not Allowed</h1>
<p>The method is not allowed for the requested URL.</p>
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/auth_session/logout" --cookie "_my_session_id=e173cb79-d3fc-4e3a-9e6f-bcd345b24721" -XDELETE
{}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users/me" --cookie "_my_session_id=e173cb79-d3fc-4e3a-9e6f-bcd345b24721"
{
"error": "Forbidden"
}
bob@dylan:~$
Login, logout… what’s else?
Now, after getting a Session ID, you can request all protected API routes by using this Session ID, no need anymore to send User email and password every time.
Score: 0.0% (Checks completed: 0.0%)
Actually you have 2 authentication systems:
- Basic authentication
- Session authentication
Now you will add an expiration date to a Session ID.
Create a class SessionExpAuth
that inherits from SessionAuth
in the file api/v1/auth/session_exp_auth.py
:
- Overload
def __init__(self):
method:- Assign an instance attribute
session_duration
:- To the environment variable
SESSION_DURATION
casts to an integer - If this environment variable doesn’t exist or can’t be parse to an integer, assign to 0
- To the environment variable
- Assign an instance attribute
- Overload
def create_session(self, user_id=None):
- Create a Session ID by calling
super()
-super()
will call thecreate_session()
method ofSessionAuth
- Return
None
ifsuper()
can’t create a Session ID - Use this Session ID as key of the dictionary
user_id_by_session_id
- the value for this key must be a dictionary (called “session dictionary”):- The key
user_id
must be set to the variableuser_id
- The key
created_at
must be set to the current datetime - you must usedatetime.now()
- The key
- Return the Session ID created
- Create a Session ID by calling
- Overload
def user_id_for_session_id(self, session_id=None):
- Return
None
ifsession_id
isNone
- Return
None
ifuser_id_by_session_id
doesn’t contain any key equals tosession_id
- Return the
user_id
key from the session dictionary ifself.session_duration
is equal or under 0 - Return
None
if session dictionary doesn’t contain a keycreated_at
- Return
None
if thecreated_at
+session_duration
seconds are before the current datetime. datetime - timedelta - Otherwise, return
user_id
from the session dictionary
- Return
Update api/v1/app.py
to instantiate auth with SessionExpAuth
if the environment variable AUTH_TYPE
is equal to session_exp_auth
.
In the first terminal:
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 AUTH_TYPE=session_exp_auth SESSION_NAME=_my_session_id SESSION_DURATION=60 python3 -m api.v1.app
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
....
In a second terminal:
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/auth_session/login" -XPOST -d "[email protected]" -d "password=fake pwd" -vvv
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to 0.0.0.0 (127.0.0.1) port 5000 (#0)
> POST /api/v1/auth_session/login HTTP/1.1
> Host: 0.0.0.0:5000
> User-Agent: curl/7.54.0
> Accept: */*
> Content-Length: 42
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 42 out of 42 bytes
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: application/json
< Set-Cookie: _my_session_id=eea5d963-8dd2-46f0-9e43-fd05029ae63f; Path=/
< Access-Control-Allow-Origin: *
< Content-Length: 210
< Server: Werkzeug/0.12.1 Python/3.4.3
< Date: Mon, 16 Oct 2017 04:57:08 GMT
<
{
"created_at": "2017-10-16 04:23:04",
"email": "[email protected]",
"first_name": null,
"id": "cf3ddee1-ff24-49e4-a40b-2540333fe992",
"last_name": null,
"updated_at": "2017-10-16 04:23:04"
}
* Closing connection 0
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users/me" --cookie "_my_session_id=eea5d963-8dd2-46f0-9e43-fd05029ae63f"
{
"created_at": "2017-10-16 04:23:04",
"email": "[email protected]",
"first_name": null,
"id": "cf3ddee1-ff24-49e4-a40b-2540333fe992",
"last_name": null,
"updated_at": "2017-10-16 04:23:04"
}
bob@dylan:~$
bob@dylan:~$ sleep 10
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users/me" --cookie "_my_session_id=eea5d963-8dd2-46f0-9e43-fd05029ae63f"
{
"created_at": "2017-10-16 04:23:04",
"email": "[email protected]",
"first_name": null,
"id": "cf3ddee1-ff24-49e4-a40b-2540333fe992",
"last_name": null,
"updated_at": "2017-10-16 04:23:04"
}
bob@dylan:~$
bob@dylan:~$ sleep 51 # 10 + 51 > 60
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users/me" --cookie "_my_session_id=eea5d963-8dd2-46f0-9e43-fd05029ae63f"
{
"error": "Forbidden"
}
bob@dylan:~$
Score: 0.0% (Checks completed: 0.0%)
Since the beginning, all Session IDs are stored in memory. It means, if your application stops, all Session IDs are lost.
For avoid that, you will create a new authentication system, based on Session ID stored in database (for us, it will be in a file, like User
).
Create a new model UserSession
in models/user_session.py
that inherits from Base
:
- Implement the
def __init__(self, *args: list, **kwargs: dict):
like inUser
but for these 2 attributes:user_id
: stringsession_id
: string
Create a new authentication class SessionDBAuth
in api/v1/auth/session_db_auth.py
that inherits from SessionExpAuth
:
- Overload
def create_session(self, user_id=None):
that creates and stores new instance ofUserSession
and returns the Session ID - Overload
def user_id_for_session_id(self, session_id=None):
that returns the User ID by requestingUserSession
in the database based onsession_id
- Overload
def destroy_session(self, request=None):
that destroys theUserSession
based on the Session ID from the request cookie
Update api/v1/app.py
to instantiate auth
with SessionDBAuth
if the environment variable AUTH_TYPE
is equal to session_db_auth
.
In the first terminal:
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 AUTH_TYPE=session_db_auth SESSION_NAME=_my_session_id SESSION_DURATION=60 python3 -m api.v1.app
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
....
In a second terminal:
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/auth_session/login" -XPOST -d "[email protected]" -d "password=fake pwd" -vvv
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to 0.0.0.0 (127.0.0.1) port 5000 (#0)
> POST /api/v1/auth_session/login HTTP/1.1
> Host: 0.0.0.0:5000
> User-Agent: curl/7.54.0
> Accept: */*
> Content-Length: 42
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 42 out of 42 bytes
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: application/json
< Set-Cookie: _my_session_id=bacadfad-3c3b-4830-b1b2-3d77dfb9ad13; Path=/
< Access-Control-Allow-Origin: *
< Content-Length: 210
< Server: Werkzeug/0.12.1 Python/3.4.3
< Date: Mon, 16 Oct 2017 04:57:08 GMT
<
{
"created_at": "2017-10-16 04:23:04",
"email": "[email protected]",
"first_name": null,
"id": "cf3ddee1-ff24-49e4-a40b-2540333fe992",
"last_name": null,
"updated_at": "2017-10-16 04:23:04"
}
* Closing connection 0
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users/me" --cookie "_my_session_id=bacadfad-3c3b-4830-b1b2-3d77dfb9ad13"
{
"created_at": "2017-10-16 04:23:04",
"email": "[email protected]",
"first_name": null,
"id": "cf3ddee1-ff24-49e4-a40b-2540333fe992",
"last_name": null,
"updated_at": "2017-10-16 04:23:04"
}
bob@dylan:~$
bob@dylan:~$ sleep 10
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users/me" --cookie "_my_session_id=bacadfad-3c3b-4830-b1b2-3d77dfb9ad13"
{
"created_at": "2017-10-16 04:23:04",
"email": "[email protected]",
"first_name": null,
"id": "cf3ddee1-ff24-49e4-a40b-2540333fe992",
"last_name": null,
"updated_at": "2017-10-16 04:23:04"
}
bob@dylan:~$
bob@dylan:~$ sleep 60
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users/me" --cookie "_my_session_id=bacadfad-3c3b-4830-b1b2-3d77dfb9ad13"
{
"error": "Forbidden"
}
bob@dylan:~$
[:point_right: 👉 api/v1/auth/session_db_auth.py, 👉 api/v1/app.py, 👉 models/user_session.py
[:point_right: 👉 api/v1/auth/basic_auth.py
This project was done by SE. Moses Mwangi. Feel free to get intouch with me;
📱 WhatsApp +254115227963
📧 Email [email protected]
👍 A lot of thanks to ALX-Africa Software Engineering program for the project requirements.