Skip to content

Commit 5a995c2

Browse files
authored
Fix webform for new auth method (#145)
* Fix webform for new auth method * Remove old instructions * Release 0.10.1
1 parent 882066d commit 5a995c2

8 files changed

+170
-92
lines changed

README.md

-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@ General usage documentation.
125125
126126
``` bash
127127
# Get started by adding one or more users
128-
# See `Getting a reCAPTCHA code` below for more help
129128
kobodl user add
130129

131130
# List users

kobodl/actions.py

+20-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,6 @@ def ListBooks(users: List[User], listAll: bool, exportFile: Union[TextIO, None])
160160
Owner=user,
161161
)
162162

163-
164163
def Login(user: User) -> None:
165164
'''perform device initialization and get token'''
166165
kobo = Kobo(user)
@@ -169,6 +168,26 @@ def Login(user: User) -> None:
169168
kobo.Login()
170169

171170

171+
def InitiateLogin(user: User) -> Tuple[str, str]:
172+
"""Start the login process and return activation details"""
173+
kobo = Kobo(user)
174+
return kobo._Kobo__ActivateOnWeb()
175+
176+
177+
def CheckActivation(user: User, check_url: str) -> bool:
178+
"""Check if activation is complete and setup user if so"""
179+
kobo = Kobo(user)
180+
try:
181+
email, user_id, user_key = kobo._Kobo__CheckActivation(check_url)
182+
user.Email = email
183+
user.UserId = user_id
184+
kobo.AuthenticateDevice(user_key)
185+
kobo.LoadInitializationSettings()
186+
return True
187+
except Exception:
188+
return False
189+
190+
172191
def GetBookOrBooks(
173192
user: User,
174193
outputPath: str,

kobodl/app.py

+30-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import json.decoder
22
import os
33

4-
from flask import Flask, abort, redirect, render_template, request, send_from_directory
4+
from flask import Flask, abort, jsonify, redirect, render_template, request, send_from_directory
55
from requests import Request, Session
66
from requests.auth import HTTPBasicAuth
77
from requests.exceptions import HTTPError
@@ -22,25 +22,45 @@ def index():
2222
def users():
2323
error = None
2424
if request.method == 'POST':
25-
print(request.form)
2625
email = request.form.get('email')
27-
password = request.form.get('password')
28-
captcha = request.form.get('captcha')
29-
print(email, password, captcha)
30-
if email and password and captcha:
26+
if email:
3127
user = User(Email=email)
3228
try:
33-
actions.Login(user, password, captcha)
34-
Globals.Settings.UserList.users.append(user)
35-
Globals.Settings.Save()
29+
activation_url, activation_code = actions.InitiateLogin(user)
30+
return jsonify({
31+
'activation_url': 'https://www.kobo.com/activate',
32+
'activation_code': activation_code,
33+
'check_url': activation_url,
34+
'email': email
35+
})
3636
except Exception as err:
3737
error = str(err)
3838
else:
39-
error = 'email, password, or captcha missing'
39+
error = 'email is required'
4040
users = Globals.Settings.UserList.users
4141
return render_template('users.j2', users=users, error=error)
4242

4343

44+
@app.route('/user/check-activation', methods=['POST'])
45+
def check_activation():
46+
data = request.get_json()
47+
check_url = data.get('check_url')
48+
email = data.get('email')
49+
50+
if not check_url or not email:
51+
return jsonify({'error': 'Missing required parameters'}), 400
52+
53+
user = User(Email=email)
54+
try:
55+
if actions.CheckActivation(user, check_url):
56+
Globals.Settings.UserList.users.append(user)
57+
Globals.Settings.Save()
58+
return jsonify({'success': True})
59+
return jsonify({'success': False})
60+
except Exception as err:
61+
return jsonify({'error': str(err)}), 400
62+
63+
4464
@app.route('/user/<userid>/remove', methods=['POST'])
4565
def deleteUser(userid):
4666
user = Globals.Settings.UserList.getUser(userid)

kobodl/kobo.py

+11-6
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,21 @@ def __GetHeaderWithAccessToken(self) -> dict:
7373
headers = {"Authorization": authorization}
7474
return headers
7575

76+
def __CheckActivation(self, activationCheckUrl) -> Tuple[str, str, str] | None:
77+
response = self.Session.get(activationCheckUrl)
78+
response.raise_for_status()
79+
jsonResponse = response.json()
80+
if jsonResponse["Status"] == "Complete":
81+
return (jsonResponse["UserEmail"], jsonResponse["UserId"], jsonResponse["UserKey"])
82+
return None
83+
7684
def __WaitTillActivation(self, activationCheckUrl) -> Tuple[str, str]:
7785
while True:
7886
print("Waiting for you to finish the activation...")
7987
time.sleep(5)
80-
81-
response = self.Session.get(activationCheckUrl)
82-
response.raise_for_status()
83-
jsonResponse = response.json()
84-
if jsonResponse["Status"] == "Complete":
85-
return jsonResponse["UserEmail"], jsonResponse["UserId"], jsonResponse["UserKey"]
88+
response = self.__CheckActivation(activationCheckUrl)
89+
if response:
90+
return response
8691

8792
def __ActivateOnWeb(self) -> Tuple[str, str]:
8893
print("Initiating web-based activation")

kobodl/templates/activation_form.html

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<div class="max-w-md">
2+
3+
<!-- Initial Email Form -->
4+
<form id="emailForm" class="mb-8">
5+
<input
6+
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
7+
type="email"
8+
name="email"
9+
id="email"
10+
placeholder="Your Kobo Account Email"
11+
required
12+
>
13+
<button
14+
class="mt-4 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
15+
type="submit"
16+
>
17+
Start Activation
18+
</button>
19+
</form>
20+
21+
<!-- Activation Instructions (Hidden initially) -->
22+
<div id="activationInstructions" class="hidden">
23+
<div class="bg-blue-100 border-l-4 border-blue-500 text-blue-700 p-4 mb-4" role="alert">
24+
<h3 class="font-bold">Follow these steps:</h3>
25+
<ol class="list-decimal ml-4 mt-2">
26+
<li class="mb-2">Open <a href="" id="activationLink" target="_blank" class="text-blue-600 hover:underline">the Kobo activation page</a></li>
27+
<li class="mb-2">Enter this code: <span id="activationCode" class="font-mono bg-gray-200 px-2 py-1 rounded"></span></li>
28+
<li>Sign in to your Kobo account if prompted</li>
29+
</ol>
30+
</div>
31+
<div class="text-center">
32+
<div class="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"></div>
33+
<p class="mt-2">Waiting for activation...</p>
34+
</div>
35+
</div>
36+
</div>
37+
38+
<script>
39+
document.getElementById('emailForm').addEventListener('submit', async (e) => {
40+
e.preventDefault();
41+
const email = document.getElementById('email').value;
42+
43+
try {
44+
const response = await fetch('/user', {
45+
method: 'POST',
46+
headers: {
47+
'Content-Type': 'application/x-www-form-urlencoded',
48+
},
49+
body: new URLSearchParams({
50+
email: email
51+
})
52+
});
53+
54+
const data = await response.json();
55+
56+
// Show activation instructions
57+
document.getElementById('emailForm').classList.add('hidden');
58+
document.getElementById('activationInstructions').classList.remove('hidden');
59+
60+
// Update activation details
61+
document.getElementById('activationLink').href = data.activation_url;
62+
document.getElementById('activationCode').textContent = data.activation_code;
63+
64+
// Start polling for activation
65+
pollActivation(data.check_url, data.email);
66+
67+
} catch (error) {
68+
console.error('Error:', error);
69+
alert('An error occurred. Please try again.');
70+
}
71+
});
72+
73+
async function pollActivation(checkUrl, email) {
74+
while (true) {
75+
try {
76+
const response = await fetch('/user/check-activation', {
77+
method: 'POST',
78+
headers: {
79+
'Content-Type': 'application/json',
80+
},
81+
body: JSON.stringify({
82+
check_url: checkUrl,
83+
email: email
84+
})
85+
});
86+
87+
const data = await response.json();
88+
89+
if (data.success) {
90+
window.location.reload();
91+
return;
92+
}
93+
94+
// Wait 5 seconds before next poll
95+
await new Promise(resolve => setTimeout(resolve, 5000));
96+
97+
} catch (error) {
98+
console.error('Error checking activation:', error);
99+
alert('Error checking activation status. Please try again.');
100+
return;
101+
}
102+
}
103+
}
104+
</script>

kobodl/templates/instructions.html

-36
This file was deleted.

kobodl/templates/users.j2

+4-37
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
<thead>
77
<tr class="text-left">
88
<th class="p-2 pl-4"> Email </th>
9-
<th class="p-2 pl-4""> DeviceId </th>
10-
<th class="p-2 pl-4""> Books </th>
9+
<th class="p-2 pl-4"> DeviceId </th>
10+
<th class="p-2 pl-4"> Books </th>
1111
</tr>
1212
</thead>
1313
<tbody>
@@ -30,40 +30,7 @@
3030
{% endif %}
3131

3232
<h1 class="text-2xl mt-6">Add User</h1>
33-
<form style="max-width: 500px;" action="/user" method="post">
34-
{% include "error.j2" %}
35-
<input
36-
class="my-2 shadow appearance-none border
37-
rounded w-full py-2 px-3 text-gray-700
38-
leading-tight focus:outline-none focus:shadow-outline"
39-
type="text"
40-
name="email"
41-
placeholder="Kobo Account Email"
42-
>
43-
<input
44-
class="my-2 shadow appearance-none border
45-
rounded w-full py-2 px-3 text-gray-700 mb-3
46-
leading-tight focus:outline-none focus:shadow-outline"
47-
name="password"
48-
type="password"
49-
placeholder="password"
50-
>
51-
<input
52-
class="my-2 shadow appearance-none border
53-
rounded w-full py-2 px-3 text-gray-700 mb-3
54-
leading-tight focus:outline-none focus:shadow-outline"
55-
name="captcha"
56-
type="text"
57-
placeholder="captcha code (see instructions)"
58-
>
59-
<button
60-
class="my-2 bg-blue-500 hover:bg-blue-700 text-white font-bold
61-
py-2 px-4 rounded focus:outline-none focus:shadow-outline"
62-
type="submit"
63-
>
64-
Add user
65-
</button>
66-
</form>
33+
{% include "error.j2" %}
34+
{% include "activation_form.html" %}
6735

68-
{% include "instructions.html" %}
6936
{% include "footer.html" %}

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ multi_line_output = 3
1212

1313
[tool.poetry]
1414
name = "kobodl"
15-
version = "0.10.0"
15+
version = "0.10.1"
1616
description = "Kobo Book Downloader"
1717
authors = ["Brandon Davis <[email protected]>"]
1818
license = "Unlicense"

0 commit comments

Comments
 (0)