Skip to content

Commit c2514d0

Browse files
Merge branch 'main' into feature/existing-users
2 parents 1907de1 + 2b9e21b commit c2514d0

File tree

14 files changed

+878
-245
lines changed

14 files changed

+878
-245
lines changed

LICENSE

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
Elastic License 2.0
2+
3+
URL: https://www.elastic.co/licensing/elastic-license
4+
5+
## Acceptance
6+
7+
By using the software, you agree to all of the terms and conditions below.
8+
9+
## Copyright License
10+
11+
The licensor grants you a non-exclusive, royalty-free, worldwide,
12+
non-sublicensable, non-transferable license to use, copy, distribute, make
13+
available, and prepare derivative works of the software, in each case subject to
14+
the limitations and conditions below.
15+
16+
## Limitations
17+
18+
You may not provide the software to third parties as a hosted or managed
19+
service, where the service provides users with access to any substantial set of
20+
the features or functionality of the software.
21+
22+
You may not move, change, disable, or circumvent the license key functionality
23+
in the software, and you may not remove or obscure any functionality in the
24+
software that is protected by the license key.
25+
26+
You may not alter, remove, or obscure any licensing, copyright, or other notices
27+
of the licensor in the software. Any use of the licensor’s trademarks is subject
28+
to applicable law.
29+
30+
## Patents
31+
32+
The licensor grants you a license, under any patent claims the licensor can
33+
license, or becomes able to license, to make, have made, use, sell, offer for
34+
sale, import and have imported the software, in each case subject to the
35+
limitations and conditions in this license. This license does not cover any
36+
patent claims that you cause to be infringed by modifications or additions to
37+
the software. If you or your company make any written claim that the software
38+
infringes or contributes to infringement of any patent, your patent license for
39+
the software granted under these terms ends immediately. If your company makes
40+
such a claim, your patent license ends immediately for work on behalf of your
41+
company.
42+
43+
## Notices
44+
45+
You must ensure that anyone who gets a copy of any part of the software from you
46+
also gets a copy of these terms.
47+
48+
If you modify the software, you must include in any modified copies of the
49+
software prominent notices stating that you have modified the software.
50+
51+
## No Other Rights
52+
53+
These terms do not imply any licenses other than those expressly granted in
54+
these terms.
55+
56+
## Termination
57+
58+
If you use the software in violation of these terms, such use is not licensed,
59+
and your licenses will automatically terminate. If the licensor provides you
60+
with a notice of your violation, and you cease all violation of this license no
61+
later than 30 days after you receive that notice, your licenses will be
62+
reinstated retroactively. However, if you violate these terms after such
63+
reinstatement, any additional violation of these terms will cause your licenses
64+
to terminate automatically and permanently.
65+
66+
## No Liability
67+
68+
*As far as the law allows, the software comes as is, without any warranty or
69+
condition, and the licensor will not be liable to you for any damages arising
70+
out of these terms or the use or nature of the software, under any kind of
71+
legal claim.*
72+
73+
## Definitions
74+
75+
The **licensor** is the entity offering these terms, and the **software** is the
76+
software the licensor makes available under these terms, including any portion
77+
of it.
78+
79+
**you** refers to the individual or entity agreeing to these terms.
80+
81+
**your company** is any legal entity, sole proprietorship, or other kind of
82+
organization that you work for, plus all organizations that have control over,
83+
are under the control of, or are under common control with that
84+
organization. **control** means ownership of substantially all the assets of an
85+
entity, or the power to direct its management and policies by vote, contract, or
86+
otherwise. Control can be direct or indirect.
87+
88+
**your licenses** are all the licenses granted to you for the software under
89+
these terms.
90+
91+
**use** means anything you do with the software requiring one of your licenses.
92+
93+
**trademark** means trademarks, service marks, and similar rights.

app/main.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from utils.config import load_config, load_initial_todos, load_initial_users
99
from utils.auth import setup_auth_config
1010
from services.auth_service import init_auth_service, add_user
11+
from flasgger import Swagger
1112
import secrets
1213

1314
def create_app(auth_config):
@@ -32,7 +33,26 @@ def create_app(auth_config):
3233
app.config['SECRET_KEY'] = secrets.token_hex(32) # Generate secure random secret key
3334
app.config['auth_config'] = auth_config
3435
app.config['initial_todos'] = load_initial_todos() # Load initial todos from config file
35-
36+
37+
# Configure Swagger
38+
template = {
39+
"swagger": "2.0",
40+
"info": {
41+
"title": "Todo API",
42+
"description": "A RESTful API for managing todos and notes",
43+
"version": "1.0.0"
44+
}
45+
}
46+
47+
app.config['SWAGGER'] = {
48+
'title': 'Todo API',
49+
'uiversion': 3,
50+
'specs_route': '/',
51+
'url_prefix': '/swagger'
52+
}
53+
54+
Swagger(app, template=template)
55+
3656
# Set up authentication
3757
init_auth_routes(auth_config)
3858
auth_middleware = AuthMiddleware(auth_config)

app/routes/auth.py

Lines changed: 67 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
1-
from flask import Blueprint, request, jsonify, session
2-
import jwt
3-
import datetime
1+
from flask import Blueprint, request, jsonify
42
from config.auth_config import AuthMethod, AuthConfig
5-
from services.auth_service import generate_jwt_token, is_username_taken, add_user, signup_user, login_user, logout_user, blacklist_token, validate_refresh_token, refresh_tokens
3+
from services.auth_service import (
4+
signup_user,
5+
validate_refresh_token,
6+
generate_jwt_token,
7+
refresh_tokens,
8+
login_jwt,
9+
login_session,
10+
logout_jwt,
11+
logout_session
12+
)
613

7-
auth_bp = Blueprint("auth", __name__)
8-
auth_config = None
14+
# --- Configuration ---
15+
auth_bp = Blueprint("auth", __name__) # Flask blueprint for auth routes
16+
auth_config = None # Global configuration object set during initialization
917

1018
def init_auth_routes(config: AuthConfig):
1119
global auth_config
1220
auth_config = config
1321

22+
# --- Authentication Routes ---
1423
@auth_bp.route("/signup", methods=["POST"])
1524
def signup():
25+
"""
26+
Register a new user
27+
Expects JSON: {"username": "user", "password": "pass"}
28+
"""
1629
if auth_config.auth_method == AuthMethod.API_KEY:
1730
return jsonify({"error": "Signup not available with API key authentication"}), 400
1831

@@ -21,14 +34,60 @@ def signup():
2134

2235
@auth_bp.route("/login", methods=["POST"])
2336
def login():
37+
"""
38+
Authenticate user and return tokens (JWT) or create session
39+
Expects JSON: {"username": "user", "password": "pass"}
40+
"""
2441
if auth_config.auth_method == AuthMethod.API_KEY:
2542
return jsonify({"error": "Login not available with API key authentication"}), 400
2643

2744
data = request.get_json()
28-
return login_user(data)
45+
if not data or "username" not in data or "password" not in data:
46+
return jsonify({"error": "Username and password are required"}), 400
47+
48+
username = data["username"]
49+
password = data["password"]
50+
51+
if auth_config.auth_method == AuthMethod.JWT:
52+
return login_jwt(username, password)
53+
54+
return login_session(username, password)
55+
56+
@auth_bp.route("/logout", methods=["POST"])
57+
def logout():
58+
"""
59+
End user session or invalidate JWT tokens
60+
For JWT: Requires Authorization header with Bearer token and refresh_token in JSON body
61+
For Session: No additional requirements
62+
"""
63+
if auth_config.auth_method == AuthMethod.API_KEY:
64+
return jsonify({"error": "Logout not available with API key authentication"}), 400
65+
66+
if auth_config.auth_method == AuthMethod.JWT:
67+
auth_header = request.headers.get('Authorization')
68+
if not auth_header or not auth_header.startswith('Bearer '):
69+
return jsonify({"error": "Access token is required in Authorization header"}), 401
70+
access_token = auth_header.split(' ')[1]
71+
72+
if not request.is_json:
73+
return jsonify({"error": "Request must be JSON"}), 415
74+
75+
data = request.get_json()
76+
refresh_token = data.get("refresh_token")
77+
if not refresh_token:
78+
return jsonify({"error": "Refresh token is required in request body"}), 400
79+
80+
return logout_jwt(access_token, refresh_token)
81+
82+
return logout_session()
2983

3084
@auth_bp.route("/refresh", methods=["POST"])
3185
def refresh_token():
86+
"""
87+
Get new access token using refresh token
88+
Expects JSON: {"refresh_token": "token"}
89+
Returns: New access token and refresh token pair
90+
"""
3291
data = request.get_json()
3392
refresh_token = data.get("refresh_token")
3493
username = validate_refresh_token(refresh_token)
@@ -37,36 +96,10 @@ def refresh_token():
3796

3897
access_token, new_refresh_token = generate_jwt_token(username)
3998

40-
# Remove old refresh token and store new one
4199
if refresh_token in refresh_tokens:
42100
del refresh_tokens[refresh_token]
43101

44102
return jsonify({
45103
"access_token": access_token,
46104
"refresh_token": new_refresh_token
47-
}), 200
48-
49-
@auth_bp.route("/logout", methods=["POST"])
50-
def logout():
51-
if auth_config.auth_method == AuthMethod.API_KEY:
52-
return jsonify({"error": "Logout not available with API key authentication"}), 400
53-
54-
elif auth_config.auth_method == AuthMethod.JWT:
55-
auth_header = request.headers.get('Authorization')
56-
if auth_header and auth_header.startswith('Bearer '):
57-
token = auth_header.split(' ')[1]
58-
blacklist_token(token)
59-
60-
# Invalidate refresh token
61-
data = request.get_json()
62-
refresh_token = data.get("refresh_token")
63-
if refresh_token in refresh_tokens:
64-
del refresh_tokens[refresh_token]
65-
66-
return jsonify({"message": "Logout successful"})
67-
68-
elif auth_config.auth_method == AuthMethod.SESSION:
69-
session.clear()
70-
return jsonify({"message": "Logout successful"})
71-
72-
return jsonify({"error": "Invalid authentication method"}), 500
105+
}), 200

0 commit comments

Comments
 (0)