mirror of
https://github.com/koel/koel
synced 2024-11-10 06:34:14 +00:00
feat: login via OTP
This commit is contained in:
parent
ddd5f3f38e
commit
7f1429377e
4 changed files with 52 additions and 5 deletions
|
@ -2,13 +2,15 @@
|
|||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Exceptions\InvalidCredentialsException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\API\UserLoginRequest;
|
||||
use App\Services\AuthenticationService;
|
||||
use App\Values\CompositeToken;
|
||||
use Closure;
|
||||
use Illuminate\Foundation\Auth\ThrottlesLogins;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Throwable;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
|
@ -19,6 +21,26 @@ class AuthController extends Controller
|
|||
}
|
||||
|
||||
public function login(UserLoginRequest $request)
|
||||
{
|
||||
$compositeToken = $this->throttleLoginRequest(
|
||||
fn () => $this->auth->login($request->email, $request->password),
|
||||
$request
|
||||
);
|
||||
|
||||
return response()->json($compositeToken->toArray());
|
||||
}
|
||||
|
||||
public function loginUsingOneTimeToken(Request $request)
|
||||
{
|
||||
$compositeToken = $this->throttleLoginRequest(
|
||||
fn () => $this->auth->loginViaOneTimeToken($request->input('token')),
|
||||
$request
|
||||
);
|
||||
|
||||
return response()->json($compositeToken->toArray());
|
||||
}
|
||||
|
||||
private function throttleLoginRequest(Closure $callback, Request $request): CompositeToken
|
||||
{
|
||||
if ($this->hasTooManyLoginAttempts($request)) {
|
||||
$this->fireLockoutEvent($request);
|
||||
|
@ -26,8 +48,8 @@ class AuthController extends Controller
|
|||
}
|
||||
|
||||
try {
|
||||
return response()->json($this->auth->login($request->email, $request->password)->toArray());
|
||||
} catch (InvalidCredentialsException) {
|
||||
return $callback();
|
||||
} catch (Throwable) {
|
||||
$this->incrementLoginAttempts($request);
|
||||
abort(Response::HTTP_UNAUTHORIZED, 'Invalid credentials');
|
||||
}
|
||||
|
|
|
@ -74,9 +74,17 @@ class AuthenticationService
|
|||
|
||||
public function generateOneTimeToken(User $user): string
|
||||
{
|
||||
$token = bin2hex(random_bytes(16));
|
||||
Cache::set("one-time-token.$user->id", $token, 60 * 10);
|
||||
$token = bin2hex(random_bytes(12));
|
||||
Cache::set("one-time-token.$token", encrypt($user->id), 60 * 10);
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
public function loginViaOneTimeToken(string $token): CompositeToken
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->userRepository->getOne(decrypt(Cache::get("one-time-token.$token")));
|
||||
|
||||
return $this->logUserIn($user);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,6 +65,8 @@ Route::prefix('api')->middleware('api')->group(static function (): void {
|
|||
Route::get('ping', static fn () => null);
|
||||
|
||||
Route::post('me', [AuthController::class, 'login'])->name('auth.login');
|
||||
Route::post('me/otp', [AuthController::class, 'loginUsingOneTimeToken']);
|
||||
|
||||
Route::delete('me', [AuthController::class, 'logout']);
|
||||
|
||||
Route::post('forgot-password', ForgotPasswordController::class);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Services\AuthenticationService;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
@ -33,6 +34,20 @@ class AuthTest extends TestCase
|
|||
->assertUnauthorized();
|
||||
}
|
||||
|
||||
public function testLoginViaOneTimeToken(): void
|
||||
{
|
||||
$user = create_user();
|
||||
$authService = app(AuthenticationService::class);
|
||||
$token = $authService->generateOneTimeToken($user);
|
||||
|
||||
$this->post('api/me/otp', ['token' => $token])
|
||||
->assertOk()
|
||||
->assertJsonStructure([
|
||||
'token',
|
||||
'audio-token',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testLogOut(): void
|
||||
{
|
||||
$user = create_user([
|
||||
|
|
Loading…
Reference in a new issue