Skip to content

Commit

Permalink
async password changed
Browse files Browse the repository at this point in the history
  • Loading branch information
asika32764 committed Mar 25, 2024
1 parent 4c5ae4b commit f024598
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 30 deletions.
3 changes: 2 additions & 1 deletion etc/app/main.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
return $cors->allowHeaders(
[
'Authorization',
'Content-Type'
'Content-Type',
'X-Password-Last-Reset'
]
)
->allowMethods('*');
Expand Down
3 changes: 3 additions & 0 deletions routes/api/v1/public/auth.route.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@

$router->any('/refreshToken')
->handler('refreshToken');

$router->any('/me')
->handler('me');
});
4 changes: 4 additions & 0 deletions src/Enum/ApiTokenType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace App\Enum;

use Windwalker\Utilities\Attributes\Enum\Title;
use Windwalker\Utilities\Enum\EnumTranslatableInterface;
use Windwalker\Utilities\Enum\EnumTranslatableTrait;
use Windwalker\Utilities\Contract\LanguageInterface;
Expand All @@ -12,7 +13,10 @@ enum ApiTokenType: string implements EnumTranslatableInterface
{
use EnumTranslatableTrait;

#[Title('Access Token')]
case ACCESS = 'access';

#[Title('Refresh Token')]
case REFRESH = 'refresh';

public function trans(LanguageInterface $lang, ...$args): string
Expand Down
5 changes: 4 additions & 1 deletion src/Enum/ErrorCode.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ enum ErrorCode: int
#[Title('Invalid Session.')]
case INVALID_SESSION = 40104;

#[Title('Password changed.')]
case PASSWORD_CHANGED = 40105;

// 403
#[Title('This email has been used.')]
case USER_EMAIL_EXISTS = 40303;
case USER_EMAIL_EXISTS = 40301;
}
30 changes: 22 additions & 8 deletions src/Middleware/ApiAuthMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

namespace App\Middleware;

use App\Entity\User;
use App\Enum\ApiTokenType;
use App\Enum\ErrorCode;
use App\Service\ApiUserService;
use App\Service\JwtAuthService;
use Lyrasoft\Luna\User\UserService;
Expand All @@ -29,18 +31,30 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
$authHeader = $request->getHeaderLine('Authorization');

if ($authHeader) {
$payload = $this->jwtAuthService->extractAccessTokenFromHeader($authHeader, $user);
$this->jwtAuthService->extractAccessTokenFromHeader($authHeader, $user);

// If not access token, let's ignore this token
if ($payload->getType() === ApiTokenType::ACCESS) {
if (!$user) {
throw new UnauthorizedException('User not found.');
}
$this->checkLastReset($request, $user);

$this->userService->setCurrentUser($user);
}
$this->userService->setCurrentUser($user);
}

return $handler->handle($request);
}

/**
* @param ServerRequestInterface $request
* @param User|null $user
*
* @return void
*/
protected function checkLastReset(ServerRequestInterface $request, ?User $user): void
{
$clientLastReset = $request->getHeaderLine('X-Password-Last-Reset');

$serverLastReset = (string) $user->getLastReset()?->toUnix();

if ($clientLastReset !== $serverLastReset) {
ErrorCode::PASSWORD_CHANGED->throw();
}
}
}
11 changes: 6 additions & 5 deletions src/Module/Api/AuthController.php
Original file line number Diff line number Diff line change
Expand Up @@ -260,11 +260,7 @@ public function refreshToken(
): array {
$authHeader = $app->getHeader('Authorization');

$payload = $jwtAuthService->extractAccessTokenFromHeader($authHeader, $user);

if ($payload->getType() !== ApiTokenType::REFRESH) {
throw new \RuntimeException('Token is not refresh token', 400);
}
$payload = $jwtAuthService->extractAccessTokenFromHeader($authHeader, $user, ApiTokenType::REFRESH);

$exp = $payload->getExp();

Expand All @@ -279,4 +275,9 @@ public function refreshToken(

return compact('accessToken', 'refreshToken');
}

public function me(\CurrentUser $currentUser): \CurrentUser
{
return $currentUser;
}
}
37 changes: 22 additions & 15 deletions src/Service/JwtAuthService.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@
use App\Data\ApiTokenPayload;
use App\Entity\User;
use App\Entity\UserSecret;
use App\Enum\ApiTokenType;
use App\Enum\ErrorCode;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use JetBrains\PhpStorm\ArrayShape;
use Windwalker\Core\DateTime\Chronos;
use Windwalker\Core\Security\Exception\UnauthorizedException;
use Windwalker\Crypt\Hasher\PasswordHasher;
use Windwalker\Crypt\SecretToolkit;
use Windwalker\DI\Attributes\Service;
use Windwalker\ORM\ORM;
use Windwalker\Utilities\TypeCast;
Expand All @@ -41,7 +38,7 @@ public function createAccessToken(User $user, UserSecret $userSecret): string
'exp' => $now->modify('+7days')->toUnix(),
'email' => $user->getEmail(),
'id' => $user->getId(),
'type' => 'access'
'type' => 'access',
];

return JWT::encode(
Expand All @@ -62,7 +59,7 @@ public function createRefreshToken(User $user, UserSecret $userSecret): string
'exp' => $now->modify('+6month')->toUnix(),
'email' => $user->getEmail(),
'id' => $user->getId(),
'type' => 'refresh'
'type' => 'refresh',
];

return JWT::encode(
Expand All @@ -72,19 +69,25 @@ public function createRefreshToken(User $user, UserSecret $userSecret): string
);
}

public function extractAccessTokenFromHeader(string $authorization, ?User &$user = null): ApiTokenPayload
{
public function extractAccessTokenFromHeader(
string $authorization,
?User &$user = null,
ApiTokenType $type = ApiTokenType::ACCESS
): ApiTokenPayload {
sscanf($authorization, 'Bearer %s', $token);

if (!$token) {
throw new \RuntimeException('Token is empty.', 400);
}

return $this->extractAccessToken((string) $token, $user);
return $this->extractAccessToken((string) $token, $user, $type);
}

public function extractAccessToken(string $token, ?User &$user = null): ApiTokenPayload
{
public function extractAccessToken(
string $token,
?User &$user = null,
ApiTokenType $type = ApiTokenType::ACCESS
): ApiTokenPayload {
$parts = explode('.', $token);

if (!isset($parts[1])) {
Expand Down Expand Up @@ -118,7 +121,13 @@ public function extractAccessToken(string $token, ?User &$user = null): ApiToken
throw new \RuntimeException('Invalid Payload', 400);
}

$issuedAt = Chronos::createFromFormat('U', (string) $payload->iat);
$payload = ApiTokenPayload::wrap(TypeCast::toArray($payload, true));

if ($payload->getType() !== $type) {
throw new \RuntimeException('Token type is not ' . $type->getTitle(), 400);
}

$issuedAt = Chronos::createFromFormat('U', (string) $payload->getIat());

if ($issuedAt < $user->getSessValidForm()) {
$user = null;
Expand All @@ -128,9 +137,7 @@ public function extractAccessToken(string $token, ?User &$user = null): ApiToken
throw $ex;
}

return ApiTokenPayload::wrap(
TypeCast::toArray($payload, true)
);
return $payload;
}

public static function getIssuer(): string
Expand Down

0 comments on commit f024598

Please sign in to comment.