Tangerine-Auth es una biblioteca de Python que proporciona utilidades para la autenticación del usuario y el manejo seguro de claves y datos cifrados. Utiliza BCRYPT para el hash de contraseña y JWT para la autenticación basada en token.
Nota: Tangerine-Auth se encuentra actualmente en Beta. Todavía no se recomienda para el uso de producción. ¡No dude en descargar y jugar con él mientras tanto! Estamos trabajando para agregar más pruebas y documentación. Si encuentra algún error o tiene alguna sugerencia, abra un problema en GitHub.
Use PIP para instalar el paquete:
pip install tangerine-authLos pasos generales para integrar a Yuzu en su aplicación son los siguientes:
def get_user_by_email ( email ):
conn = psycopg2 . connect ( "postgresql://postgres:<your postgres password>@localhost:5432/local_development" )
cur = conn . cursor ()
cur . execute ( "SELECT * FROM tangerine.users WHERE email = %s" , ( email ,))
user = cur . fetchone ()
cur . close ()
conn . close ()
if user :
return { '_id' : user [ 0 ], 'email' : user [ 1 ], 'password' : user [ 2 ]}
else :
return None
def create_user ( user_data ):
conn = psycopg2 . connect ( "postgresql://<your postgres password>@localhost:5432/local_development" )
cur = conn . cursor ()
cur . execute ( "INSERT INTO tangerine.users (email, password) VALUES (%s, %s) RETURNING id" , ( user_data [ 'email' ], user_data [ 'password' ]))
user_id = cur . fetchone ()[ 0 ]
conn . commit ()
cur . close ()
conn . close ()
return { '_id' : user_id , 'email' : user_data [ 'email' ], 'password' : user_data [ 'password' ]}Cree un objeto Keylime y páselo al constructor Yuzu. El objeto KeyLime se utiliza para manejar de forma segura claves y datos cifrados. Puede usar el objeto KeyLime para cifrar y descifrar datos, y almacenar y recuperar claves.
Cree un objeto Yuzu y pase el objeto KeyLime, la función para encontrar un usuario en su base de datos y la función para crear un usuario en su base de datos.
(Opcional) Yuzu usa BCRYPT a las contraseñas hash. Opcionalmente, puede pasar una función hash personalizada al constructor Yuzu. La función hash personalizada debe tomar una cadena de contraseña como argumento y devolver una cadena de contraseña hash.
Después de que se complete el paso 3, ahora puede usar el objeto Yuzu para registrar un nuevo usuario, iniciar sesión en un usuario, iniciar sesión un usuario y verificar los tokens de autenticación.
Yuzu fue construido para trabajar con Flask y Tangerine, actualmente hay dos WWT Middlewares incluidos con Yuzu, uno para Flask y otro para Tangerine. Una vez que haya inicializado la clase Yuzu correctamente, puede usar el middleware de mandarina llamando:
app . use ( auth . jwt_middleware ).La versión Flask JWT Middleware todavía es experimental y podría ser propensa a problemas. No se recomendará para el uso de la producción hasta que se haya probado más a fondo. El Middleware JWT funciona un poco diferente en Flask, para usarlo con Flask, lo usa como decorador:
from flask import Flask
from yuzu import Yuzu
app = Flask ( __name__ )
def get_user_by_email ( email ):
# Logic to create user in DB
pass
def create_user ( user_data ):
# Logic to create user in DB
pass
auth = Yuzu ( keychain , get_user_by_email , create_user ) # Fill with your functions
@ app . route ( '/secure_route' )
@ auth . flask_jwt_middleware ( auth )
def secure_route ():
# Your secure code here. This will only run if the JWT is valid.
return "This is a secure route"
if __name__ == "__main__" :
app . run ( debug = True )Nota: El Auth Middleware agrega los datos del usuario al objeto CTX. Puede acceder a los datos del usuario en su manejador de ruta llamando
ctx . auth . user or ctx . get ( "user" )Yuzu es una clase que proporciona funcionalidades de autenticación de usuarios. Utiliza BCRYPT para el hash de contraseña y JWT para crear y verificar tokens de autenticación.
A continuación se presentan los métodos clave de la clase Yuzu:
- `__init__(self, keychain, get_user_by_email, create_user, hash_func: Optional[Callable] = None)` : Initializes the Yuzu object .
- `get_config(self, key_name: str) -> str` : Fetches the configuration value for a given key .
- `authenticate(self, email: str, password: str) -> bool` : Checks if the given email and password are valid .
- `generate_auth_token(self, user_id: str, email: str) -> str` : Generates an authentication token for the given user .
- `verify_auth_token(self, token: str) -> dict` : Verifies if the given authentication token is valid .
- `sign_up(self, user_data: dict) -> dict` : Signs up a new user with the given user data .
- `login(self, email: str, password: str) -> Tuple[str, str]` : Logs in a user with the given email and password .
- `logout(self)` : Logs out the current user .
- `jwt_middleware()` : Tangerine middleware for JWT authentication .
- `flask_jwt_middleware(yuzu_instance)` : Flask middleware for JWT authentication .Keylime es una clase que proporciona funcionalidades para manejar de forma segura claves y datos cifrados.
A continuación se presentan los métodos clave de la clase Keylime:
- `__init__(self, keychain: Dict[str, bytes] = {})` : Initializes the KeyLime object .
- `add_key(self, key_name: str, key: bytes)` : Adds a key to the keychain .
- `remove_key(self, key_name: str)` : Removes a key from the keychain .
- `get_key(self, key_name: str) -> bytes` : Fetches a key from the keychain .
- `encrypt(self, key_name: str, message: str) -> str` : Encrypts a given message using a key from the keychain .
- `decrypt(self, key_name: str, cipher_text: str) -> str` : Decrypts a given cipher text using a key from the keychain .El general Conept para esto es que desea escribir dos funciones, una para encontrar un usuario en el sistema de base de datos elegido y otra para crear un usuario en el sistema de base de datos elegido. Estas funciones deben tomar un diccionario de datos del usuario como argumento y devolver un diccionario de datos del usuario. El diccionario de datos del usuario debe contener al menos un campo de correo electrónico y contraseña. El Diccionario de datos del usuario puede contener cualquier otro campo que desee almacenar en su base de datos.
Aquí hay un ejemplo de cómo usar las clases de Yuzu y Keylime:
from tangerine import Tangerine , Ctx , Router
from pymongo import MongoClient
from tangerine . key_lime import KeyLime
from tangerine . yuzu import Yuzu
import json
import jwt
import hashlib
app = Tangerine ( debug_level = 1 )
client = MongoClient ( 'mongodb://localhost:27017/' )
keychain = KeyLime ({
"SECRET_KEY" : "ILOVECATS" ,
})
def get_user_by_email ( email ):
db = client [ 'mydatabase' ]
users = db [ 'users' ]
query = { 'email' : email }
user = users . find_one ( query )
if user :
user [ '_id' ] = str ( user [ '_id' ]) # Convert ObjectId to string
return user
def create_user ( user_data ):
db = client [ 'mydatabase' ]
users = db [ 'users' ]
result = users . insert_one ( user_data )
if result . inserted_id :
user_data [ '_id' ] = str ( result . inserted_id ) # Convert ObjectId to string
return user_data
auth = Yuzu ( keychain , get_user_by_email , create_user )
# serve static files to any request not starting with /api
app . static ( '^/(?!api).*$' , './public' )
# This is how you define a custom middleware.
def hello_middle ( ctx : Ctx , next ) -> None :
ctx . hello_message = json . dumps ({ "message" : "Hello from middleware!" })
next ()
# ==================== AUTH HANDLERS ====================
def api_hello_world ( ctx : Ctx ) -> None :
ctx . body = ctx . hello_message
ctx . send ( 200 , content_type = 'application/json' )
def signup ( ctx : Ctx ) -> None :
user_data = ctx . request . body
created_user = auth . sign_up ( user_data )
if created_user :
ctx . body = json . dumps ( created_user )
ctx . send ( 201 , content_type = 'application/json' )
else :
ctx . send ( 500 , content_type = 'application/json' )
def login ( ctx : Ctx ) -> None :
user_data = ctx . request . body
email = user_data [ 'email' ]
password = user_data [ 'password' ]
user_id , token = auth . login ( email , password )
print ( ctx . user , "HELLO FROM LOGIN" )
if token :
ctx . body = json . dumps ({ "message" : "Logged in successfully" , "token" : token })
ctx . set_res_header ( "Set-Cookie" , f"auth_token= { token } ; HttpOnly; Path=/" )
ctx . send ( 200 , content_type = 'application/json' )
# Set the token as a cookie or in the response headers
else :
ctx . body = json . dumps ({ "message" : "Invalid credentials" })
ctx . send ( 401 , content_type = 'application/json' )
def logout ( ctx : Ctx ) -> None :
auth . logout ( ctx )
ctx . body = json . dumps ({ "message" : "Logged out successfully" })
ctx . send ( 200 , content_type = 'application/json' )
@ Router . auth_required
def get_protected_content ( ctx : Ctx ) -> None :
ctx . body = json . dumps ({ "message" : "This is protected content. Only authenticated users can see this. I hope you feel special ???." })
ctx . send ( 200 , content_type = 'application/json' )
# ==================== API ROUTES ====================
# if you need to bind more variables to your handler, you can pass in a closure
api_router = Router ( prefix = '/api' )
api_router . post ( '/logout' , logout )
api_router . post ( '/login' , login )
api_router . post ( '/signup' , signup )
api_router . get ( '/hello' , api_hello_world )
# api_router.get('/users', get_and_delete_users)
api_router . get ( '/protected' , get_protected_content )
app . use ( hello_middle )
app . use ( auth . jwt_middleware )
app . use_router ( api_router )
app . start ()Yuzu está diseñado para ser flexible, lo que le permite adaptarlo a las necesidades específicas de su proyecto. Una forma en que puede personalizar su comportamiento es cambiando las funciones predeterminadas de hash y verificación de contraseña.
De manera predeterminada, Yuzu usa la biblioteca bcrypt para el hash y la verificación de la contraseña. Si desea utilizar un enfoque diferente, puede pasar su propio hashing y funciones de verificación al constructor de clase Yuzu. Aquí hay un ejemplo de cómo hacerlo:
import hashlib
def my_hash_func ( password : str , salt : str = None ) -> str :
return hashlib . sha256 ( password . encode ()). hexdigest ()
def my_check_password_func ( password : str , hashed_password : str , salt : str = None ) -> bool :
return hashlib . sha256 ( password . encode ()). hexdigest () == hashed_password
auth = Yuzu ( keychain , get_user_by_email , create_user , hash_func = my_hash_func , checkpw_func = my_check_password_func )El uso de Argon2 para Hashing Argon2 es un algoritmo moderno y seguro de hashing de contraseña que es recomendado por la competencia de hashing de contraseña. Aquí hay un ejemplo de cómo usarlo con Yuzu:
from argon2 import PasswordHasher , exceptions
ph = PasswordHasher ( time_cost = 16 , memory_cost = 65536 )
def my_hash_func ( password : str , salt : str = None ) -> str :
return ph . hash ( password )
def my_check_password_func ( password : str , hashed_password : str , salt : str = None ) -> bool :
try :
return ph . verify ( hashed_password , password )
except exceptions . VerifyMismatchError :
return False
auth = Yuzu ( keychain , get_user_by_email , create_user , hash_func = my_hash_func , checkpw_func = my_check_password_func )Estos ejemplos le permiten cambiar del algoritmo BCRYPT predeterminado a SHA256 o Argon2. También puede modificar estas funciones para cambiar la dificultad de la función hash (por ejemplo, al aumentar el número de iteraciones o el uso de la memoria) según sus requisitos específicos.
Recuerde que cambiar estas funciones puede tener implicaciones para la seguridad de su aplicación. Debe comprender el funcionamiento de la función hash elegida y sus fortalezas y debilidades antes de tomar una decisión.