Beta migration

This commit is contained in:
Tracker-Friendly 2024-05-15 19:45:36 +01:00
parent 862f7f2296
commit f388ff86a5
5 changed files with 431 additions and 13 deletions

80
main
View File

@ -109,6 +109,14 @@ def check_username_taken(username):
return "error" return "error"
return post["id"] return post["id"]
def check_sub_taken(sub):
conn = get_db_connection()
post = conn.execute("SELECT * FROM users WHERE password = ?",
(str("OAUTH-" + sub),)).fetchone()
conn.close()
if post is None:
return "error"
return post["id"]
def get_session(id): def get_session(id):
conn = get_db_connection() conn = get_db_connection()
@ -387,22 +395,54 @@ async def apilogin():
"error": "https://http.cat/images/400.jpg" "error": "https://http.cat/images/400.jpg"
}, 400 }, 400
@app.route("/api/migrate", methods=("GET", "POST"))
async def migrate():
usersession = request.cookies.get("session_DO_NOT_SHARE")
if request.method == "POST":
data = await request.get_json()
sub = data["sub"]
password = data["access_token"]
userCookie = get_session(usersession)
user = get_user(userCookie["id"])
if user == "error":
return { "error": "User doesn't exist" }, 403
conn = get_db_connection()
subdata = '{"access_token":"' + password + '"}'
response = requests.post("https://auth.hectabit.org/api/uniqueid", subdata)
if response.status_code == 200:
data = response.json()
conn.execute("UPDATE users SET password = ? WHERE id = ?",
(str("OAUTH-" + data['uniqueid']), user["id"]))
else:
return {"error": response.json()["error"]}, response.status_code
conn.commit()
conn.close()
return {"success": "true"}, 200
@app.route("/api/oauth", methods=("GET", "POST")) @app.route("/api/oauth", methods=("GET", "POST"))
async def apioauth(): async def apioauth():
if request.method == "POST": if request.method == "POST":
data = await request.get_json() data = await request.get_json()
username = data["username"] username = data["username"]
sub = data["sub"]
password = data["access_token"] password = data["access_token"]
conn = get_db_connection() conn = get_db_connection()
subdata = '{"access_token":"' + password + '"}' subdata = '{"access_token":"' + password + '"}'
response = requests.post("https://auth.hectabit.org/api/loggedin", subdata) response = requests.post("https://auth.hectabit.org/api/loggedin", subdata)
if response.status_code == 200: if response.status_code == 200:
userID = check_sub_taken(sub)
user = get_user(userID)
if user == "error":
userID = check_username_taken(username) userID = check_username_taken(username)
user = get_user(userID) user = get_user(userID)
if user == "error": if user == "error":
conn.execute("INSERT INTO users (username, password, created, htmldescription) VALUES (?, ?, ?, ?)", conn.execute("INSERT INTO users (username, password, created, htmldescription) VALUES (?, ?, ?, ?)",
(username, "OAUTH2", str(time.time()), "")) (username, str("OAUTH-" + sub), str(time.time()), ""))
else:
if user["password"] != "OAUTH-" + sub:
return {"error": "Migration required or username taken"}, 422
else: else:
return {"error": response.json()["error"]}, response.status_code return {"error": response.json()["error"]}, response.status_code
@ -553,6 +593,38 @@ async def cdn(filename):
else: else:
return "file doesn't exist!!" return "file doesn't exist!!"
@app.route("/legacysignup", methods=("GET", "POST"))
async def legacysignup():
usersession = request.cookies.get("session_DO_NOT_SHARE")
if usersession:
return redirect(url_for("main"))
if request.method == "POST":
requestData = await request.form
if not check_username_taken(requestData["username"]) == "error":
await flash("Username already taken :3")
return redirect(url_for("signup"))
if not requestData["username"].isalnum():
await flash("Username must be alphanumeric :3")
return redirect(url_for("signup"))
if not len(requestData["password"]) > int(PASSWORD_REQUIREMENT):
await flash("Password must contain at least " + PASSWORD_REQUIREMENT + " characters")
return redirect(url_for("signup"))
hashedpassword = generate_password_hash(requestData["password"])
conn = get_db_connection()
conn.execute("INSERT INTO users (username, password, created, htmldescription) VALUES (?, ?, ?, ?)",
(requestData["username"], hashedpassword, str(time.time()), ""))
conn.commit()
conn.close()
return redirect(url_for("login"))
else:
return await render_template("signup.html")
@app.route("/signup", methods=("GET", "POST")) @app.route("/signup", methods=("GET", "POST"))
async def signup(): async def signup():
usersession = request.cookies.get("session_DO_NOT_SHARE") usersession = request.cookies.get("session_DO_NOT_SHARE")
@ -602,7 +674,11 @@ async def login():
@app.route("/oauth", methods=("GET", "POST")) @app.route("/oauth", methods=("GET", "POST"))
async def oauth(): async def oauth():
legacymigrate = request.cookies.get("legacy_migrate")
if legacymigrate != "1":
return await render_template("oauth.html") return await render_template("oauth.html")
else:
return await render_template("migrate.html")
@app.route("/settings", methods=("GET", "POST")) @app.route("/settings", methods=("GET", "POST"))
async def settings(): async def settings():
@ -673,7 +749,7 @@ async def logout():
resp = redirect(url_for("main")) resp = redirect(url_for("main"))
session = request.cookies.get("session_DO_NOT_SHARE") session = request.cookies.get("session_DO_NOT_SHARE")
resp.delete_cookie("session_DO_NOT_SHARE") resp.delete_cookie("session_DO_NOT_SHARE")
resp.delete_cookie("prefuser")
return resp return resp
@app.errorhandler(500) @app.errorhandler(500)

151
templates/migrate.html Normal file
View File

@ -0,0 +1,151 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="/static/css/style.css">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login to Burgercat</title>
</head>
<body>
<div class="accountform">
<br>
<a href="/">back</a><br><br>
<h1 id="text">Migrate to Burgerauth</h1>
<button onclick="authorize()">Link a Burgerauth Account</button>
</div>
<script>
function getCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
// Configuration
const clientId = 'jT89GqLCqobN3pb6Rgkk0klDyicUbdjY';
const redirectUri = 'https://cat.hectabit.org/oauth';
const authorizationEndpoint = 'https://auth.hectabit.org/login';
const tokenEndpoint = 'https://auth.hectabit.org/api/tokenauth';
const userinfoEndpoint = 'https://auth.hectabit.org/userinfo'; // Added userinfo endpoint
// Generate a random code verifier
function generateCodeVerifier() {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
const length = 128;
return Array.from(crypto.getRandomValues(new Uint8Array(length)))
.map((x) => charset[x % charset.length])
.join("");
}
// Create a code challenge from the code verifier using SHA-256
async function createCodeChallenge(codeVerifier) {
const buffer = new TextEncoder().encode(codeVerifier);
const hashArrayBuffer = await crypto.subtle.digest('SHA-256', buffer);
const hashBase64 = btoa(String.fromCharCode(...new Uint8Array(hashArrayBuffer)))
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
return hashBase64;
}
// Authorization function with PKCE
function authorize() {
const codeVerifier = generateCodeVerifier();
localStorage.setItem('codeVerifier', codeVerifier); // Store code verifier
createCodeChallenge(codeVerifier)
.then((codeChallenge) => {
const authorizationUrl = `${authorizationEndpoint}?response_type=code&client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&code_challenge=${codeChallenge}&code_challenge_method=S256`;
window.location.href = authorizationUrl;
})
.catch((error) => {
console.error('Error generating code challenge:', error);
});
}
// Parse the authorization code from the URL
function parseCodeFromUrl() {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('code');
}
// Exchange authorization code for access token
async function exchangeCodeForToken(code) {
const codeVerifier = localStorage.getItem('codeVerifier'); // Retrieve code verifier
const formData = new URLSearchParams();
formData.append('client_id', String(clientId));
formData.append('code', String(code));
formData.append('redirect_uri', String(redirectUri));
formData.append('grant_type', 'authorization_code');
formData.append('code_verifier', String(codeVerifier));
const response = await fetch(tokenEndpoint, {
method: 'POST',
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: formData
});
const data = await response.json();
const accessToken = data.access_token;
const idToken = data.id_token;
// Request userinfo with id_token in bearer format
fetch(userinfoEndpoint, {
headers: {
"Authorization": `Bearer ${idToken}`
}
})
.then((response) => {
async function doStuff() {
if (response.status == 200) {
const userinfoData = await response.json();
console.log("User:", userinfoData.name)
console.log("Sub:", userinfoData.sub);
document.getElementById("text").innerText = "Authenticating, " + userinfoData.name;
fetch("https://cat.hectabit.org/api/migrate", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
sub: userinfoData.sub,
access_token: data.access_token
})
})
.then((response) => {
async function doStuff2() {
if (response.status == 200) {
window.location.replace("/")
} else {
document.getElementById("text").innerText = "Failed: " + key["error"]
}
}
doStuff2()
});
} else {
document.getElementById("text").innerText = "Authentication failed"
}
}
doStuff()
});
}
// Main function to handle OAuth2 flow
async function main() {
const code = parseCodeFromUrl();
if (code) {
await exchangeCodeForToken(code);
}
}
// Call the main function on page load
main();
</script>
</body>
</html>

169
templates/migrate.html.save Normal file
View File

@ -0,0 +1,169 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="/static/css/style.css">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login to Burgercat</title>
</head>
<body>
<div class="accountform">
<br>
<a href="/">back</a><br><br>
<h1 id="text">Migration</h1>
<button onclick="authorize()">Migrate to Burgerauth Account</button>
</div>
<script>
function getCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
// Configuration
const clientId = 'jT89GqLCqobN3pb6Rgkk0klDyicUbdjY';
const redirectUri = 'https://cat.hectabit.org/oauth';
const authorizationEndpoint = 'https://auth.hectabit.org/login';
const tokenEndpoint = 'https://auth.hectabit.org/api/tokenauth';
const userinfoEndpoint = 'https://auth.hectabit.org/userinfo'; // Added userinfo endpoint
// Generate a random code verifier
function generateCodeVerifier() {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
const length = 128;
return Array.from(crypto.getRandomValues(new Uint8Array(length)))
.map((x) => charset[x % charset.length])
.join("");
}
// Create a code challenge from the code verifier using SHA-256
async function createCodeChallenge(codeVerifier) {
const buffer = new TextEncoder().encode(codeVerifier);
const hashArrayBuffer = await crypto.subtle.digest('SHA-256', buffer);
const hashBase64 = btoa(String.fromCharCode(...new Uint8Array(hashArrayBuffer)))
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
return hashBase64;
}
function cuser_authorize() {
document.cookie = "prefuser" + "=" + window.prompt("Choose your custom username (cannot be longer than 20 characters)") + "; expires=Session" + "; path=/" + "; samesite=Strict";
authorize()
}
// Authorization function with PKCE
function authorize() {
const codeVerifier = generateCodeVerifier();
localStorage.setItem('codeVerifier', codeVerifier); // Store code verifier
createCodeChallenge(codeVerifier)
.then((codeChallenge) => {
const authorizationUrl = `${authorizationEndpoint}?response_type=code&client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&code_challenge=${codeChallenge}&code_challenge_method=S256`;
window.location.href = authorizationUrl;
})
.catch((error) => {
console.error('Error generating code challenge:', error);
});
}
// Parse the authorization code from the URL
function parseCodeFromUrl() {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('code');
}
// Exchange authorization code for access token
async function exchangeCodeForToken(code) {
const codeVerifier = localStorage.getItem('codeVerifier'); // Retrieve code verifier
const formData = new URLSearchParams();
formData.append('client_id', String(clientId));
formData.append('code', String(code));
formData.append('redirect_uri', String(redirectUri));
formData.append('grant_type', 'authorization_code');
formData.append('code_verifier', String(codeVerifier));
const response = await fetch(tokenEndpoint, {
method: 'POST',
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: formData
});
const data = await response.json();
const accessToken = data.access_token;
const idToken = data.id_token;
// Request userinfo with id_token in bearer format
fetch(userinfoEndpoint, {
headers: {
"Authorization": `Bearer ${idToken}`
}
})
.then((response) => {
async function doStuff() {
if (response.status == 200) {
const userinfoData = await response.json();
console.log("User:", userinfoData.name)
console.log("Sub:", userinfoData.sub);
let preferreduser = userinfoData.name
if (getCookie("prefuser") != "") {
preferreduser = getCookie("prefuser")
}
document.getElementById("text").innerText = "Authenticating, " + userinfoData.name;
fetch("https://cat.hectabit.org/api/migrate", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
username: preferreduser,
access_token: data.access_token
})
})
.then((response) => {
async function doStuff2() {
let key = await response.json()
if (response.status == 200) {
document.cookie = "session_DO_NOT_SHARE" + "=" + (key["key"] || "") + "; expires=Session" + "; path=/" + "; samesite=Strict";
window.location.replace("/")
} else if (response.status == 422) {
document.getElementById("text").innerText = "Username taken. Migrate or choose a new custom username!"
} else {
document.getElementById("text").innerText = "Failed: " + key["error"]
}
}
doStuff2()
});
localStorage.setItem("user", preferreduser)
} else {
document.getElementById("text").innerText = "Authentication failed"
}
}
doStuff()
});
}
// Main function to handle OAuth2 flow
async function main() {
if (localStorage.getItem("user") !== null) {
document.getElementById("text").innerText = "Welcome back, " + localStorage.getItem("user")
}
const code = parseCodeFromUrl();
if (code) {
await exchangeCodeForToken(code);
}
}
// Call the main function on page load
main();
</script>
</body>
</html>

View File

@ -13,10 +13,23 @@
<h1 id="text">Login to Burgercat</h1> <h1 id="text">Login to Burgercat</h1>
<button onclick="authorize()">Log in with Burgerauth Account</button> <button onclick="authorize()">Log in with Burgerauth Account</button>
<br><br> <br><br>
<button onclick="cuser_authorize()">Log in with Burgerauth Account (custom username)</button>
<br><br>
<button onclick="window.location.href = '/login'">Use legacy login</button> <button onclick="window.location.href = '/login'">Use legacy login</button>
</div> </div>
<script> <script>
function getCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
// Configuration // Configuration
const clientId = 'jT89GqLCqobN3pb6Rgkk0klDyicUbdjY'; const clientId = 'jT89GqLCqobN3pb6Rgkk0klDyicUbdjY';
const redirectUri = 'https://cat.hectabit.org/oauth'; const redirectUri = 'https://cat.hectabit.org/oauth';
@ -44,6 +57,12 @@
return hashBase64; return hashBase64;
} }
function cuser_authorize() {
document.cookie = "prefuser" + "=" + window.prompt("Choose your custom username (cannot be longer than 20 characters)") + "; expires=Session" + "; path=/" + "; samesite=Strict";
authorize()
}
// Authorization function with PKCE // Authorization function with PKCE
function authorize() { function authorize() {
const codeVerifier = generateCodeVerifier(); const codeVerifier = generateCodeVerifier();
@ -98,6 +117,10 @@
const userinfoData = await response.json(); const userinfoData = await response.json();
console.log("User:", userinfoData.name) console.log("User:", userinfoData.name)
console.log("Sub:", userinfoData.sub); console.log("Sub:", userinfoData.sub);
let preferreduser = userinfoData.name
if (getCookie("prefuser") != "") {
preferreduser = getCookie("prefuser")
}
document.getElementById("text").innerText = "Authenticating, " + userinfoData.name; document.getElementById("text").innerText = "Authenticating, " + userinfoData.name;
fetch("https://cat.hectabit.org/api/oauth", { fetch("https://cat.hectabit.org/api/oauth", {
method: "POST", method: "POST",
@ -105,8 +128,9 @@
"Content-Type": "application/json" "Content-Type": "application/json"
}, },
body: JSON.stringify({ body: JSON.stringify({
username: userinfoData.name, username: preferreduser,
access_token: data.access_token access_token: data.access_token,
sub: userinfoData.sub
}) })
}) })
.then((response) => { .then((response) => {
@ -115,14 +139,15 @@
if (response.status == 200) { if (response.status == 200) {
document.cookie = "session_DO_NOT_SHARE" + "=" + (key["key"] || "") + "; expires=Session" + "; path=/" + "; samesite=Strict"; document.cookie = "session_DO_NOT_SHARE" + "=" + (key["key"] || "") + "; expires=Session" + "; path=/" + "; samesite=Strict";
window.location.replace("/") window.location.replace("/")
} else if (response.status == 422) {
document.getElementById("text").innerText = "Username taken. Migrate or choose a new custom username!"
} else { } else {
document.getElementById("text").innerText = "Failed: " + key["error"] document.getElementById("text").innerText = "Failed: " + key["error"]
} }
} }
doStuff2() doStuff2()
}); });
localStorage.setItem("user", userinfoData.name) localStorage.setItem("user", preferreduser)
localStorage.setItem("sub", userinfoData.sub)
} else { } else {
document.getElementById("text").innerText = "Authentication failed" document.getElementById("text").innerText = "Authentication failed"
} }

View File

@ -10,9 +10,6 @@
</head> </head>
<body> <body>
<script>
window.location.replace("/oauth")
</script>
<form class="accountform" method="post"> <form class="accountform" method="post">
<br> <br>
<a href="/">back</a> <a href="/">back</a>