Skip to content

Commit 879cda0

Browse files
committed
Initial commit of user registration and authentication backend.
1 parent a3c0298 commit 879cda0

File tree

5 files changed

+121
-3
lines changed

5 files changed

+121
-3
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.pyc
2+
__pycache__/

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
# op-api
2-
octo-potato: API
1+
# ce-backend
2+
CodeEcho: Backend
33

44
## License
5-
[GNU GPL v3.0](https://github.com/octo-potato/op-api/blob/master/LICENSE)
5+
[GNU GPL v3.0](https://github.com/octo-potato/ce-api/blob/master/LICENSE)

app.py

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
from sanic import Sanic
2+
from sanic import response as res
3+
from sanic import exceptions as exc
4+
import argon2
5+
import random, string, json
6+
7+
app = Sanic('codeecho')
8+
9+
ph = argon2.PasswordHasher()
10+
11+
config = {}
12+
with open("config.json") as data:
13+
config = json.loads(data.read())
14+
data.close()
15+
16+
@app.listener('before_server_start')
17+
def init(sanic, loop):
18+
"""Initialize database before server starts"""
19+
global db
20+
from motor.motor_asyncio import AsyncIOMotorClient
21+
db = AsyncIOMotorClient(
22+
host=config.get('mongo_host', '0.0.0.0'),
23+
port=config.get('mongo_port', 27017)
24+
)[config.get('mongo_db_name', 'codeecho')]
25+
26+
@app.route('/api/auth', methods=['POST'])
27+
async def auth_handler(request):
28+
"""Handles authentication requests"""
29+
req = request.json
30+
if not req: raise exc.InvalidUsage("Bad request")
31+
32+
# Ensure required data is included in the request
33+
username = req.get('username')
34+
password = req.get('password')
35+
if not (username and password): raise exc.InvalidUsage("Bad request")
36+
37+
# Ensure user exists in database
38+
user = await db['users'].find_one({ "username": username })
39+
if not user: raise exc.Forbidden("Invalid credentials")
40+
41+
# Ensure password is correct
42+
try:
43+
ph.verify(user['password'], password)
44+
except argon2.exceptions.VerifyMismatchError:
45+
raise exc.Forbidden("Invalid credentials")
46+
except argon2.exceptions.VerificationError:
47+
raise exc.ServerError("Password verification failed")
48+
49+
return res.json({
50+
"id": str(user['_id']),
51+
"username": user['username'],
52+
"email": user['email'],
53+
"token": user['token']
54+
})
55+
56+
@app.route('/api/user', methods=['POST'])
57+
async def new_user_handler(request):
58+
"""Handles requests for new users"""
59+
req = request.json
60+
if not req: raise exc.InvalidUsage("Bad request")
61+
62+
# Ensure required data is included in the request
63+
username = req.get('username')
64+
email = req.get('email')
65+
password = req.get('password')
66+
if not (username and email and password): raise exc.InvalidUsage("Bad request")
67+
68+
# Ensure user does not already exist in database
69+
user = await db['users'].find_one({ "username": username })
70+
if user is not None: return res.json({ "message": "A user with this username already exists", "status": 409 })
71+
user = await db['users'].find_one({ "email": email })
72+
if user is not None: return res.json({ "message": "A user with thsi email already exists", "status": 409 })
73+
74+
# Hash password
75+
hashed_pass = ph.hash(password)
76+
77+
# Generate new token
78+
token = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for i in range(25))
79+
80+
# Insert user into database
81+
user = await db['users'].insert_one({
82+
"username": username,
83+
"email": email,
84+
"password": hashed_pass,
85+
"token": token
86+
})
87+
88+
# Send response
89+
return res.json({
90+
"id": str(user.inserted_id),
91+
"username": username,
92+
"email": email,
93+
"token": token
94+
})
95+
96+
@app.exception(exc.SanicException)
97+
def errors(request, exception):
98+
"""Handles errors"""
99+
return res.json({ "error": exception.args[0], "status": exception.status_code })
100+
101+
if __name__ == "__main__":
102+
app.run(
103+
host=config.get('app_host', '0.0.0.0'),
104+
port=config.get('app_port', 80),
105+
debug=True
106+
)

config.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"app_host": "localhost",
3+
"app_port": 8080,
4+
"mongo_host": "localhost",
5+
"mongo_port": 27017,
6+
"mongo_db_name": "codeecho"
7+
}

requirements.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
sanic
2+
motor
3+
argon2_cffi

0 commit comments

Comments
 (0)