security.py
This file centralizes core security functionalities, including password hashing, JWT-based access token creation and verification, and secure password reset token management. It ensures robust authentication and authorization mechanisms for the application.
Scope
-
Manages password hashing and secure verification processes.
-
Generates and encodes secure JSON Web Tokens for user authentication.
-
Validates incoming access tokens, ensuring their integrity and expiration.
-
Creates and verifies specialized tokens for password reset workflows.
-
Handles authentication failures by raising appropriate HTTP exceptions.
Imports
-
datetime from datetimeProvides date and time objects for token expiration calculations. This ensures tokens have a defined lifespan, enhancing security by limiting exposure.
-
JWTError, jwt from joseFacilitates JSON Web Token (JWT) creation, encoding, and decoding. It handles cryptographic operations essential for secure token-based authentication within the application.
-
CryptContext from passlib.contextManages password hashing and verification using secure algorithms like bcrypt. This ensures sensitive user passwords are never stored in plain text, protecting user data.
-
Depends, HTTPException, status from fastapiProvides core FastAPI utilities for dependency injection, error handling, and HTTP status codes. These are crucial for integrating security functions into API routes.
-
HTTPBearer, HTTPAuthCredentials from fastapi.securityOffers FastAPI-specific security utilities for HTTP Bearer token authentication. It simplifies extracting and validating tokens from incoming request headers.
-
settings from app.core.configImports application-wide configuration settings, such as
SECRET_KEYandACCESS_TOKEN_EXPIRE_MINUTES. This centralizes sensitive parameters, promoting secure and flexible deployment.
Variables
-
pwd_contextAn instance of
CryptContextconfigured for password hashing. It provides a secure and consistent interface for handling password storage and verification across the application. -
securityAn instance of
HTTPBearerused by FastAPI for extracting Bearer tokens. It defines the authentication scheme, enabling token-based security for protected API endpoints.
Global Code
Initializes the password hashing context using bcrypt scheme. This setup ensures all password operations leverage strong, modern cryptographic practices for user data protection.
Instantiates the HTTP Bearer token scheme for FastAPI. This object is used as a dependency to automatically extract and provide authentication credentials from request headers.
Functions
verify_password
public
Verifies a plain-text password against its hashed counterpart using a secure password hashing context. This function ensures user-provided passwords match stored hashes without exposing the original password, crucial for authentication processes.
def verify_password(plain_password: str, hashed_password: str) -> bool:
-
plain_password(str)The unhashed, plain-text password string provided by the user for verification. This input is compared against the stored hashed password to confirm authenticity during login or password change operations.
-
hashed_password(str)The securely hashed password string retrieved from storage, typically a database. This value is the reference against which the
plain_passwordis checked, ensuring secure comparison without direct exposure.
verification_result(bool)A boolean indicating whether the
plain_passwordsuccessfully matches thehashed_password. ReturnsTrueif they match,Falseotherwise. This result determines authentication success or failure for users.
- Call the
verify()method of the globalpwd_contextobject, passingplain_passwordandhashed_passwordas arguments. - Return the boolean result obtained from the
pwd_context.verify()method, indicating password match status.
Usage Tips
- Always use this function to compare user-provided passwords with stored hashes. Never store plain-text passwords directly, and always hash new passwords using
pwd_context.hash()before storage for security.
Additional Notes
pwd_contextis typically configured globally with a strong hashing algorithm like bcrypt. Ensure this context is properly initialized and accessible for secure password operations throughout the application lifecycle.
hash_password
public
This function securely hashes a plain-text password using a pre-configured password context. It ensures sensitive user credentials are never stored in plain text, providing essential security for authentication systems. This is crucial for protecting user data.
def hash_password(password: str) -> str:
password(str)The plain-text password string to be securely hashed before storage or comparison. This input is critical for user authentication processes, ensuring that sensitive credentials are never exposed directly within the system. It must be a valid string.
hashed_password(str)The securely hashed password string generated by the
pwd_context. This output is suitable for storage in a database and subsequent comparison during user login attempts, ensuring robust security without storing original passwords.
- Call
pwd_context.hash()method with the providedpasswordto generate a hashed version. - Return the securely hashed password string.
Usage Tips
-
Always hash passwords before storing them in any persistent storage, even temporary caches. This prevents exposure of sensitive user data if the database is compromised, maintaining strong security practices.
-
Ensure
pwd_contextis properly configured with a strong hashing algorithm and appropriate work factors. Regularly review and update hashing parameters to adapt to evolving security standards and computational power.
Additional Notes
-
The
pwd_contextobject, typically a global instance ofCryptContext, manages the hashing algorithm and salt generation. Its configuration dictates the strength and computational cost of the resulting password hash. -
This function is a fundamental building block for secure user authentication. It should be used consistently across all password-handling operations, including registration, password changes, and verification processes, for system integrity.
create_password_reset_token
public
This function generates a secure JSON Web Token for password reset operations. It encodes user data with an expiration time and a specific type, ensuring token validity. This token is crucial for securely initiating and completing user password recovery processes.
def create_password_reset_token(data: dict) -> str:
data(dict)This dictionary contains user-specific information required for the password reset token. It typically includes the user's identifier, ensuring the token is linked to the correct account. This data is securely encoded within the JWT payload for later verification.
encoded_jwt(str)The function returns a string representing the securely encoded JWT. This token contains the user's data, expiration, and type, which is essential for verifying password reset requests. It serves as a temporary, verifiable credential.
- Create a mutable copy of the input
datadictionary, storing it into_encode. - Calculate the expiration time by adding 24 hours to the current UTC time using
datetime.utcnow()andtimedelta(hours=24), storing it inexpire. - Update the
to_encodedictionary by adding two new key-value pairs:"exp"set to the calculatedexpiretime, and"type"set to the string"password_reset". - Encode the
to_encodedictionary into a JSON Web Token usingjwt.encode(), providing thesettings.SECRET_KEYandsettings.ALGORITHMfor cryptographic signing. - Return the generated
encoded_jwtstring.
Usage Tips
-
Ensure the
datadictionary passed contains sufficient, non-sensitive information to identify the user for reset. Avoid including highly confidential data directly within the token payload. -
Always store the
settings.SECRET_KEYsecurely and rotate it periodically to maintain the integrity of generated tokens. Compromised keys invalidate all existing tokens.
Additional Notes
-
The token's 24-hour expiration (
timedelta(hours=24)) provides a reasonable window for users to complete the reset process. Adjust this duration based on security policies. -
The
typefield ("password_reset") within the token payload is vital for distinguishing this token from other JWTs, preventing misuse in different authentication contexts.
create_access_token
public
This function generates a JSON Web Token (JWT) for authentication purposes, encoding provided data. It calculates an expiration time for the token, either custom or default. This token is crucial for securing API endpoints and user sessions.
def create_access_token(data: dict, expires_delta: timedelta = None) -> str:
-
data(dict)A dictionary containing the payload data to be encoded into the JWT. This typically includes user identification or session-specific information. Ensure sensitive data is not directly stored here.
-
expires_delta(timedelta)An optional
timedeltaobject specifying the token's lifespan. If provided, it overrides the default expiration setting. This allows for flexible token validity periods based on specific use cases.
encoded_jwt(str)A string representing the securely encoded JWT. This token is then typically sent to the client for subsequent authenticated requests. It contains the payload and expiration information.
- Create a mutable copy of the input
datadictionary, namedto_encode, to avoid modifying the original dictionary directly. - Check if an
expires_deltatimedeltaobject was provided as an argument to the function.- If
expires_deltais provided:- Calculate the
expiretimestamp by addingexpires_deltato the current UTC time usingdatetime.utcnow().
- Calculate the
- If
expires_deltais not provided (i.e.,None):- Calculate the
expiretimestamp by adding a defaulttimedeltaofsettings.ACCESS_TOKEN_EXPIRE_MINUTESto the current UTC time.
- Calculate the
- If
- Update the
to_encodedictionary by adding anexpkey with the calculatedexpiretimestamp, representing the token's expiration. - Encode the
to_encodedictionary into a JWT string usingjwt.encode(), applyingsettings.SECRET_KEYandsettings.ALGORITHM. - Return the resulting
encoded_jwtstring, which is ready for client consumption.
Usage Tips
-
Always use a strong, securely managed
settings.SECRET_KEYfor JWT signing to prevent token tampering and unauthorized access. Rotate this key periodically for enhanced security. -
Carefully manage
expires_deltato balance security and user experience. Shorter lifespans enhance security but require more frequent re-authentication, impacting user flow.
Additional Notes
-
The
datetime.utcnow()function is used for expiration calculation, ensuring time zone independence. This is critical for consistent token validation across different server environments and client locations. -
This function relies on global
settingsforSECRET_KEY,ALGORITHM, andACCESS_TOKEN_EXPIRE_MINUTES. Ensure these are properly configured and loaded before calling this token creation utility.
verify_token
public
This function verifies a JSON Web Token (JWT) by decoding it using a secret key and algorithm. It extracts the payload if valid, providing user identity or session data. Essential for securing API endpoints and authenticating requests across the application.
def verify_token(token: str) -> dict:
token(str)The
tokenparameter is a string representing the JSON Web Token to be verified. This token typically contains encoded user information or session data, crucial for authenticating requests and authorizing access to protected resources.
payload(dict)The
payloadrepresents the decoded contents of the JWT as a dictionary if verification succeeds. It contains claims like user ID or roles. If token verification fails due toJWTError,Noneis returned, indicating an invalid or expired token.
- Attempt to decode the provided
tokenusingjwt.decode():- The decoding process uses
settings.SECRET_KEYfor signature verification. - The decoding process uses
settings.ALGORITHMto specify the expected algorithm.
- The decoding process uses
- If decoding is successful, return the
payloaddictionary containing the token's claims. - If a
JWTErroroccurs during decoding (e.g., invalid signature, expired token):- Catch the
JWTErrorexception. - Return
None, indicating that the token could not be verified.
- Catch the
Usage Tips
-
Integrate
verify_token()early in your request processing pipeline to ensure all protected routes are secured. Always check for aNonereturn value, indicating an invalid token, and respond with an appropriate authentication error. -
Ensure
settings.SECRET_KEYandsettings.ALGORITHMare securely managed and consistent across token generation and verification. Mismatched keys or algorithms will causeJWTError, leading to failed authentication attempts.
Additional Notes
-
This function only verifies the token's integrity and expiration, not its revocation status. For robust security, implement a token revocation mechanism (e.g., a blacklist) to invalidate tokens before their natural expiration.
-
The
jwt.decode()function automatically handles common JWT validation checks like expiration (exp) and not-before (nbf) claims. Ensure these claims are properly set during token creation for effective security.
get_token
public
This asynchronous function retrieves and validates an authentication token from HTTP credentials. It ensures the provided token is valid and returns the decoded payload. Essential for securing API endpoints, it integrates with FastAPI's dependency injection system for seamless authentication flow.
async def get_token(credentials: HTTPAuthCredentials = Depends(security)) -> dict:
credentials(HTTPAuthCredentials)Represents the HTTP authentication credentials, typically a Bearer token. This parameter is automatically injected by FastAPI's
Depends(security)mechanism, providing the raw token string for validation.
payload(dict)The decoded JWT payload as a dictionary, containing user-specific information like user ID or roles. This payload represents the authenticated user's identity and permissions for subsequent operations.
HTTPException 401 Unauthorized[401]Raised when the
tokenextracted fromcredentialsis invalid or cannot be verified. This indicates that the authentication credentials provided by the client are either missing, malformed, or expired.
- Extract the
tokenstring from thecredentials.credentialsobject. - Call the
verify_token()function, passing the extractedtoken, to decode and validate it. - Check if the returned
payloadfromverify_token()isNone:- If
payloadisNone, indicating invalid credentials:- Raise an
HTTPExceptionwithstatus.HTTP_401_UNAUTHORIZEDand a detail message "Invalid authentication credentials".
- Raise an
- If
- If
payloadis notNone, return the decodedpayloaddictionary.
Usage Tips
-
Always ensure the
securitydependency is correctly configured to extract tokens from request headers. This function relies onHTTPAuthCredentialsbeing properly populated before execution. -
Integrate this
get_tokenfunction as a FastAPI dependency for protected routes. This automatically handles token extraction and validation, simplifying secure endpoint development significantly.
Additional Notes
-
The
verify_tokenfunction (assumed to be defined elsewhere) is crucial for JWT decoding and signature verification. Its implementation directly impacts the security and robustness of this authentication flow. -
This function only validates the token's integrity and expiration. Further authorization checks (e.g., role-based access control) should be performed on the returned
payloadin subsequent dependency functions.
verify_password_reset_token
public
This function verifies a given `token` as a password reset token using `JWT` decoding. It ensures the token type is `password_reset` and returns the decoded `payload` or `None` if invalid. Essential for securing password reset workflows.
def verify_password_reset_token(token: str) -> dict:
token(str)This
tokenstring represents theJWTissued for a password reset operation. It contains encoded user information and expiration data. Crucial for authenticating reset requests.
payload(dict | None)Returns the decoded
JWTpayloadas a dictionary if valid and of typepassword_reset. Otherwise, it returnsNone, indicating an invalid or expired token.
JWTErrorRaised by
jwt.decode()if thetokenis invalid, expired, or tampered with. This exception is caught, leading to aNonereturn, preventing sensitive information exposure.
- Attempt to decode the provided
tokenusingjwt.decode():- Use
settings.SECRET_KEYfor signature verification. - Specify
settings.ALGORITHMfor decoding.
- Use
- If
JWTErroroccurs during decoding:- Return
None, indicating an invalid or expired token.
- Return
- If decoding is successful, check the
payload'stypefield:- If
payload.get("type")is not equal to"password_reset":- Return
None, as the token is not intended for password reset.
- Return
- If
- If the
payloadis valid and of typepassword_reset:- Return the decoded
payloaddictionary.
- Return the decoded
Usage Tips
-
Always ensure the
tokenis securely transmitted (e.g., viaHTTPS) to prevent interception and replay attacks. Validatetokenexpiration and user identity after successful decoding. -
Integrate this function early in your password reset endpoint to quickly reject invalid or malformed tokens. This reduces unnecessary database lookups and improves security.
Additional Notes
-
The
settings.SECRET_KEYandsettings.ALGORITHMare critical forJWTsecurity. Ensure they are robustly managed and never exposed in client-side code or version control. -
Returning
Nonefor bothJWTErrorand incorrecttypeprevents attackers from distinguishing between an invalid token and a valid token of the wrong type.
FAQs
Why is bcrypt chosen for password hashing in this module?
The
bcryptalgorithm is selected for password hashing due to its strong resistance against brute-force attacks and its adaptive nature. It provides a secure, industry-standard method for protecting user credentials effectively.
What is the rationale behind setting a short expiration for access tokens?
Access tokens are designed with a short expiration to minimize the window of opportunity for token misuse if compromised. This strategy enhances overall security by requiring frequent re-authentication or token refresh.
How does the system ensure password reset tokens are used only for their intended purpose?
Password reset tokens include a specific
typeclaim ("password_reset") within their payload. This claim is explicitly verified during token processing, ensuring tokens are used solely for password reset operations.
Insights
| Metric | Score | Level |
|---|---|---|
| Complexity | 0.70 | High Complexity |
| Security | 0.80 | Secure |
| Performance | 0.80 | Optimised |