Skip to content

Commit

Permalink
set up email, database credentials and add documentation in readme
Browse files Browse the repository at this point in the history
  • Loading branch information
VannySothea committed Nov 4, 2024
1 parent afa7716 commit 46b9da4
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 17 deletions.
47 changes: 39 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ FastAPI User Authentication: An open-source Python and FastAPI project for user
3. [Usage](#usage)
- [Alembic](#alembic)
- [Postman Collection](#postman-collection)
- [Sending Emails from the Shared Account](#sending-emails-from-the-shared-account)
- [Check Rate Limit and Account Lockout](#check-rate-limit-and-account-lockout)
4. [Project Structure](#project-structure)
5. [Testing](#testing) <!-- Placeholder section -->
Expand Down Expand Up @@ -156,13 +157,15 @@ REFRESH_TOKEN_EXPIRE_MINUTES=25920 #18 days = 25920 minutes
```
#### Secret Environment Variables
**Note: Change base on your credentials**
**Sample `.env.settings` File**:
```ini
DATABASE_HOSTNAME=localhost
DATABASE_PORT=5432
DATABASE_USERNAME=postgres
DATABASE_PASSWORD=YOUR_DATABASE_PASSWORD
DATABASE_NAME=YOUR_DATABASE_NAME
DATABASE_PASSWORD=user
DATABASE_NAME=fastapi_user_authentication
# JWT Secret Key
JWT_SECRET=355fa9f6f9c491417c53b701b26dd97df5825d4abd02957ce3bb1b9658593d9a
Expand All @@ -172,16 +175,18 @@ SECRET_KEY=9a35f82513e1cdf2748afbd4681ff2eda8fc29a46df52cc8c4cdd561c0632400
```
#### Mail Environment Variables
**Note: We highly encourage to use your own customized email address.**
**Sample `.env.mail` File**:
```ini
MAIL_USERNAME=REPLACE_THIS_WITH_YOUR_EMAIL_ADDRESS_@GMAIL.COM
MAIL_PASSWORD=REPLACE_THIS_WITH_YOUR_EMAIL_PASSWORD
MAIL_PORT=EMAIL_PORT
MAIL_SERVER=YOUR_EMAIL_SERVER
MAIL_USERNAME=open.source.user.authentication@gmail.com
MAIL_PASSWORD=avvx yapu kbko nbzg
MAIL_PORT=587
MAIL_SERVER=smtp.gmail.com
MAIL_STARTTLS=True
MAIL_SSL_TLS=False
MAIL_DEBUG=True
MAIL_FROM=REPLACE_THIS_WITH_YOUR_EMAIL_ADDRESS_@GMAIL.COM
MAIL_FROM=open.source.user.authentication@gmail.com
MAIL_FROM_NAME=APP_NAME
USE_CREDENTIALS=True
```
Expand Down Expand Up @@ -217,9 +222,12 @@ OPERATION_IP_LOCKOUT_PERIOD=10800
### Alembic
To manage database schema changes, this project utilizes Alembic. Ensure you have Alembic installed and configured. You can run migrations with the following command:
```bash
alembic revision --autogenerate -m "Your message here"
alembic upgrade head
```
or
```bash
alembic revision --autogenerate -m "Your message here"
```
This module can be tested and used via Postman. Below is example of how to interact with the user authentication API using Postman.
### Register a User
Expand Down Expand Up @@ -252,6 +260,29 @@ To make it easier, you can use the provided Postman collection that includes all
4. The collection will appear in your Postman app, ready to use.
### Sending Emails from the Shared Account
This application is configured to send emails from a dedicated account: `open.source.user.authentication@gmail.com`. This account is specifically created for application use and utilizes an app password for secure authentication.
**Note: It is perfectly fine to use your own customized email address.**
#### Important Guidelines
- **Account Usage**: Emails sent from this account should only be related to application functionalities (e.g., account verification, password resets).
- **Rate Limits**: Be aware that this account has a limit on the number of emails sent per day. Please do not send excessive emails to avoid being flagged for spam.
- **Content Restrictions**: Ensure that the content of the emails adheres to community standards and does not include spam or unsolicited messages.
- **Consent**: Always obtain consent from recipients before sending emails, particularly for verification purposes.
#### Important Notice: Shared Email Account Security
As this application is open source, the email account used for sending communications is also publicly accessible to anyone who has access to the codebase. Please keep the following points in mind:
- **Account Transparency**: The email account (`open.source.user.authentication@gmail.com`) is intended solely for sending application-related emails, such as account verification and notifications. Since it is shared, any contributor or user of the codebase may see the email credentials.
- **Data Handling**: Be mindful of the data you handle and send through this shared account. Avoid including sensitive personal information in email communications to protect users' privacy.
- **Security Practices**: While we use an app password for secure access, it's crucial to maintain best practices around data security. Do not share or expose the email credentials in public forums or repositories.
- **Usage Guidelines**: Only use the shared email account for legitimate application purposes. This helps prevent misuse of the account and ensures that we maintain a positive reputation for the application.
### Check Rate Limit and Account Lockout
For routes that implement rate limiting and lockout, you can make requests to that endpoint multiple times to test the functionality.
Expand Down
134 changes: 134 additions & 0 deletions alembic/versions/da30e7c419f2_auto_generated_db_tables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
"""auto generated db tables
Revision ID: da30e7c419f2
Revises:
Create Date: 2024-11-04 23:45:59.654459
"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = 'da30e7c419f2'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('registration_requests',
sa.Column('user_id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('user_name', sa.String(length=150), nullable=False),
sa.Column('email', sa.String(length=255), nullable=False),
sa.Column('password', sa.String(length=100), nullable=False),
sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.PrimaryKeyConstraint('user_id'),
sa.UniqueConstraint('user_id')
)
op.create_index(op.f('ix_registration_requests_email'), 'registration_requests', ['email'], unique=True)
op.create_table('roles',
sa.Column('role_id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('role_name', sa.String(length=50), nullable=False),
sa.PrimaryKeyConstraint('role_id'),
sa.UniqueConstraint('role_id'),
sa.UniqueConstraint('role_name')
)
op.create_table('users',
sa.Column('user_id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('user_name', sa.String(length=150), nullable=False),
sa.Column('email', sa.String(length=255), nullable=False),
sa.Column('password', sa.String(length=100), nullable=False),
sa.Column('status', sa.String(), server_default=sa.text("'active'"), nullable=False),
sa.Column('verified_at', sa.TIMESTAMP(timezone=True), nullable=True),
sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.Column('updated_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.PrimaryKeyConstraint('user_id'),
sa.UniqueConstraint('user_id')
)
op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True)
op.create_table('codes',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('code', sa.String(), nullable=False),
sa.Column('expires_at', sa.TIMESTAMP(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['users.user_id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('id')
)
op.create_index(op.f('ix_codes_code'), 'codes', ['code'], unique=False)
op.create_index(op.f('ix_codes_user_id'), 'codes', ['user_id'], unique=False)
op.create_table('user_roles',
sa.Column('user_id', sa.Integer(), nullable=True),
sa.Column('role_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['role_id'], ['roles.role_id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(['user_id'], ['users.user_id'], ondelete='CASCADE')
)
op.create_table('user_tokens',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('access_key', sa.String(length=250), nullable=False),
sa.Column('refresh_key', sa.String(length=250), nullable=False),
sa.Column('device_id', sa.String(), nullable=False),
sa.Column('device_name', sa.String(), server_default=sa.text("'NONAME'"), nullable=False),
sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.Column('last_used_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.Column('expires_at', sa.TIMESTAMP(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['users.user_id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_user_tokens_access_key'), 'user_tokens', ['access_key'], unique=False)
op.create_index(op.f('ix_user_tokens_device_id'), 'user_tokens', ['device_id'], unique=False)
op.create_index(op.f('ix_user_tokens_refresh_key'), 'user_tokens', ['refresh_key'], unique=False)
op.create_index(op.f('ix_user_tokens_user_id'), 'user_tokens', ['user_id'], unique=False)
op.create_table('verification_codes',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('code', sa.String(), nullable=False),
sa.Column('expires_at', sa.TIMESTAMP(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['users.user_id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('id')
)
op.create_index(op.f('ix_verification_codes_code'), 'verification_codes', ['code'], unique=False)
op.create_index(op.f('ix_verification_codes_user_id'), 'verification_codes', ['user_id'], unique=False)
op.create_table('verification_codes_opt',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('code', sa.String(), nullable=False),
sa.Column('expires_at', sa.TIMESTAMP(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['registration_requests.user_id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('id')
)
op.create_index(op.f('ix_verification_codes_opt_code'), 'verification_codes_opt', ['code'], unique=False)
op.create_index(op.f('ix_verification_codes_opt_user_id'), 'verification_codes_opt', ['user_id'], unique=False)
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_verification_codes_opt_user_id'), table_name='verification_codes_opt')
op.drop_index(op.f('ix_verification_codes_opt_code'), table_name='verification_codes_opt')
op.drop_table('verification_codes_opt')
op.drop_index(op.f('ix_verification_codes_user_id'), table_name='verification_codes')
op.drop_index(op.f('ix_verification_codes_code'), table_name='verification_codes')
op.drop_table('verification_codes')
op.drop_index(op.f('ix_user_tokens_user_id'), table_name='user_tokens')
op.drop_index(op.f('ix_user_tokens_refresh_key'), table_name='user_tokens')
op.drop_index(op.f('ix_user_tokens_device_id'), table_name='user_tokens')
op.drop_index(op.f('ix_user_tokens_access_key'), table_name='user_tokens')
op.drop_table('user_tokens')
op.drop_table('user_roles')
op.drop_index(op.f('ix_codes_user_id'), table_name='codes')
op.drop_index(op.f('ix_codes_code'), table_name='codes')
op.drop_table('codes')
op.drop_index(op.f('ix_users_email'), table_name='users')
op.drop_table('users')
op.drop_table('roles')
op.drop_index(op.f('ix_registration_requests_email'), table_name='registration_requests')
op.drop_table('registration_requests')
# ### end Alembic commands ###
2 changes: 1 addition & 1 deletion app/main.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from fastapi import FastAPI
from app.routes import user, security
from app.jobs.scheduler import start_scheduler, shutdown_scheduler

from app.config.role import initialize_app_roles
initialize_app_roles()




def create_application():
application = FastAPI()

Expand Down
13 changes: 7 additions & 6 deletions env/.env.mail
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
MAIL_USERNAME=REPLACE_THIS_WITH_YOUR_EMAIL_ADDRESS_@GMAIL.COM
MAIL_PASSWORD=REPLACE_THIS_WITH_YOUR_EMAIL_PASSWORD
MAIL_PORT=EMAIL_PORT
MAIL_SERVER=YOUR_EMAIL_SERVER
# Warning: PLEASE USE THESE CREDENTIALS FOR DEVELOPMENT PURPOSES ONLY
MAIL_USERNAME=open.source.user.authentication@gmail.com
MAIL_PASSWORD=avvx yapu kbko nbzg
MAIL_PORT=587
MAIL_SERVER=smtp.gmail.com
MAIL_STARTTLS=True
MAIL_SSL_TLS=False
MAIL_DEBUG=True
MAIL_FROM=REPLACE_THIS_WITH_YOUR_EMAIL_ADDRESS_@GMAIL.COM
MAIL_FROM_NAME=APP_NAME
MAIL_FROM=open.source.user.authentication@gmail.com
MAIL_FROM_NAME=fastapi-user-authentication
USE_CREDENTIALS=True
5 changes: 3 additions & 2 deletions env/.env.settings
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Warning: Do not commit this file to the repository
DATABASE_HOSTNAME=localhost
DATABASE_PORT=5432
DATABASE_USERNAME=postgres
DATABASE_PASSWORD=YOUR_DATABASE_PASSWORD
DATABASE_NAME=YOUR_DATABASE_NAME
DATABASE_NAME=fastapi_user_authentication
DATABASE_PASSWORD=user

# JWT Secret Key
JWT_SECRET=355fa9f6f9c491417c53b701b26dd97df5825d4abd02957ce3bb1b9658593d9a
Expand Down
Binary file modified requirements.txt
Binary file not shown.

0 comments on commit 46b9da4

Please sign in to comment.