First commit

master
Stefan Krulj 7 years ago
commit ac16c46e01
  1. 1
      .gitignore
  2. 0
      README.md
  3. 27
      model/models.py
  4. 109
      nonce.py
  5. 19
      simple_auth_service.py
  6. 179
      templates/login-page.html

1
.gitignore vendored

@ -0,0 +1 @@
__pycache__

@ -0,0 +1,27 @@
from datetime import datetime,timedelta
from uuid import uuid1
import secrets
class Nounce(object):
def __init__(self):
self.id = uuid1()
self.expiration = datetime.now()+timedelta(seconds=60)
self.token = secrets.token_urlsafe()
def expired(self):
return datetime.now() > self.expiration
def bump(self):
self.expiration = datetime.now()+timedelta(seconds=60)
class AuthSession(object):
def __init__(self, username):
self.id = uuid1()
self.expiration = datetime.now()+timedelta(minutes=30)
self.username = username
def expired(self):
return datetime.now() > self.expiration
def bumpExpiration(self):
self.expiration = datetime.now()+timedelta(minutes=30)

@ -0,0 +1,109 @@
from flask import *
import secrets, hashlib
from model.models import *
import json
app = Blueprint('nounce', __name__)
SID = 'SID'
nonces = {}
authSessions = {}
users = {}
def loadUsers(path):
global users
with open(path) as json_file:
users = json.load(json_file)
@app.route('/nonce', methods=['GET'])
def nounce():
n = tryGetSessionNonce()
if n == None or n.expired():
n = generateSessionNonce()
n.bump()
return jsonify(n.__dict__),201
@app.route('/login', methods=['POST'])
def login():
if isLoggedIn():
return 'already logged in', 400
n = tryGetSessionNonce()
if n == None:
return 'no nonce was generated', 400
if n.expired():
clearSessionNonce()
return 'nonce expired', 408
user = request.form['user']
cnonce = request.form['cnonce']
pwdhash = request.form['pwdhash']
pwdHashed_sha256 = ''
if user in users:
pwdHashed_sha256 = users[user]
else:
abort(403)
hash = generateHash(pwdHashed_sha256, cnonce, n.token)
if hash == pwdhash:
s = generateAuthSession(user)
return jsonify(s.__dict__),201
else:
abort(403)
@app.route('/check-login', methods=['GET'])
def checkLogin():
if isLoggedIn():
asession = authSessions[session[SID]]
asession.bumpExpiration()
return jsonify(asession.__dict__)
else:
return abort(403)
@app.route('/login-page', methods=['GET'])
def loginPage():
return render_template('login-page.html')
def isLoggedIn():
if SID in session and session[SID] in authSessions:
return not(authSessions[session[SID]].expired())
return False
def generateAuthSession(username):
asession = AuthSession(username)
authSessions[session[SID]] = asession
return asession
def getAuthSession():
if not SID in session:
return None
if not session[SID]:
return None
return authSessions[session[SID]]
def clearSessionNonce():
if not SID in session:
return
if not session[SID] in nonces:
return
del nonces[session[SID]]
def tryGetSessionNonce():
if not SID in session:
return None
if not session[SID] in nonces:
return None
return nonces[session[SID]]
def generateSessionNonce():
n = Nounce()
session[SID] = secrets.token_urlsafe()
nonces[session[SID]] = n
return n
def generateHash(pwdsha256,cnonce,nonce):
all = pwdsha256+cnonce+nonce
sha256 = hashlib.sha256( )
sha256.update( all.encode('utf8') )
return sha256.hexdigest()

@ -0,0 +1,19 @@
#!/usr/bin/env python3
from flask import *
import secrets
import nonce
import os.path
app = Flask(__name__)
app.secret_key = secrets.token_urlsafe()
app.register_blueprint(nonce.app)
ROUTES = {'routes': ['/', '/nonce'] }
@app.route('/')
def home():
return jsonify(ROUTES)
nonce.loadUsers( os.path.join('data','userlist.json') )
if __name__ == '__main__':
app.run( host='0.0.0.0', port=7070)

@ -0,0 +1,179 @@
<!doctype html>
<html>
<head>
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsSHA/2.3.1/sha256.js"
integrity="sha384-rxrcz1OyCVG21aEKlNBwgap2/r4pSV/RrN6eEMQhLriiTZnnzhCFZr+ZKGPJNSJX"
crossorigin="anonymous"></script>
<script>
function generateCnonce() {
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var text = "";
for(var i = 0; i < 43; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
function submit() {
var user = $("#user").val();
var pwd = $("#pwd").val();
jQuery.get( 'nonce', {}, function(data){
var nonce = data.token;
var cnonce = generateCnonce();
var sha256 = new jsSHA('SHA-256', 'TEXT'); //B64
sha256.update(pwd);
var pwdHashed_sha256 = sha256.getHash('HEX')
sha256 = new jsSHA('SHA-256', 'TEXT');
sha256.update( pwdHashed_sha256+cnonce+nonce );
const pwdhash= sha256.getHash('HEX')
const payload = {
user: user,
cnonce: cnonce,
pwdhash: pwdhash
}
jQuery.post('login', payload,
function(data){
location.href = '/';
}
).fail(function() {
var el = $('.error_msg');
el.css('animation-name','');
void el[0].offsetWidth; //trigger reflow
el.css('animation-name','fade');
});
} );
}
$( document ).ready(function() {
$("body").keypress( function(e) {
if(e.key=='Enter') {
submit();
}
});
$("#login").click( submit );
});
</script>
<style>
input {
border: 1px solid lightgray;
border-radius: 5px;
padding: 5px 5px 5px 5px;
/*width: 150px;*/
width:100%;
display: block;
margin: 1px auto 3px auto;
box-sizing: border-box;
}
button {
border: none;
border-radius: 5px;
padding: 5px 5px 5px 5px;
/*width: 150px;*/
width:100%;
display: block;
margin: 1px auto 3px auto;
box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.75);
cursor: pointer;
}
button:hover {
background-color: lightgray;
}
button:active {
transform: translateY(1px);
box-shadow: 0px 1px 2px 0px rgba(0,0,0,0.75);
}
button:focus {
outline: 0;
}
.error_msg {
border: none;
border-radius: 5px;
display: block;
background-color: #CC3333;
color: white;
animation-duration: 2.5s;
opacity: 0;
padding: 5px;
width:100%;
box-sizing: border-box;
font: 400 13.3333px Arial;
font-weight: 600;
text-align: center;
margin: 1px auto 3px auto;
}
@keyframes fade {
0% {opacity: 1;}
25% {opacity: 1;}
100% {opacity: 0;}
}
.centered {
width: 180px;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
body {
background:
linear-gradient(27deg,
#151515 5px,
transparent 5px
) 0 5px,
linear-gradient(207deg,
#151515 5px,
transparent 5px
) 10px 0px,
linear-gradient(27deg,
#222 5px,
transparent 5px
) 0px 10px,
linear-gradient(207deg,
#222 5px,
transparent 5px
) 10px 5px,
linear-gradient(90deg,
#1b1b1b 10px,
transparent 10px
),
linear-gradient(
#1d1d1d 25%,
#1a1a1a 25%,
#1a1a1a 50%,
transparent 50%,
transparent 75%,
#242424 75%,
#242424
);
background-color: #131313;
background-size: 20px 20px;
background-clip: content-box;
}
</style>
</head>
<body>
<div class="centered">
<input id="user" type="text" />
<input id="pwd" type="password" />
<button id="login">Login</button>
<div class="error_msg">Login failed!</div>
</div>
</body>
</html>
Loading…
Cancel
Save