[mangadex] implement login with client credentials

unless the legacy method still works for your account,
login now requires 4 values:

- client-id
- client-secret
- username
- password

where 'client-id' and 'client-secret' are the credentials
of a personal API client.

> Public clients are not yet available.
This commit is contained in:
Mike Fährmann
2025-05-12 11:14:58 +02:00
parent d92bc06f90
commit f88e42c194
3 changed files with 72 additions and 10 deletions

View File

@@ -7566,6 +7566,23 @@ How To
as ``"api-key"`` and ``"api-secret"``
extractor.mangadex.client-id & .client-secret
---------------------------------------------
Type
``string``
How To
* login and go to your `User Settings <https://mangadex.org/settings>`__
* open the "API Clients" section
* click "``+ Create``"
* choose a name
* click "``✔️ Create``"
* wait for approval / reload the page
* copy the value after "AUTOAPPROVED ACTIVE" in the form "personal-client-..."
and put it in your configuration file as ``"client-id"``
* click "``Get Secret``", then "``Copy Secret``",
and paste it into your configuration file as ``"client-secret"``
extractor.reddit.client-id & .user-agent
----------------------------------------
Type

View File

@@ -409,6 +409,8 @@
},
"mangadex":
{
"client-id" : "",
"client-secret": "",
"username": "",
"password": "",

View File

@@ -29,11 +29,8 @@ class MangadexExtractor(Extractor):
useragent = util.USERAGENT
_cache = {}
def __init__(self, match):
Extractor.__init__(self, match)
self.uuid = match.group(1)
def _init(self):
self.uuid = self.groups[0]
self.api = MangadexAPI(self)
def items(self):
@@ -199,7 +196,14 @@ class MangadexAPI():
self.headers = {}
self.username, self.password = extr._get_auth_info()
if not self.username:
if self.username:
self.client_id = cid = extr.config("client-id")
self.client_secret = extr.config("client-secret")
if cid:
self._authenticate_impl = self._authenticate_impl_client
else:
self._authenticate_impl = self._authenticate_impl_legacy
else:
self.authenticate = util.noop
server = extr.config("api-server")
@@ -249,19 +253,58 @@ class MangadexAPI():
self._authenticate_impl(self.username, self.password)
@cache(maxage=900, keyarg=1)
def _authenticate_impl(self, username, password):
def _authenticate_impl_client(self, username, password):
refresh_token = _refresh_token_cache((username, "personal"))
if refresh_token:
self.extractor.log.info("Refreshing access token")
data = {
"grant_type" : "refresh_token",
"refresh_token": refresh_token,
"client_id" : self.client_id,
"client_secret": self.client_secret,
}
else:
self.extractor.log.info("Logging in as %s", username)
data = {
"grant_type" : "password",
"username" : self.username,
"password" : self.password,
"client_id" : self.client_id,
"client_secret": self.client_secret,
}
self.extractor.log.debug("Using client-id '%s'", self.client_id[:24])
url = ("https://auth.mangadex.org/realms/mangadex"
"/protocol/openid-connect/token")
data = self.extractor.request(
url, method="POST", data=data, fatal=None).json()
try:
access_token = data["access_token"]
except Exception:
raise exception.AuthenticationError(data.get("error_description"))
if refresh_token != data.get("refresh_token"):
_refresh_token_cache.update(
(username, "personal"), data["refresh_token"])
return "Bearer " + access_token
@cache(maxage=900, keyarg=1)
def _authenticate_impl_legacy(self, username, password):
refresh_token = _refresh_token_cache(username)
if refresh_token:
self.extractor.log.info("Refreshing access token")
url = self.root + "/auth/refresh"
data = {"token": refresh_token}
json = {"token": refresh_token}
else:
self.extractor.log.info("Logging in as %s", username)
url = self.root + "/auth/login"
data = {"username": username, "password": password}
json = {"username": username, "password": password}
self.extractor.log.debug("Using legacy login method")
data = self.extractor.request(
url, method="POST", json=data, fatal=None).json()
url, method="POST", json=json, fatal=None).json()
if data.get("result") != "ok":
raise exception.AuthenticationError()
@@ -329,6 +372,6 @@ class MangadexAPI():
return
@cache(maxage=28*86400, keyarg=0)
@cache(maxage=90*86400, keyarg=0)
def _refresh_token_cache(username):
return None