Skip to content

Instantly share code, notes, and snippets.

@bwaidelich
Last active February 2, 2017 13:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bwaidelich/cd9f0d2358a50db27b56 to your computer and use it in GitHub Desktop.
Save bwaidelich/cd9f0d2358a50db27b56 to your computer and use it in GitHub Desktop.
Simple JWT based authentication for TYPO3 Flow (see http://jwt.io/).
{
"name": "wwwision/jwt",
"type": "typo3-flow-package",
"description": "Simple JWT based authentication",
"require": {
"typo3/flow": "*",
"firebase/php-jwt": "dev-master"
},
"autoload": {
"psr-0": {
"Wwwision\\Jwt": "Classes"
}
}
}
<?php
namespace Wwwision\Jwt\Security\Authentication\Token;
use TYPO3\Flow\Annotations as Flow;
use TYPO3\Flow\Mvc\ActionRequest;
use TYPO3\Flow\Security\Authentication\Token\AbstractToken;
use TYPO3\Flow\Security\Authentication\Token\SessionlessTokenInterface;
/**
* An authentication token used for simple username and password authentication.
*/
class Jwt extends AbstractToken implements SessionlessTokenInterface {
/**
* The jwt credentials
*
* @var array
* @Flow\Transient
*/
protected $credentials = array('jwt' => '');
/**
* @param ActionRequest $actionRequest The current action request
* @return void
*/
public function updateCredentials(ActionRequest $actionRequest) {
$jwtCookie = $actionRequest->getHttpRequest()->getCookie('jwt');
if ($jwtCookie === NULL) {
return;
}
$this->credentials['jwt'] = $jwtCookie->getValue();
$this->setAuthenticationStatus(self::AUTHENTICATION_NEEDED);
}
/**
* Returns a string representation of the token for logging purposes.
*
* @return string The username credential
*/
public function __toString() {
return 'JWT: "' . substr($this->credentials['jwt'], 0, 10) . '..."';
}
}
<?php
namespace Wwwision\Jwt\Security\Authentication\Provider;
use TYPO3\Flow\Annotations as Flow;
use TYPO3\Flow\Security\Account;
use TYPO3\Flow\Security\Authentication\Provider\PersistedUsernamePasswordProvider;
use TYPO3\Flow\Security\Authentication\TokenInterface;
use TYPO3\Flow\Security\Cryptography\HashService;
use TYPO3\Flow\Security\Exception\UnsupportedAuthenticationTokenException;
use Wwwision\Jwt\Security\Authentication\Token\Jwt;
/**
* An authentication provider that authenticates
* Wwwision\Jwt\Security\Authentication\Token\JwtUsernamePassword tokens.
*/
class PersistedJwtProvider extends PersistedUsernamePasswordProvider {
/**
* @Flow\Inject
* @var HashService
*/
protected $hashService;
/**
* Returns the class names of the tokens this provider can authenticate.
*
* @return array
*/
public function getTokenClassNames() {
return array('Wwwision\Jwt\Security\Authentication\Token\Jwt');
}
/**
* Checks the given token for validity and sets the token authentication status
* accordingly (success, wrong credentials or no credentials given).
*
* @param TokenInterface $authenticationToken The token to be authenticated
* @return void
* @throws UnsupportedAuthenticationTokenException
*/
public function authenticate(TokenInterface $authenticationToken) {
if (!($authenticationToken instanceof Jwt)) {
throw new UnsupportedAuthenticationTokenException('This provider cannot authenticate the given token.', 1417040168);
}
/** @var $account Account */
$account = NULL;
$credentials = $authenticationToken->getCredentials();
if (!is_array($credentials) || !isset($credentials['jwt'])) {
$authenticationToken->setAuthenticationStatus(TokenInterface::NO_CREDENTIALS_GIVEN);
return;
}
// People were confused by the hard-coded "jwt" string here. Of course you could make that configurable but
// not that this is *not* the "secret" that secures the signature. That will be generated based on the *encryption key*
// of the application when using HashService::generateHmac()
$hmac = $this->hashService->generateHmac('jwt');
$jwtPayload = NULL;
try {
$jwtPayload = (array)\JWT::decode($credentials['jwt'], $hmac);
} catch (\Exception $exception) {
$authenticationToken->setAuthenticationStatus(TokenInterface::WRONG_CREDENTIALS);
}
if ($jwtPayload === NULL || !isset($jwtPayload['accountIdentifier'])) {
$authenticationToken->setAuthenticationStatus(TokenInterface::WRONG_CREDENTIALS);
return;
}
// TODO check further JWT properties (e.g. expiration date, client IP, ...)
$this->securityContext->withoutAuthorizationChecks(function() use ($jwtPayload, &$account) {
$account = $this->accountRepository->findActiveByAccountIdentifierAndAuthenticationProviderName($jwtPayload['accountIdentifier'], $this->name);
});
if ($account === NULL) {
$authenticationToken->setAuthenticationStatus(TokenInterface::WRONG_CREDENTIALS);
return;
}
$authenticationToken->setAuthenticationStatus(TokenInterface::AUTHENTICATION_SUCCESSFUL);
$authenticationToken->setAccount($account);
}
}
TYPO3:
Flow:
security:
authentication:
providers:
# just an example - the neos backend login won't (yet) work sessionless
'Typo3BackendProvider':
provider: 'Wwwision\Jwt\Security\Authentication\Provider\PersistedJwtProvider'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment