-
Notifications
You must be signed in to change notification settings - Fork 0
Slim 3 Example
Andy Dyer edited this page Feb 20, 2020
·
8 revisions
composer require andrewdyer/jwt-auth firebase/php-jwt illuminate/database nesbot/carbon slim/slim:3.*
Update composer.json
and add autoload information.
{
// ...
+ "autoload": {
+ "psr-4": {
+ "App\\": "app"
+ }
+ }
}
Create a user model which extends Illuminate\Database\Eloquent\Model
and implements the Anddye\JwtAuth\Contracts\JwtSubject
interface.
<?php
namespace App\Models;
use Anddye\JwtAuth\Contracts\JwtSubject;
use Illuminate\Database\Eloquent\Model;
/**
* Class User.
*/
final class User extends Model implements JwtSubject
{
/**
* @var array
*/
protected $hidden = ['password'];
/**
* @return int
*/
public function getJwtIdentifier(): int
{
return $this->id;
}
}
Create an auth provider which implements the Anddye\JwtAuth\Providers\AuthProviderInterface
interface. This will handle the user validation.
<?php
namespace App\Auth;
use Anddye\JwtAuth\Contracts\JwtSubject;
use Anddye\JwtAuth\Providers\AuthProviderInterface;
use App\Models\User;
/**
* Class AuthProvider.
*/
final class AuthProvider implements AuthProviderInterface
{
/**
* @param string $username
* @param string $password
*
* @return JwtSubject|null
*/
public function byCredentials(string $username, string $password): ?JwtSubject
{
if (!$user = User::where('username', $username)->first()) {
return null;
}
if (!password_verify($password, $user->password)) {
return null;
}
return $user;
}
/**
* @param int $id
*
* @return JwtSubject|null
*/
public function byId(int $id): ?JwtSubject
{
return User::find($id);
}
}
Create an jwt provider which implements the Anddye\JwtAuth\Providers\JwtProviderInterface
interface. This will handle encoding / decoding of the jwt token. The slim container is injected to get config properties off the settings array.
<?php
namespace App\Auth;
use Anddye\JwtAuth\Providers\JwtProviderInterface;
use Firebase\JWT\JWT;
/**
* Class JwtProvider.
*/
final class JwtProvider implements JwtProviderInterface
{
/**
* @var string
*/
protected $algorithm;
/**
* @var string
*/
protected $secret;
/**
* JwtProvider constructor.
*
* @param string $algorithm
* @param string $secret
*/
public function __construct(string $algorithm, string $secret)
{
$this->algorithm = $algorithm;
$this->secret = $secret;
}
/**
* @param string $token
*
* @return object
*/
public function decode(string $token): object
{
return JWT::decode($token, $this->secret, [$this->algorithm]);
}
/**
* @param array $claims
*
* @return string
*/
public function encode(array $claims): string
{
return JWT::encode($claims, $this->secret, $this->algorithm);
}
}
<?php
namespace App\Controllers;
use Anddye\JwtAuth\JwtAuth;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
/**
* Class LoginController.
*/
final class LoginController
{
/**
* @var JwtAuth
*/
protected $jwtAuth;
/**
* LoginController constructor.
*
* @param JwtAuth $jwtAuth
*/
public function __construct(JwtAuth $jwtAuth)
{
$this->jwtAuth = $jwtAuth;
}
/**
* @param Request $request
* @param Response $response
*
* @return Response
*/
public function postAction(Request $request, Response $response): Response
{
$username = $request->getParam('username', '');
$password = $request->getParam('password', '');
if (!$token = $this->jwtAuth->attempt($username, $password)) {
return $response->withJson(['error' => '']);
}
return $response->withJson(['token' => $token]);
}
}
<?php
namespace App\Controllers;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
/**
* Class ActorController.
*/
final class ActorController
{
/**
* @param Request $request
* @param Response $response
*
* @return Response
*/
public function getAction(Request $request, Response $response): Response
{
$actor = $request->getAttribute('actor');
return $response->withJson($actor);
}
}
<?php
namespace App\Middleware;
use Anddye\JwtAuth\JwtAuth;
use Firebase\JWT\BeforeValidException;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\SignatureInvalidException;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
/**
* Class AuthenticateMiddleware.
*/
final class AuthenticateMiddleware
{
/**
* @var JwtAuth
*/
protected $jwtAuth;
/**
* AuthenticateMiddleware constructor.
*
* @param JwtAuth $jwtAuth
*/
public function __construct(JwtAuth $jwtAuth)
{
$this->jwtAuth = $jwtAuth;
}
/**
* @param Request $request
* @param Response $response
* @param callable $next
*
* @return Response
*/
public function __invoke(Request $request, Response $response, callable $next): Response
{
try {
if (!$header = $this->getAuthorizationHeader($request)) {
return $response->withJson(['error' => ''], 401);
}
if (!$token = $this->extractToken($header)) {
return $response->withJson(['error' => ''], 401);
}
if (!$actor = $this->jwtAuth->authenticate($token)->getActor()) {
return $response->withJson(['error' => ''], 401);
}
$request = $request->withAttribute('actor', $actor);
return $next($request, $response);
} catch (BeforeValidException | ExpiredException | SignatureInvalidException $ex) {
return $response->withJson(['error' => $ex->getMessage()], 401);
}
}
/**
* @param string $header
*
* @return string|null
*/
protected function extractToken(string $header): ?string
{
if (preg_match('/Bearer\s(\S+)/', $header, $matches)) {
return $matches[1];
}
return null;
}
/**
* @param Request $request
*
* @return string|null
*/
protected function getAuthorizationHeader(Request $request): ?string
{
if ($header = $request->getHeader('Authorization')) {
list($header) = $header;
return $header;
}
return null;
}
}
<?php
use Slim\App;
use Slim\Handlers\Strategies\RequestResponseArgs;
require __DIR__ . '/../vendor/autoload.php';
$app = new App([
'settings' => [
'displayErrorDetails' => true,
'database' => [
'driver' => 'mysql',
'host' => '127.0.0.1',
'port' => '3306',
'database' => 'pt',
'username' => 'root',
'password' => 'password',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
],
'jwt' => [
'algorithm' => 'HS256',
'issuer' => 'https://github.com/andrewdyer/jwt-auth',
'jwt_id' => '60ad17YE73',
'secret' => 'supersecretkeynottobecommitedtogithub',
],
],
'foundHandler' => function () {
return new RequestResponseArgs();
},
]);
require_once __DIR__ . '/container.php';
require_once __DIR__ . '/routes.php';
<?php
use Anddye\JwtAuth\ClaimsFactory;
use Anddye\JwtAuth\JwtAuth;
use App\Auth\AuthProvider;
use App\Auth\JwtProvider;
use App\Controllers\ActorController;
use App\Controllers\LoginController;
use App\Middleware\AuthenticateMiddleware;
use Carbon\Carbon;
use Illuminate\Database\Capsule\Manager as Capsule;
$container = $app->getContainer();
$container[Capsule::class] = function ($container) {
$capsule = new Capsule();
$capsule->addConnection($container->get('settings')['database']);
$capsule->setAsGlobal();
return $capsule;
};
$container[Capsule::class]->bootEloquent();
$container[JwtAuth::class] = function ($container) {
$settings = $container->get('settings')['jwt'];
$authProvider = new AuthProvider();
$jwtProvider = new JwtProvider($settings['algorithm'], $settings['secret']);
$claimsFactory = ClaimsFactory::build([
'exp' => Carbon::now()->addMinute(5)->getTimestamp(),
'iat' => Carbon::now()->getTimestamp(),
'iss' => $settings['issuer'],
'jti' => $settings['jwt_id'],
'nbf' => Carbon::now()->getTimestamp(),
]);
return new JwtAuth($authProvider, $jwtProvider, $claimsFactory);
};
$container[ActorController::class] = function () {
return new ActorController();
};
$container[LoginController::class] = function ($container) {
return new LoginController(
$container->get(JwtAuth::class)
);
};
$container[AuthenticateMiddleware::class] = function ($container) {
return new AuthenticateMiddleware(
$container->get(JwtAuth::class)
);
};
<?php
use App\Controllers\ActorController;
use App\Controllers\LoginController;
use App\Middleware\AuthenticateMiddleware;
$app->group('/api', function () use ($container) {
$this->get('/auth/me', ActorController::class . ':getAction')->add($container[AuthenticateMiddleware::class]);
$this->post('/auth/login', LoginController::class . ':postAction');
});
<?php
require_once __DIR__ . '/../bootstrap/app.php';
$app->run();