Skip to content

Commit

Permalink
Support account delete
Browse files Browse the repository at this point in the history
  • Loading branch information
asika32764 committed May 20, 2024
1 parent 6ea57ee commit 0c3f2f1
Show file tree
Hide file tree
Showing 8 changed files with 263 additions and 161 deletions.
158 changes: 79 additions & 79 deletions composer.lock

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion etc/di.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

declare(strict_types=1);

use App\Enum\ErrorCode;
use Lyrasoft\Luna\User\UserService;
use Windwalker\Console\CommandWrapper;
use Windwalker\Core\Attributes\Controller;
Expand Down Expand Up @@ -33,7 +34,13 @@ class_alias(\App\Entity\User::class, CurrentUser::class);
],
'bindings' => [
CurrentUser::class => function (Container $container) {
return $container->get(UserService::class)->getCurrentUser();
$user = $container->get(UserService::class)->getCurrentUser();

if (!$user->isLogin()) {
ErrorCode::USER_NOT_FOUND->throw();
}

return $user;
},
],
'aliases' => [
Expand Down
8 changes: 7 additions & 1 deletion routes/api/v1/public/auth.route.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace App\Routes;

use App\Middleware\ApiAuthMiddleware;
use App\Module\Api\AuthController;
use Windwalker\Core\Router\RouteCreator;

Expand All @@ -26,5 +27,10 @@
->handler('refreshToken');

$router->any('/me')
->handler('me');
->handler('me')
->middleware(ApiAuthMiddleware::class);

$router->any('/delete/me')
->handler('deleteMe')
->middleware(ApiAuthMiddleware::class);
});
2 changes: 2 additions & 0 deletions src/Entity/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class User implements EntityInterface, UserEntityInterface
use EntityTrait;

#[Column('id'), PK, AutoIncrement]

#[UUIDBin]
#[CastNullable('uuid_bin', 'uuid_bin')]
protected ?UuidInterface $id = null;
Expand Down Expand Up @@ -166,6 +167,7 @@ public static function afterDelete(AfterDeleteEvent $event): void
$orm = $event->getORM();

$orm->deleteWhere(UserSecret::class, ['user_id' => uuid2bin($entity->getId())]);
$orm->deleteWhere(Account::class, ['user_id' => uuid2bin($entity->getId())]);
}

public function can(string $action, ...$args): bool
Expand Down
3 changes: 3 additions & 0 deletions src/Enum/ErrorCode.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ enum ErrorCode: int
#[Title('Password changed.')]
case PASSWORD_CHANGED = 40105;

#[Title('User not found.')]
case USER_NOT_FOUND = 40106;

// 403
#[Title('This email has been used.')]
case USER_EMAIL_EXISTS = 40301;
Expand Down
7 changes: 6 additions & 1 deletion src/Middleware/ApiAuthMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Windwalker\Core\Security\Exception\UnauthorizedException;
use Windwalker\Query\Exception\NoResultException;

class ApiAuthMiddleware implements MiddlewareInterface
{
Expand All @@ -31,7 +32,11 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
$authHeader = $request->getHeaderLine('Authorization');

if ($authHeader) {
$this->jwtAuthService->extractAccessTokenFromHeader($authHeader, $user);
try {
$this->jwtAuthService->extractAccessTokenFromHeader($authHeader, $user);
} catch (NoResultException) {
ErrorCode::USER_NOT_FOUND->throw();
}

$this->checkLastReset($request, $user);

Expand Down
161 changes: 120 additions & 41 deletions src/Module/Api/AuthController.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use App\Service\EncryptionService;
use App\Service\JwtAuthService;
use Brick\Math\BigInteger;
use Brick\Math\Exception\NumberFormatException;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Lyrasoft\Luna\Auth\SRP\SRPService;
Expand All @@ -24,6 +25,10 @@
use Windwalker\ORM\ORM;
use Windwalker\SRP\Exception\InvalidSessionProofException;

use Windwalker\SRP\Step\PasswordFile;

use Windwalker\SRP\Step\ProofResult;

use function Windwalker\Query\uuid2bin;
use function Windwalker\uid;

Expand Down Expand Up @@ -101,8 +106,6 @@ public function authenticate(
ORM $orm,
JwtAuthService $jwtAuthService,
CipherInterface $cipher,
SRPService $srpService,
EncryptionService $encryptionService,
): array {
[
$email,
Expand Down Expand Up @@ -130,48 +133,17 @@ public function authenticate(
ErrorCode::INVALID_CREDENTIALS->throw();
}

$userSecret = $orm->mustFindOne(
UserSecret::class,
['user_id' => uuid2bin($user->getId())]
);

if (!$loginToken = $user->getLoginToken()) {
ErrorCode::INVALID_SESSION->throw();
}

$loginPayload = JWT::decode(
$loginToken,
new Key($userSecret->getDecodedServerSecret(), 'HS512')
);

$sessPayload = JWT::decode(
$sess,
new Key($userSecret->getDecodedServerSecret(), 'HS512')
[$result, $loginPayload, $userSecret] = $app->call(
$this->srpValidate(...),
compact(
'user',
'A',
'M1',
'sess'
)
);

if ($loginPayload->sess !== $sessPayload->sess) {
ErrorCode::INVALID_SESSION->throw();
}

$password = $user->getPassword();

$pf = SRPService::decodePasswordVerifier($password);

$A = BigInteger::fromBase($A, 16);
$M1 = BigInteger::fromBase($M1, 16);

try {
$server = $srpService->getSRPServer();
$result = $server->step2(
$email,
$pf->salt,
$pf->verifier,
$A,
BigInteger::fromBase($loginPayload->B, 16),
BigInteger::fromBase($loginPayload->b, 16),
$M1
);

// Save enc secrets
if ($loginPayload->updateSecrets && $encSecret && $encMaster) {
$encSecret = $cipher->decrypt($encSecret, $result->preMasterSecret->toBase(10));
Expand Down Expand Up @@ -280,4 +252,111 @@ public function me(\CurrentUser $currentUser): \CurrentUser
{
return $currentUser;
}

public function deleteMe(
AppContext $app,
ORM $orm,
\CurrentUser $user,
): true {
[
$A,
$M1,
$sess,
] = $app->input(
'A',
'M1',
'sess',
)->values();

RequestAssert::assert($A, 'Invalid credentials');
RequestAssert::assert($M1, 'Invalid credentials');

$app->call(
$this->srpValidate(...),
compact(
'user',
'A',
'M1',
'sess'
)
);

// Delete User
$orm->deleteWhere(User::class, ['id' => uuid2bin($user->getId())]);

return true;
}

/**
* @param ORM $orm
* @param SRPService $srpService
* @param User $user
* @param string $A
* @param string $M1
* @param string $sess
*
* @return array{ 0: ProofResult, 1: \stdClass, 2: UserSecret }
*
* @throws NumberFormatException
* @throws \Brick\Math\Exception\DivisionByZeroException
* @throws \Brick\Math\Exception\MathException
* @throws \Brick\Math\Exception\NegativeNumberException
*/
protected function srpValidate(
ORM $orm,
SRPService $srpService,
User $user,
string $A,
string $M1,
string $sess
): array {
$userSecret = $orm->mustFindOne(
UserSecret::class,
['user_id' => uuid2bin($user->getId())]
);

if (!$loginToken = $user->getLoginToken()) {
ErrorCode::INVALID_SESSION->throw();
}

$loginPayload = JWT::decode(
$loginToken,
new Key($userSecret->getDecodedServerSecret(), 'HS512')
);

$sessPayload = JWT::decode(
$sess,
new Key($userSecret->getDecodedServerSecret(), 'HS512')
);

if ($loginPayload->sess !== $sessPayload->sess) {
ErrorCode::INVALID_SESSION->throw();
}

$password = $user->getPassword();

$pf = SRPService::decodePasswordVerifier($password);

$A = BigInteger::fromBase($A, 16);
$M1 = BigInteger::fromBase($M1, 16);
$b = BigInteger::fromBase($loginPayload->b, 16);
$B = BigInteger::fromBase($loginPayload->B, 16);

try {
$server = $srpService->getSRPServer();
$result = $server->step2(
$user->getEmail(),
$pf->salt,
$pf->verifier,
$A,
$B,
$b,
$M1
);

return [$result, $loginPayload, $userSecret];
} catch (InvalidSessionProofException) {
ErrorCode::INVALID_CREDENTIALS->throw();
}
}
}
Loading

0 comments on commit 0c3f2f1

Please sign in to comment.