Skip to content

Commit 8c7952c

Browse files
Vpatrikmvdkleijn
authored andcommitted
fix: Adapt Duplicati.php to new auth endpoint using JWT
1 parent 7e6423a commit 8c7952c

File tree

1 file changed

+31
-69
lines changed

1 file changed

+31
-69
lines changed

Duplicati/Duplicati.php

Lines changed: 31 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -30,31 +30,44 @@ public function test()
3030
public function auth()
3131
{
3232
// Auth flow references
33-
// https://github.com/duplicati/duplicati/blob/master/Duplicati/Server/webroot/login/login.js
34-
// https://github.com/Pectojin/duplicati-client/blob/master/auth.py
33+
// https://github.com/duplicati/duplicati/blob/master/Duplicati/WebserverCore/Endpoints/V1/Auth.cs
34+
// https://github.com/duplicati/duplicati/blob/master/Duplicati/WebserverCore/Middlewares/JWTProvider.cs
35+
$body = json_encode(["password" => $this->config->password]);
3536

36-
$noncedPassword = $this->getNoncedPassword($this->config->password);
3737

38-
$passAttrs = [
39-
"body" => "password=" . urlencode($noncedPassword),
40-
"cookies" => $this->jar,
38+
$vars = [
39+
"http_errors" => false,
40+
"timeout" => 5,
41+
"body" => $body,
42+
"cookies" => $this->jar, // Store cookies for session handling
4143
"headers" => [
42-
"content-type" => "application/x-www-form-urlencoded"
44+
"Content-Type" => "application/json",
4345
],
4446
];
4547

46-
$passResponse = parent::execute(
47-
$this->url("login.cgi"),
48-
$passAttrs,
49-
null,
48+
49+
$result = parent::execute(
50+
$this->url("api/v1/auth/login"),
51+
[],
52+
$vars,
5053
"POST"
5154
);
5255

53-
if (null === $passResponse || $passResponse->getStatusCode() !== 200) {
56+
if ($result === null) {
57+
throw new Exception("Could not connect to Duplicati");
58+
}
59+
60+
$responseBody = $result->getBody()->getContents();
61+
62+
63+
$response = json_decode($responseBody, true);
64+
65+
if (null === $response || $result->getStatusCode() !== 200 || !isset($response['AccessToken'])) {
5466
throw new Exception("Error logging in");
5567
}
5668

57-
return $passResponse;
69+
$this->config->jwt = $response['AccessToken']; // Store the token correctly
70+
return $response['AccessToken'];
5871
}
5972

6073
public function livestats()
@@ -85,65 +98,14 @@ public function url($endpoint)
8598
return $api_url;
8699
}
87100

88-
private function getNoncedPassword($password)
89-
{
90-
91-
$nonceDetails = $this->getNonce();
92-
93-
$nonce = $nonceDetails["Nonce"];
94-
$salt = $nonceDetails["Salt"];
95-
96-
// Prepare nonced+salted password
97-
$encodedPassword = mb_convert_encoding($password, 'UTF-8', 'ISO-8859-1');
98-
99-
$saltedPasssword = $encodedPassword . base64_decode($salt);
100-
101-
$hashedSaltedPassword = hash('sha256', $saltedPasssword, true);
102-
103-
$nonceAndPass = base64_decode($nonce) . $hashedSaltedPassword;
104-
105-
$hashedNoncedPassword = hash('sha256', $nonceAndPass, true);
106-
107-
$encodedHashedNoncedPassword = base64_encode($hashedNoncedPassword);
108-
109-
return $encodedHashedNoncedPassword;
110-
}
111-
112-
private function getNonce()
113-
{
114-
$nonceAttrs = [
115-
"body" => "get-nonce=1",
116-
"cookies" => $this->jar,
117-
"headers" => [
118-
"content-type" => "application/x-www-form-urlencoded"
119-
],
120-
];
121-
122-
$nonceResponse = parent::execute(
123-
$this->url("login.cgi"),
124-
$nonceAttrs,
125-
null,
126-
"POST"
127-
);
128-
129-
if (null === $nonceResponse || $nonceResponse->getStatusCode() !== 200) {
130-
throw new Exception("Error getting nonce");
131-
}
132-
133-
$nonceBody = $nonceResponse->getBody();
134-
$nonceBodyData = $nonceBody->read(50 * 1024);
135-
$nonceDetails = json_decode(preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $nonceBodyData), true);
136-
137-
return $nonceDetails;
138-
}
139101

140102
private function getServerState()
141103
{
142104
$attrs = [
143105
"cookies" => $this->jar,
144106
"headers" => [
145-
"content-type" => "application/json",
146-
"X-XSRF-Token" => urldecode($this->jar->getCookieByName("xsrf-token")->getValue())
107+
"Content-Type" => "application/json",
108+
"Authorization" => "Bearer " . $this->config->jwt, // Add JWT token
147109
],
148110
];
149111
$result = parent::execute(
@@ -197,11 +159,11 @@ private function getDateDiff($time1, $time2, $precision = 1)
197159

198160
// If time1 > time2 then swap the 2 values
199161
if ($time1 > $time2) {
200-
list( $time1, $time2 ) = array( $time2, $time1 );
162+
list($time1, $time2) = array($time2, $time1);
201163
}
202164

203165
// Set up intervals and diffs arrays
204-
$intervals = array( 'year', 'month', 'day', 'hour', 'minute', 'second' );
166+
$intervals = array('year', 'month', 'day', 'hour', 'minute', 'second');
205167
$diffs = array();
206168

207169
foreach ($intervals as $interval) {
@@ -219,7 +181,7 @@ private function getDateDiff($time1, $time2, $precision = 1)
219181
}
220182

221183
$time1 = strtotime("+" . $looped . " " . $interval, $time1);
222-
$diffs[ $interval ] = $looped;
184+
$diffs[$interval] = $looped;
223185
}
224186

225187
$count = 0;

0 commit comments

Comments
 (0)