Skip to content

Commit 46b9da4

Browse files
committed
set up email, database credentials and add documentation in readme
1 parent afa7716 commit 46b9da4

File tree

6 files changed

+184
-17
lines changed

6 files changed

+184
-17
lines changed

README.md

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ FastAPI User Authentication: An open-source Python and FastAPI project for user
2727
3. [Usage](#usage)
2828
- [Alembic](#alembic)
2929
- [Postman Collection](#postman-collection)
30+
- [Sending Emails from the Shared Account](#sending-emails-from-the-shared-account)
3031
- [Check Rate Limit and Account Lockout](#check-rate-limit-and-account-lockout)
3132
4. [Project Structure](#project-structure)
3233
5. [Testing](#testing) <!-- Placeholder section -->
@@ -156,13 +157,15 @@ REFRESH_TOKEN_EXPIRE_MINUTES=25920 #18 days = 25920 minutes
156157
```
157158
158159
#### Secret Environment Variables
160+
**Note: Change base on your credentials**
161+
159162
**Sample `.env.settings` File**:
160163
```ini
161164
DATABASE_HOSTNAME=localhost
162165
DATABASE_PORT=5432
163166
DATABASE_USERNAME=postgres
164-
DATABASE_PASSWORD=YOUR_DATABASE_PASSWORD
165-
DATABASE_NAME=YOUR_DATABASE_NAME
167+
DATABASE_PASSWORD=user
168+
DATABASE_NAME=fastapi_user_authentication
166169
167170
# JWT Secret Key
168171
JWT_SECRET=355fa9f6f9c491417c53b701b26dd97df5825d4abd02957ce3bb1b9658593d9a
@@ -172,16 +175,18 @@ SECRET_KEY=9a35f82513e1cdf2748afbd4681ff2eda8fc29a46df52cc8c4cdd561c0632400
172175
```
173176
174177
#### Mail Environment Variables
178+
**Note: We highly encourage to use your own customized email address.**
179+
175180
**Sample `.env.mail` File**:
176181
```ini
177-
MAIL_USERNAME=REPLACE_THIS_WITH_YOUR_EMAIL_ADDRESS_@GMAIL.COM
178-
MAIL_PASSWORD=REPLACE_THIS_WITH_YOUR_EMAIL_PASSWORD
179-
MAIL_PORT=EMAIL_PORT
180-
MAIL_SERVER=YOUR_EMAIL_SERVER
182+
MAIL_USERNAME=open.source.user.authentication@gmail.com
183+
MAIL_PASSWORD=avvx yapu kbko nbzg
184+
MAIL_PORT=587
185+
MAIL_SERVER=smtp.gmail.com
181186
MAIL_STARTTLS=True
182187
MAIL_SSL_TLS=False
183188
MAIL_DEBUG=True
184-
MAIL_FROM=REPLACE_THIS_WITH_YOUR_EMAIL_ADDRESS_@GMAIL.COM
189+
MAIL_FROM=open.source.user.authentication@gmail.com
185190
MAIL_FROM_NAME=APP_NAME
186191
USE_CREDENTIALS=True
187192
```
@@ -217,9 +222,12 @@ OPERATION_IP_LOCKOUT_PERIOD=10800
217222
### Alembic
218223
To manage database schema changes, this project utilizes Alembic. Ensure you have Alembic installed and configured. You can run migrations with the following command:
219224
```bash
220-
alembic revision --autogenerate -m "Your message here"
221225
alembic upgrade head
222226
```
227+
or
228+
```bash
229+
alembic revision --autogenerate -m "Your message here"
230+
```
223231
This module can be tested and used via Postman. Below is example of how to interact with the user authentication API using Postman.
224232
225233
### Register a User
@@ -252,6 +260,29 @@ To make it easier, you can use the provided Postman collection that includes all
252260
253261
4. The collection will appear in your Postman app, ready to use.
254262
263+
### Sending Emails from the Shared Account
264+
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.
265+
266+
**Note: It is perfectly fine to use your own customized email address.**
267+
268+
#### Important Guidelines
269+
- **Account Usage**: Emails sent from this account should only be related to application functionalities (e.g., account verification, password resets).
270+
- **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.
271+
- **Content Restrictions**: Ensure that the content of the emails adheres to community standards and does not include spam or unsolicited messages.
272+
- **Consent**: Always obtain consent from recipients before sending emails, particularly for verification purposes.
273+
274+
#### Important Notice: Shared Email Account Security
275+
276+
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:
277+
278+
- **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.
279+
280+
- **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.
281+
282+
- **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.
283+
284+
- **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.
285+
255286
### Check Rate Limit and Account Lockout
256287
For routes that implement rate limiting and lockout, you can make requests to that endpoint multiple times to test the functionality.
257288
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
"""auto generated db tables
2+
3+
Revision ID: da30e7c419f2
4+
Revises:
5+
Create Date: 2024-11-04 23:45:59.654459
6+
7+
"""
8+
from typing import Sequence, Union
9+
10+
from alembic import op
11+
import sqlalchemy as sa
12+
13+
14+
# revision identifiers, used by Alembic.
15+
revision: str = 'da30e7c419f2'
16+
down_revision: Union[str, None] = None
17+
branch_labels: Union[str, Sequence[str], None] = None
18+
depends_on: Union[str, Sequence[str], None] = None
19+
20+
21+
def upgrade() -> None:
22+
# ### commands auto generated by Alembic - please adjust! ###
23+
op.create_table('registration_requests',
24+
sa.Column('user_id', sa.Integer(), autoincrement=True, nullable=False),
25+
sa.Column('user_name', sa.String(length=150), nullable=False),
26+
sa.Column('email', sa.String(length=255), nullable=False),
27+
sa.Column('password', sa.String(length=100), nullable=False),
28+
sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
29+
sa.PrimaryKeyConstraint('user_id'),
30+
sa.UniqueConstraint('user_id')
31+
)
32+
op.create_index(op.f('ix_registration_requests_email'), 'registration_requests', ['email'], unique=True)
33+
op.create_table('roles',
34+
sa.Column('role_id', sa.Integer(), autoincrement=True, nullable=False),
35+
sa.Column('role_name', sa.String(length=50), nullable=False),
36+
sa.PrimaryKeyConstraint('role_id'),
37+
sa.UniqueConstraint('role_id'),
38+
sa.UniqueConstraint('role_name')
39+
)
40+
op.create_table('users',
41+
sa.Column('user_id', sa.Integer(), autoincrement=True, nullable=False),
42+
sa.Column('user_name', sa.String(length=150), nullable=False),
43+
sa.Column('email', sa.String(length=255), nullable=False),
44+
sa.Column('password', sa.String(length=100), nullable=False),
45+
sa.Column('status', sa.String(), server_default=sa.text("'active'"), nullable=False),
46+
sa.Column('verified_at', sa.TIMESTAMP(timezone=True), nullable=True),
47+
sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
48+
sa.Column('updated_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
49+
sa.PrimaryKeyConstraint('user_id'),
50+
sa.UniqueConstraint('user_id')
51+
)
52+
op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True)
53+
op.create_table('codes',
54+
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
55+
sa.Column('user_id', sa.Integer(), nullable=False),
56+
sa.Column('code', sa.String(), nullable=False),
57+
sa.Column('expires_at', sa.TIMESTAMP(timezone=True), nullable=False),
58+
sa.ForeignKeyConstraint(['user_id'], ['users.user_id'], ondelete='CASCADE'),
59+
sa.PrimaryKeyConstraint('id'),
60+
sa.UniqueConstraint('id')
61+
)
62+
op.create_index(op.f('ix_codes_code'), 'codes', ['code'], unique=False)
63+
op.create_index(op.f('ix_codes_user_id'), 'codes', ['user_id'], unique=False)
64+
op.create_table('user_roles',
65+
sa.Column('user_id', sa.Integer(), nullable=True),
66+
sa.Column('role_id', sa.Integer(), nullable=True),
67+
sa.ForeignKeyConstraint(['role_id'], ['roles.role_id'], ondelete='CASCADE'),
68+
sa.ForeignKeyConstraint(['user_id'], ['users.user_id'], ondelete='CASCADE')
69+
)
70+
op.create_table('user_tokens',
71+
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
72+
sa.Column('user_id', sa.Integer(), nullable=False),
73+
sa.Column('access_key', sa.String(length=250), nullable=False),
74+
sa.Column('refresh_key', sa.String(length=250), nullable=False),
75+
sa.Column('device_id', sa.String(), nullable=False),
76+
sa.Column('device_name', sa.String(), server_default=sa.text("'NONAME'"), nullable=False),
77+
sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
78+
sa.Column('last_used_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
79+
sa.Column('expires_at', sa.TIMESTAMP(timezone=True), nullable=False),
80+
sa.ForeignKeyConstraint(['user_id'], ['users.user_id'], ondelete='CASCADE'),
81+
sa.PrimaryKeyConstraint('id')
82+
)
83+
op.create_index(op.f('ix_user_tokens_access_key'), 'user_tokens', ['access_key'], unique=False)
84+
op.create_index(op.f('ix_user_tokens_device_id'), 'user_tokens', ['device_id'], unique=False)
85+
op.create_index(op.f('ix_user_tokens_refresh_key'), 'user_tokens', ['refresh_key'], unique=False)
86+
op.create_index(op.f('ix_user_tokens_user_id'), 'user_tokens', ['user_id'], unique=False)
87+
op.create_table('verification_codes',
88+
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
89+
sa.Column('user_id', sa.Integer(), nullable=False),
90+
sa.Column('code', sa.String(), nullable=False),
91+
sa.Column('expires_at', sa.TIMESTAMP(timezone=True), nullable=False),
92+
sa.ForeignKeyConstraint(['user_id'], ['users.user_id'], ondelete='CASCADE'),
93+
sa.PrimaryKeyConstraint('id'),
94+
sa.UniqueConstraint('id')
95+
)
96+
op.create_index(op.f('ix_verification_codes_code'), 'verification_codes', ['code'], unique=False)
97+
op.create_index(op.f('ix_verification_codes_user_id'), 'verification_codes', ['user_id'], unique=False)
98+
op.create_table('verification_codes_opt',
99+
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
100+
sa.Column('user_id', sa.Integer(), nullable=False),
101+
sa.Column('code', sa.String(), nullable=False),
102+
sa.Column('expires_at', sa.TIMESTAMP(timezone=True), nullable=False),
103+
sa.ForeignKeyConstraint(['user_id'], ['registration_requests.user_id'], ondelete='CASCADE'),
104+
sa.PrimaryKeyConstraint('id'),
105+
sa.UniqueConstraint('id')
106+
)
107+
op.create_index(op.f('ix_verification_codes_opt_code'), 'verification_codes_opt', ['code'], unique=False)
108+
op.create_index(op.f('ix_verification_codes_opt_user_id'), 'verification_codes_opt', ['user_id'], unique=False)
109+
# ### end Alembic commands ###
110+
111+
112+
def downgrade() -> None:
113+
# ### commands auto generated by Alembic - please adjust! ###
114+
op.drop_index(op.f('ix_verification_codes_opt_user_id'), table_name='verification_codes_opt')
115+
op.drop_index(op.f('ix_verification_codes_opt_code'), table_name='verification_codes_opt')
116+
op.drop_table('verification_codes_opt')
117+
op.drop_index(op.f('ix_verification_codes_user_id'), table_name='verification_codes')
118+
op.drop_index(op.f('ix_verification_codes_code'), table_name='verification_codes')
119+
op.drop_table('verification_codes')
120+
op.drop_index(op.f('ix_user_tokens_user_id'), table_name='user_tokens')
121+
op.drop_index(op.f('ix_user_tokens_refresh_key'), table_name='user_tokens')
122+
op.drop_index(op.f('ix_user_tokens_device_id'), table_name='user_tokens')
123+
op.drop_index(op.f('ix_user_tokens_access_key'), table_name='user_tokens')
124+
op.drop_table('user_tokens')
125+
op.drop_table('user_roles')
126+
op.drop_index(op.f('ix_codes_user_id'), table_name='codes')
127+
op.drop_index(op.f('ix_codes_code'), table_name='codes')
128+
op.drop_table('codes')
129+
op.drop_index(op.f('ix_users_email'), table_name='users')
130+
op.drop_table('users')
131+
op.drop_table('roles')
132+
op.drop_index(op.f('ix_registration_requests_email'), table_name='registration_requests')
133+
op.drop_table('registration_requests')
134+
# ### end Alembic commands ###

app/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
from fastapi import FastAPI
22
from app.routes import user, security
33
from app.jobs.scheduler import start_scheduler, shutdown_scheduler
4+
45
from app.config.role import initialize_app_roles
56
initialize_app_roles()
67

78

89

9-
1010
def create_application():
1111
application = FastAPI()
1212

env/.env.mail

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
MAIL_USERNAME=REPLACE_THIS_WITH_YOUR_EMAIL_ADDRESS_@GMAIL.COM
2-
MAIL_PASSWORD=REPLACE_THIS_WITH_YOUR_EMAIL_PASSWORD
3-
MAIL_PORT=EMAIL_PORT
4-
MAIL_SERVER=YOUR_EMAIL_SERVER
1+
# Warning: PLEASE USE THESE CREDENTIALS FOR DEVELOPMENT PURPOSES ONLY
2+
MAIL_USERNAME=open.source.user.authentication@gmail.com
3+
MAIL_PASSWORD=avvx yapu kbko nbzg
4+
MAIL_PORT=587
5+
MAIL_SERVER=smtp.gmail.com
56
MAIL_STARTTLS=True
67
MAIL_SSL_TLS=False
78
MAIL_DEBUG=True
8-
MAIL_FROM=REPLACE_THIS_WITH_YOUR_EMAIL_ADDRESS_@GMAIL.COM
9-
MAIL_FROM_NAME=APP_NAME
9+
MAIL_FROM=open.source.user.authentication@gmail.com
10+
MAIL_FROM_NAME=fastapi-user-authentication
1011
USE_CREDENTIALS=True

env/.env.settings

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
# Warning: Do not commit this file to the repository
12
DATABASE_HOSTNAME=localhost
23
DATABASE_PORT=5432
34
DATABASE_USERNAME=postgres
4-
DATABASE_PASSWORD=YOUR_DATABASE_PASSWORD
5-
DATABASE_NAME=YOUR_DATABASE_NAME
5+
DATABASE_NAME=fastapi_user_authentication
6+
DATABASE_PASSWORD=user
67

78
# JWT Secret Key
89
JWT_SECRET=355fa9f6f9c491417c53b701b26dd97df5825d4abd02957ce3bb1b9658593d9a

requirements.txt

164 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)