Commit da566b89 authored by Jacob Priddy's avatar Jacob Priddy 👌

Token authentication!

parent 3127edcd
...@@ -10,6 +10,8 @@ use Illuminate\Http\Request; ...@@ -10,6 +10,8 @@ use Illuminate\Http\Request;
use Illuminate\Auth\GuardHelpers; use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Guard; use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\Authenticatable;
use Source\UseCases\Token\Authenticate\AuthenticateUseCase;
use Source\UseCases\Token\Authenticate\TranslationPresenter;
class ApiGuard implements Guard { class ApiGuard implements Guard {
use GuardHelpers; use GuardHelpers;
...@@ -35,21 +37,25 @@ class ApiGuard implements Guard { ...@@ -35,21 +37,25 @@ class ApiGuard implements Guard {
*/ */
protected string $storageKey; protected string $storageKey;
protected AuthenticateUseCase $authenticator;
/** /**
* Create a new authentication guard. * Create a new authentication guard.
* *
* @param Request $request * @param AuthenticateUseCase $authenticator
* @param string $inputKey * @param Request $request
* @param string $storageKey * @param string $inputKey
* @return void * @param string $storageKey
*/ */
public function __construct( public function __construct(
AuthenticateUseCase $authenticator,
Request $request, Request $request,
$inputKey = 'api_token', $inputKey = 'api_token',
$storageKey = 'api_token') { $storageKey = 'api_token') {
$this->request = $request; $this->request = $request;
$this->inputKey = $inputKey; $this->inputKey = $inputKey;
$this->storageKey = $storageKey; $this->storageKey = $storageKey;
$this->authenticator = $authenticator;
} }
/** /**
...@@ -135,10 +141,10 @@ class ApiGuard implements Guard { ...@@ -135,10 +141,10 @@ class ApiGuard implements Guard {
* @return Authenticatable|null * @return Authenticatable|null
*/ */
public function retrieveByToken(string $token): ?Authenticatable { public function retrieveByToken(string $token): ?Authenticatable {
$token = Token::where($this->storageKey, $token)->first(); $presenter = new TranslationPresenter();
if ($token) {
return $token->user()->first(); $this->authenticator->check($presenter, $token);
}
return null; return $presenter->getViewModel();
} }
} }
...@@ -3,14 +3,14 @@ ...@@ -3,14 +3,14 @@
namespace App\Providers; namespace App\Providers;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Source\UseCases\Users\GetUser\GetUserUseCase;
use Source\Gateways\Users\UsersRepositoryServiceProvider; use Source\Gateways\Users\UsersRepositoryServiceProvider;
use Source\UseCases\Users\GetAllUsers\GetAllUsersUseCase; use Source\Gateways\Tokens\TokensRepositoryServiceProvider;
use Source\UseCases\Users\GetUser\GetUserUseCaseServiceProvider; use Source\UseCases\Users\GetUser\GetUserUseCaseServiceProvider;
use Source\UseCases\Users\CreateUser\CreateUserUseCaseServiceProvider; use Source\UseCases\Users\CreateUser\CreateUserUseCaseServiceProvider;
use Source\UseCases\Users\DeleteUser\DeleteUserUseCaseServiceProvider; use Source\UseCases\Users\DeleteUser\DeleteUserUseCaseServiceProvider;
use Source\UseCases\Users\UpdateUser\UpdateUserUseCaseServiceProvider; use Source\UseCases\Users\UpdateUser\UpdateUserUseCaseServiceProvider;
use Source\UseCases\Users\GetAllUsers\GetAllUsersUseCaseServiceProvider; use Source\UseCases\Users\GetAllUsers\GetAllUsersUseCaseServiceProvider;
use Source\UseCases\Token\Authenticate\AuthenticateUseCaseServiceProvider;
class AppServiceProvider extends ServiceProvider class AppServiceProvider extends ServiceProvider
{ {
...@@ -20,6 +20,7 @@ class AppServiceProvider extends ServiceProvider ...@@ -20,6 +20,7 @@ class AppServiceProvider extends ServiceProvider
*/ */
protected array $gatewayProviders = [ protected array $gatewayProviders = [
UsersRepositoryServiceProvider::class, UsersRepositoryServiceProvider::class,
TokensRepositoryServiceProvider::class,
]; ];
/** /**
...@@ -31,6 +32,8 @@ class AppServiceProvider extends ServiceProvider ...@@ -31,6 +32,8 @@ class AppServiceProvider extends ServiceProvider
UpdateUserUseCaseServiceProvider::class, UpdateUserUseCaseServiceProvider::class,
CreateUserUseCaseServiceProvider::class, CreateUserUseCaseServiceProvider::class,
GetAllUsersUseCaseServiceProvider::class, GetAllUsersUseCaseServiceProvider::class,
AuthenticateUseCaseServiceProvider::class,
]; ];
/** /**
......
...@@ -4,6 +4,7 @@ namespace App\Providers; ...@@ -4,6 +4,7 @@ namespace App\Providers;
use App\Guards\ApiGuard; use App\Guards\ApiGuard;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Source\UseCases\Token\Authenticate\AuthenticateUseCase;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider { class AuthServiceProvider extends ServiceProvider {
...@@ -28,7 +29,7 @@ class AuthServiceProvider extends ServiceProvider { ...@@ -28,7 +29,7 @@ class AuthServiceProvider extends ServiceProvider {
Auth::extend( Auth::extend(
'api', 'api',
static function ($app, $name, array $config) { static function ($app, $name, array $config) {
return new ApiGuard($app['request']); return new ApiGuard($app->make(AuthenticateUseCase::class), $app['request']);
} }
); );
} }
......
...@@ -9,6 +9,10 @@ use Illuminate\Foundation\Auth\User as Authenticatable; ...@@ -9,6 +9,10 @@ use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable { class User extends Authenticatable {
use SoftDeletes; use SoftDeletes;
protected $fillable = [
'*',
];
/** /**
* The attributes that should be cast to native types. * The attributes that should be cast to native types.
* *
......
...@@ -38,9 +38,9 @@ class User { ...@@ -38,9 +38,9 @@ class User {
protected string $email; protected string $email;
/** /**
* @var string * @var string|null
*/ */
protected string $password; protected ?string $password;
/** /**
* @var string * @var string
...@@ -81,7 +81,7 @@ class User { ...@@ -81,7 +81,7 @@ class User {
string $displayName, string $displayName,
?string $emplid, ?string $emplid,
string $email, string $email,
string $password, ?string $password,
string $doorcode, string $doorcode,
?Carbon $expiresAt, ?Carbon $expiresAt,
?Carbon $createdAt, ?Carbon $createdAt,
...@@ -142,9 +142,9 @@ class User { ...@@ -142,9 +142,9 @@ class User {
} }
/** /**
* @return string * @return string|null
*/ */
public function getPassword(): string { public function getPassword(): ?string {
return $this->password; return $this->password;
} }
...@@ -196,6 +196,9 @@ class User { ...@@ -196,6 +196,9 @@ class User {
} }
public function matchCredentials(?string $email, ?string $password): bool { public function matchCredentials(?string $email, ?string $password): bool {
if (!$password || !$email) {
return false;
}
return $this->getEmail() === $email && $this->getPassword() === $password; return $this->getEmail() === $email && $this->getPassword() === $password;
} }
......
...@@ -10,6 +10,6 @@ use Source\Entities\Token; ...@@ -10,6 +10,6 @@ use Source\Entities\Token;
class LocalTokensRepository extends InMemoryTokensRepository { class LocalTokensRepository extends InMemoryTokensRepository {
public function __construct() { public function __construct() {
$this->tokens[] = new Token(1, 1, 'token_string', 'basic token'); $this->tokens[] = new Token(1, 1, 'token_string', 'basic token');
$this->tokens[] = new Token(2, 420, 'expired token', Carbon::now()->subDays(3)); $this->tokens[] = new Token(2, 420, 'expired_token', '', Carbon::now()->subDays(3));
} }
} }
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
namespace Source\Gateways\Tokens; namespace Source\Gateways\Tokens;
use Source\Entities\User;
use Source\Entities\Token; use Source\Entities\Token;
use Source\Exceptions\EntityNotFoundException; use Source\Exceptions\EntityNotFoundException;
...@@ -18,7 +17,7 @@ interface TokensRepository { ...@@ -18,7 +17,7 @@ interface TokensRepository {
/** /**
* @param string $token * @param string $token
* @return User|null * @return Token|null
*/ */
public function findByToken(string $token): ?Token; public function findByToken(string $token): ?Token;
} }
...@@ -107,6 +107,7 @@ class DatabaseUsersRepository implements UsersRepository { ...@@ -107,6 +107,7 @@ class DatabaseUsersRepository implements UsersRepository {
$dbUser->email = $user->getEmail(); $dbUser->email = $user->getEmail();
$dbUser->password = bcrypt($user->getPassword()); $dbUser->password = bcrypt($user->getPassword());
$dbUser->doorcode = hash('sha256', $user->getDoorcode()); $dbUser->doorcode = hash('sha256', $user->getDoorcode());
$dbUser->expires_at = $user->getExpiresAt();
$dbUser->save(); $dbUser->save();
......
<?php
namespace Source\UseCases\Token\Authenticate;
use Source\Entities\User;
use Source\Gateways\Users\UsersRepository;
use Source\Gateways\Tokens\TokensRepository;
class Authenticate implements AuthenticateUseCase {
protected TokensRepository $tokens;
protected UsersRepository $users;
/**
* @param TokensRepository $tokens
* @param UsersRepository $users
*/
public function __construct(TokensRepository $tokens, UsersRepository $users) {
$this->tokens = $tokens;
$this->users = $users;
}
/**
* @inheritDoc
*/
public function check(Presenter $presenter, ?string $token): void {
if (!$token) {
return;
}
$found = $this->tokens->findByToken($token);
if (!$found) {
return;
}
$response = new ResponseModel();
$user = $this->users->get($found->getUserId());
$response->setUser($user);
$presenter->present($response);
}
}
<?php
namespace Source\UseCases\Token\Authenticate;
interface AuthenticateUseCase {
/**
* Authenticates an API token
*
* @param Presenter $presenter
* @param string|null $token
*/
public function check(Presenter $presenter, ?string $token): void;
}
<?php
namespace Source\UseCases\Token\Authenticate;
use Illuminate\Support\ServiceProvider;
use Source\Gateways\Users\UsersRepository;
use Source\Gateways\Tokens\TokensRepository;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Support\DeferrableProvider;
/**
* Service provider must be registered in AppServiceProvider
*/
class AuthenticateUseCaseServiceProvider extends ServiceProvider implements DeferrableProvider {
/**
* Register any application services.
*
* @return void
*/
public function register() {
$this->app->bind(
AuthenticateUseCase::class,
static function (Application $app) {
return new Authenticate($app->make(TokensRepository::class), $app->make(UsersRepository::class));
}
);
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot(): void {
}
/**
* @return array
*/
public function provides() {
return [AuthenticateUseCase::class];
}
}
<?php
namespace Source\UseCases\Token\Authenticate;
use App\User;
interface Presenter {
/**
* @param ResponseModel $responseModel
* @return void
*/
public function present(ResponseModel $responseModel): void;
/**
* @return User|null
*/
public function getViewModel(): ?User;
}
<?php
namespace Source\UseCases\Token\Authenticate;
use Source\Entities\User;
class ResponseModel {
protected ?User $user = null;
/**
* @param User|null $user
*/
public function setUser(?User $user): void {
$this->user = $user;
}
/**
* @return User|null
*/
public function getUser(): ?User {
return $this->user;
}
}
<?php
namespace Source\UseCases\Token\Authenticate;
use App\User;
use Source\UseCases\BasePresenter;
class TranslationPresenter extends BasePresenter implements Presenter {
protected ?User $viewModel = null;
/** @inheritDoc */
public function present(ResponseModel $responseModel): void {
$user = $responseModel->getUser();
if (!$user) {
return;
}
$dbUser = new User();
$dbUser->id = $user->getId();
$dbUser->email = $user->getEmail();
$dbUser->first_name = $user->getFirstName();
$dbUser->last_name = $user->getLastName();
$dbUser->display_name = $user->getDisplayName();
$dbUser->emplid = $user->getEmplid();
$dbUser->created_at = $user->getCreatedAt();
$dbUser->expires_at = $user->getExpiresAt();
$dbUser->updated_at = $user->getUpdatedAt();
$this->viewModel = $dbUser;
}
/** @inheritDoc */
public function getViewModel(): ?User {
return $this->viewModel;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment