In this project, you will learn what the authentication process means and implement a Basic Authentication on a simple API.
In the industry, you should not implement your own Basic authentication system and use a module or framework that doing it for you (like in Python-Flask: Flask-HTTPAuth). Here, for the learning purpose, we will walk through each step of this mechanism to understand it by doing.
Read or watch:
- REST API Authentication Mechanisms
- Base64 in Python
- HTTP header Authorization
- Flask
- Base64 - concept
At the end of this project, you are expected to be able to explain to anyone, without the help of Google:
- What authentication means
- What Base64 is
- How to encode a string in Base64
- What Basic authentication means
- How to send the Authorization header
- All your files will be interpreted/compiled on Ubuntu 18.04 LTS using
python3
(version3.7
) - All your files should end with a new line
- The first line of all your files should be exactly
#!/usr/bin/env python3
- A
README.md
file, at the root of the folder of the project, is mandatory - Your code should use the
pycodestyle
style (version2.5
) - All your files must be executable
- The length of your files will be tested using
wc
- All your modules should have a documentation (
python3 -c 'print(__import__("my_module").__doc__)'
) - All your classes should have a documentation (
python3 -c 'print(__import__("my_module").MyClass.__doc__)'
) - All your functions (inside and outside a class) should have a documentation (
python3 -c 'print(__import__("my_module").my_function.__doc__)'
andpython3 -c 'print(__import__("my_module").MyClass.my_function.__doc__)'
) - A documentation is not a simple word, it’s a real sentence explaining what’s the purpose of the module, class or method (the length of it will be verified)
Download and start your project from this archive.zip
In this archive, you will find a simple API with one model: User
. Storage of these users is done via a serialization/deserialization in files.
Setup and start server
bob@dylan:~$ pip3 install -r requirements.txt
...
bob@dylan:~$
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 python3 -m api.v1.app
* Serving Flask app "app" (lazy loading)
...
bob@dylan:~$
Use the API (in another tab or in your browser)
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/status" -vvv
* Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to 0.0.0.0 (127.0.0.1) port 5000 (#0)
> GET /api/v1/status HTTP/1.1
> Host: 0.0.0.0:5000
> User-Agent: curl/7.54.0
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: application/json
< Content-Length: 16
< Access-Control-Allow-Origin: *
< Server: Werkzeug/1.0.1 Python/3.7.5
< Date: Mon, 18 May 2020 20:29:21 GMT
<
{"status":"OK"}
* Closing connection 0
bob@dylan:~$
Repo:
- GitHub repository:
alx-backend-user-data
- Directory:
0x01-Basic_authentication
What's the HTTP status code for a request unauthorized? 401
of course!
Edit api/v1/app.py
:
- Add a new error handler for this status code, the response must be:
- a JSON:
{"error": "Unauthorized"}
- status code
401
- you must use
jsonify
from Flask
- a JSON:
For testing this new error handler, add a new endpoint in api/v1/views/index.py :
- Route:
GET /api/v1/unauthorized
- This endpoint must raise a
401
error by usingabort
- Custom Error Page
By calling abort(401)
, the error handler for 401 will be executed.
In the first terminal:
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 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/unauthorized"
{
"error": "Unauthorized"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/unauthorized" -vvv
* Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to 0.0.0.0 (127.0.0.1) port 5000 (#0)
> GET /api/v1/unauthorized HTTP/1.1
> Host: 0.0.0.0:5000
> User-Agent: curl/7.54.0
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 401 UNAUTHORIZED
< Content-Type: application/json
< Content-Length: 30
< Server: Werkzeug/0.12.1 Python/3.4.3
< Date: Sun, 24 Sep 2017 22:50:40 GMT
<
{
"error": "Unauthorized"
}
* Closing connection 0
bob@dylan:~$
Repo:
- GitHub repository:
alx-backend-user-data
- Directory:
0x01-Basic_authentication
- File:
api/v1/app.py, api/v1/views/index.py
What the HTTP status code for a request where the user is authenticate but not allowed to access to a resource? 403
of course!
Edit api/v1/app.py
:
- Add a new error handler for this status code, the response must be:
- a JSON:
{"error": "Forbidden"}
- status code
403
- you must use
jsonify
from Flask
- a JSON:
For testing this new error handler, add a new endpoint in api/v1/views/index.py :
- Route:
GET /api/v1/forbidden
- This endpoint must raise a 403 error by using
abort
- Custom Error Pages
By calling abort(403)
, the error handler for 403 will be executed.
In the first terminal:
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 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/forbidden"
{
"error": "Forbidden"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/forbidden" -vvv
* Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to 0.0.0.0 (127.0.0.1) port 5000 (#0)
> GET /api/v1/forbidden HTTP/1.1
> Host: 0.0.0.0:5000
> User-Agent: curl/7.54.0
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 403 FORBIDDEN
< Content-Type: application/json
< Content-Length: 27
< Server: Werkzeug/0.12.1 Python/3.4.3
< Date: Sun, 24 Sep 2017 22:54:22 GMT
<
{
"error": "Forbidden"
}
* Closing connection 0
bob@dylan:~$
Repo:
- GitHub repository:
alx-backend-user-data
- Directory:
0x01-Basic_authentication
- File:
api/v1/app.py, api/v1/views/index.py
Now you will create a class to manage the API authentication.
- Create a folder
api/v1/auth
- Create an empty file
api/v1/auth/__init__.py
- Create the class
Auth
:- in the file
api/v1/auth/auth.py
- import
request
fromflask
- class name
Auth
- public method
def require_auth(self, path: str, excluded_paths: List[str]) -> bool:
that returnsFalse
-path
andexcluded_paths
will be used later, now, you don’t need to take care of them - public method
def authorization_header(self, request=None) -> str:
that returnsNone
-request
will be the Flask request object - public method
def current_user(self, request=None) -> TypeVar('User'):
that returnsNone
-request
will be the Flask request object
- in the file
This class is the template for all authentication system you will implement.
bob@dylan:~$ cat main_0.py
#!/usr/bin/env python3
""" Main 0
"""
from api.v1.auth.auth import Auth
a = Auth()
print(a.require_auth("/api/v1/status/", ["/api/v1/status/"]))
print(a.authorization_header())
print(a.current_user())
bob@dylan:~$
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 ./main_0.py
False
None
None
bob@dylan:~$
Repo:
- GitHub repository:
alx-backend-user-data
- Directory:
0x01-Basic_authentication
- File:
api/v1/auth, api/v1/auth/__init__.py, api/v1/auth/auth.py
Update the method def require_auth(self, path: str, excluded_paths: List[str]) -> bool:
in Auth
that returns True
if the path
is not in the list of strings excluded_paths
:
- Returns
True
ifpath
isNone
- Returns
True
ifexcluded_paths
isNone
or empty - Returns
False
ifpath
is inexcluded_paths
- You can assume
excluded_paths
contains string path always ending by a/
- This method must be slash tolerant:
path=/api/v1/status
andpath=/api/v1/status/
must be returnedFalse
ifexcluded_paths
contains/api/v1/status/
bob@dylan:~$ cat main_1.py
#!/usr/bin/env python3
""" Main 1
"""
from api.v1.auth.auth import Auth
a = Auth()
print(a.require_auth(None, None))
print(a.require_auth(None, []))
print(a.require_auth("/api/v1/status/", []))
print(a.require_auth("/api/v1/status/", ["/api/v1/status/"]))
print(a.require_auth("/api/v1/status", ["/api/v1/status/"]))
print(a.require_auth("/api/v1/users", ["/api/v1/status/"]))
print(a.require_auth("/api/v1/users", ["/api/v1/status/", "/api/v1/stats"]))
bob@dylan:~$
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 ./main_1.py
True
True
True
False
False
True
True
bob@dylan:~$
Repo:
- GitHub repository:
alx-backend-user-data
- Directory:
0x01-Basic_authentication
- File:
api/v1/auth/auth.py
Now you will validate all requests to secure the API:
Update the method def authorization_header(self, request=None) -> str:
in api/v1/auth/auth.py
:
- If
request
isNone
, returnsNone
- If
request
doesn’t contain the header keyAuthorization
, returnsNone
- Otherwise, return the value of the header request
Authorization
Update the file api/v1/app.py :
- Create a variable
auth
initialized toNone
after theCORS
definition - Based on the environment variable
AUTH_TYPE
, load and assign the right instance of authentication toauth
- if
auth
:- import
Auth
fromapi.v1.auth.auth
- create an instance of
Auth
and assign it to the variableauth
- import
- if
Now the biggest piece is the filtering of each request. For that you will use the Flask method before_request
- Add a method in
api/v1/app.py
to handlerbefore_request
- if
auth
isNone
, do nothing - if
request.path
is not part of this list['/api/v1/status/', '/api/v1/unauthorized/', '/api/v1/forbidden/']
, do nothing - you must use the methodrequire_auth
from theauth
instance - if
auth.authorization_header(request)
returnsNone
, raise the error401
- you must useabort
- if
auth.current_user(request)
returnsNone
, raise the error403
- you must useabort
- if
In the first terminal:
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 AUTH_TYPE=auth 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/status/"
{
"status": "OK"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users"
{
"error": "Unauthorized"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users" -H "Authorization: Test"
{
"error": "Forbidden"
}
bob@dylan:~$
Repo:
- GitHub repository:
alx-backend-user-data
- Directory:
0x01-Basic_authentication
- File:
api/v1/app.py, api/v1/auth/auth.py
Create a class BasicAuth
that inherits from Auth
. For the moment this class will be empty.
Update api/v1/app.py
for using BasicAuth
class instead of Auth
depending of the value of the environment variable AUTH_TYPE
, If AUTH_TYPE
is equal to basic_auth
:
- import
BasicAuth
fromapi.v1.auth.basic_auth
- create an instance of
BasicAuth
and assign it to the variableauth
Otherwise, keep the previous mechanism with auth
an instance of Auth
.
In the first terminal:
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 AUTH_TYPE=basic_auth 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/status/"
{
"status": "OK"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users"
{
"error": "Unauthorized"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users" -H "Authorization: Test"
{
"error": "Forbidden"
}
bob@dylan:~$
Repo:
- GitHub repository:
alx-backend-user-data
- Directory:
0x01-Basic_authentication
- File:
api/v1/app.py, api/v1/auth/basic_auth.py
Add the method def extract_base64_authorization_header(self, authorization_header: str) -> str:
in the class BasicAuth
that returns the Base64 part of the Authorization
header for a Basic Authentication:
- Return
None
ifauthorization_header
isNone
- Return
None
ifauthorization_header
is not a string - Return
None
ifauthorization_header
doesn’t start byBasic
(with a space at the end) - Otherwise, return the value after
Basic
(after the space) - You can assume
authorization_header
contains only oneBasic
bob@dylan:~$ cat main_2.py
#!/usr/bin/env python3
""" Main 2
"""
from api.v1.auth.basic_auth import BasicAuth
a = BasicAuth()
print(a.extract_base64_authorization_header(None))
print(a.extract_base64_authorization_header(89))
print(a.extract_base64_authorization_header("Holberton School"))
print(a.extract_base64_authorization_header("Basic Holberton"))
print(a.extract_base64_authorization_header("Basic SG9sYmVydG9u"))
print(a.extract_base64_authorization_header("Basic SG9sYmVydG9uIFNjaG9vbA=="))
print(a.extract_base64_authorization_header("Basic1234"))
bob@dylan:~$
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 ./main_2.py
None
None
None
Holberton
SG9sYmVydG9u
SG9sYmVydG9uIFNjaG9vbA==
None
bob@dylan:~$
Repo:
- GitHub repository:
alx-backend-user-data
- Directory:
0x01-Basic_authentication
- File:
api/v1/auth/basic_auth.py
Add the method def decode_base64_authorization_header(self, base64_authorization_header: str) -> str:
in the class BasicAuth
that returns the decoded value of a Base64 string base64_authorization_header
:
- Return
None
ifbase64_authorization_header
isNone
- Return
None
ifbase64_authorization_header
is not a string - Return
None
ifbase64_authorization_header
is not a valid Base64 - you can usetry/except
- Otherwise, return the decoded value as UTF8 string - you can use
decode('utf-8')
bob@dylan:~$ cat main_3.py
#!/usr/bin/env python3
""" Main 3
"""
from api.v1.auth.basic_auth import BasicAuth
a = BasicAuth()
print(a.decode_base64_authorization_header(None))
print(a.decode_base64_authorization_header(89))
print(a.decode_base64_authorization_header("Holberton School"))
print(a.decode_base64_authorization_header("SG9sYmVydG9u"))
print(a.decode_base64_authorization_header("SG9sYmVydG9uIFNjaG9vbA=="))
print(a.decode_base64_authorization_header(a.extract_base64_authorization_header("Basic SG9s
YmVydG9uIFNjaG9vbA==")))
bob@dylan:~$
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 ./main_3.py
None
None
None
Holberton
Holberton School
Holberton School
bob@dylan:~$
Repo:
- GitHub repository:
alx-backend-user-data
- Directory:
0x01-Basic_authentication
- File:
api/v1/auth/basic_auth.py
Add the method def extract_user_credentials(self, decoded_base64_authorization_header: str) -> (str, str)
in the class BasicAuth
that returns the user email and password from the Base64 decoded value.
- This method must return 2 values
- Return
None, None
ifdecoded_base64_authorization_header
isNone
- Return
None, None
ifdecoded_base64_authorization_header
is not a string - Return
None, None
ifdecoded_base64_authorization_header
doesn’t contain:
- Otherwise, return the user email and the user password - these 2 values must be separated by a
:
- You can assume
decoded_base64_authorization_header
will contain only one:
bob@dylan:~$ cat main_4.py
#!/usr/bin/env python3
""" Main 4
"""
from api.v1.auth.basic_auth import BasicAuth
a = BasicAuth()
print(a.extract_user_credentials(None))
print(a.extract_user_credentials(89))
print(a.extract_user_credentials("Holberton School"))
print(a.extract_user_credentials("Holberton:School"))
print(a.extract_user_credentials("bob@gmail.com:toto1234"))
bob@dylan:~$
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 ./main_4.py
(None, None)
(None, None)
(None, None)
('Holberton', 'School')
('bob@gmail.com', 'toto1234')
bob@dylan:~$
Repo:
- GitHub repository:
alx-backend-user-data
- Directory:
0x01-Basic_authentication
- File:
api/v1/auth/basic_auth.py
Add the method def user_object_from_credentials(self, user_email: str, user_pwd: str) -> TypeVar('User'):
in the class BasicAuth
that returns the User
instance based on his email and password.
- Return
None
ifuser_email
isNone
or not a string - Return
None
ifuser_pwd
isNone
or not a string - Return
None
if your database (file) doesn’t contain anyUser
instance with email equal touser_email
- you should use the class methodsearch
of theUser
to lookup the list of users based on their email. Don’t forget to test all cases: “what if there is no user in DB?”, etc. - Return
None
ifuser_pwd
is not the password of theUser
instance found - you must use the methodis_valid_password
ofUser
- Otherwise, return the
User
instance
bob@dylan:~$ cat main_5.py
#!/usr/bin/env python3
""" Main 5
"""
import uuid
from api.v1.auth.basic_auth import BasicAuth
from models.user import User
""" Create a user test """
user_email = str(uuid.uuid4())
user_clear_pwd = str(uuid.uuid4())
user = User()
user.email = user_email
user.first_name = "Bob"
user.last_name = "Dylan"
user.password = user_clear_pwd
print("New user: {}".format(user.display_name()))
user.save()
""" Retreive this user via the class BasicAuth """
a = BasicAuth()
u = a.user_object_from_credentials(None, None)
print(u.display_name() if u is not None else "None")
u = a.user_object_from_credentials(89, 98)
print(u.display_name() if u is not None else "None")
u = a.user_object_from_credentials("email@notfound.com", "pwd")
print(u.display_name() if u is not None else "None")
u = a.user_object_from_credentials(user_email, "pwd")
print(u.display_name() if u is not None else "None")
u = a.user_object_from_credentials(user_email, user_clear_pwd)
print(u.display_name() if u is not None else "None")
bob@dylan:~$
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 ./main_5.py
New user: Bob Dylan
None
None
None
None
Bob Dylan
bob@dylan:~$
Repo:
- GitHub repository:
alx-backend-user-data
- Directory:
0x01-Basic_authentication
- File:
api/v1/auth/basic_auth.py
Now, you have all pieces for having a complete Basic authentication.
Add the method def current_user(self, request=None) -> TypeVar('User')
in the class BasicAuth
that overloads Auth
and retrieves the User
instance for a request:
- You must use
authorization_header
- You must use
extract_base64_authorization_header
- You must use
decode_base64_authorization_header
- You must use
extract_user_credentials
- You must use
user_object_from_credentials
With this update, now your API is fully protected by a Basic Authentication. Enjoy!
In the first terminal:
bob@dylan:~$ cat main_6.py
#!/usr/bin/env python3
""" Main 6
"""
import base64
from api.v1.auth.basic_auth import BasicAuth
from models.user import User
""" Create a user test """
user_email = "bob@hbtn.io"
user_clear_pwd = "H0lbertonSchool98!"
user = User()
user.email = user_email
user.password = user_clear_pwd
print("New user: {} / {}".format(user.id, user.display_name()))
user.save()
basic_clear = "{}:{}".format(user_email, user_clear_pwd)
print("Basic Base64: {}".format(base64.b64encode(basic_clear.encode('utf-8')).decode("utf-
8")))
bob@dylan:~$
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 ./main_6.py
New user: 9375973a-68c7-46aa-b135-29f79e837495 / bob@hbtn.io
Basic Base64: Ym9iQGhidG4uaW86SDBsYmVydG9uU2Nob29sOTgh
bob@dylan:~$
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 AUTH_TYPE=basic_auth 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/users"
{
"error": "Unauthorized"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users" -H "Authorization: Test"
{
"error": "Forbidden"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users" -H "Authorization: Basic test"
{
"error": "Forbidden"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users" -H "Authorization: Basic Ym9iQGhidG4uaW
86SDBsYmVydG9uU2Nob29sOTgh"
[
{
"created_at": "2017-09-25 01:55:17",
"email": "bob@hbtn.io",
"first_name": null,
"id": "9375973a-68c7-46aa-b135-29f79e837495",
"last_name": null,
"updated_at": "2017-09-25 01:55:17"
}
]
bob@dylan:~$
Repo:
- GitHub repository:
alx-backend-user-data
- Directory:
0x01-Basic_authentication
- File:
api/v1/auth/basic_auth.py
Improve the method def extract_user_credentials(self, decoded_base64_authorization_header)
to allow password with :
.
In the first terminal:
bob@dylan:~$ cat main_100.py
#!/usr/bin/env python3
""" Main 100
"""
import base64
from api.v1.auth.basic_auth import BasicAuth
from models.user import User
""" Create a user test """
user_email = "bob100@hbtn.io"
user_clear_pwd = "H0lberton:School:98!"
user = User()
user.email = user_email
user.password = user_clear_pwd
print("New user: {}".format(user.id))
user.save()
basic_clear = "{}:{}".format(user_email, user_clear_pwd)
print("Basic Base64: {}".format(base64.b64encode(basic_clear.encode('utf-8')).decode("utf-
8")))
bob@dylan:~$
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 ./main_100.py
New user: 5891469b-d2d5-4d33-b05d-02617d665368
Basic Base64: Ym9iMTAwQGhidG4uaW86SDBsYmVydG9uOlNjaG9vbDo5OCE=
bob@dylan:~$
bob@dylan:~$ API_HOST=0.0.0.0 API_PORT=5000 AUTH_TYPE=basic_auth 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/users"
{
"error": "Unauthorized"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users" -H "Authorization: Test"
{
"error": "Forbidden"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users" -H "Authorization: Basic test"
{
"error": "Forbidden"
}
bob@dylan:~$
bob@dylan:~$ curl "http://0.0.0.0:5000/api/v1/users" -H "Authorization: Basic Ym9iMTAwQGhidG
4uaW86SDBsYmVydG9uOlNjaG9vbDo5OCE="
[
{
"created_at": "2017-09-25 01:55:17",
"email": "bob@hbtn.io",
"first_name": null,
"id": "9375973a-68c7-46aa-b135-29f79e837495",
"last_name": null,
"updated_at": "2017-09-25 01:55:17"
},
{
"created_at": "2017-09-25 01:59:42",
"email": "bob100@hbtn.io",
"first_name": null,
"id": "5891469b-d2d5-4d33-b05d-02617d665368",
"last_name": null,
"updated_at": "2017-09-25 01:59:42"
}
]
bob@dylan:~$
Repo:
- GitHub repository:
alx-backend-user-data
- Directory:
0x01-Basic_authentication
- File:
api/v1/auth/basic_auth.py
Improve def require_auth(self, path, excluded_paths)
by allowing *
at the end of excluded paths.
Example for excluded_paths = ["/api/v1/stat*"]
:
/api/v1/users
will returnTrue
/api/v1/status
will returnFalse
/api/v1/stats
will returnFalse
Repo:
- GitHub repository:
alx-backend-user-data
- Directory:
0x01-Basic_authentication
- File:
api/v1/auth/auth.py