Skip to content

Commit 913c92f

Browse files
authored
Merge pull request #1 from fdsis/f_hmac_token
Multiple AuthenticationProvider
2 parents d37c595 + f830baf commit 913c92f

6 files changed

Lines changed: 271 additions & 54 deletions

File tree

Classes/Authentication/UsernameHmacTimestampProvider.php

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
namespace FormatD\HmacAuthentication\Authentication;
33

44

5+
use FormatD\HmacAuthentication\Domain\Model\HmacToken;
6+
use FormatD\HmacAuthentication\Service\HmacService;
57
use Neos\Flow\Annotations as Flow;
68
use Neos\Flow\Security\Account;
79
use Neos\Flow\Security\AccountRepository;
8-
use Neos\Flow\Security\Authentication\Token\UsernamePassword;
9-
use Neos\Flow\Security\Authentication\Token\UsernamePasswordHttpBasic;
10+
use Neos\Flow\Security\Authentication\TokenAndProviderFactoryInterface;
1011
use Neos\Flow\Security\Authentication\TokenInterface;
1112
use Neos\Flow\Security\Context;
12-
use Neos\Flow\Security\Cryptography\HashService;
1313
use Neos\Flow\Security\Exception\UnsupportedAuthenticationTokenException;
1414

1515
/**
@@ -31,6 +31,12 @@ class UsernameHmacTimestampProvider extends \Neos\Flow\Security\Authentication\P
3131
*/
3232
protected $hmacService;
3333

34+
/**
35+
* @Flow\Inject
36+
* @var TokenAndProviderFactoryInterface
37+
*/
38+
protected $tokenAndProviderFactory;
39+
3440
/**
3541
* @var Context
3642
* @Flow\Inject
@@ -43,6 +49,12 @@ class UsernameHmacTimestampProvider extends \Neos\Flow\Security\Authentication\P
4349
*/
4450
protected $persistenceManager;
4551

52+
/**
53+
* @Flow\InjectConfiguration(package="FormatD.HmacAuthentication.allowedAuthenticationProviders")
54+
* @var array
55+
*/
56+
protected $allowedAuthenticationProviders;
57+
4658
/**
4759
* Returns the class names of the tokens this provider can authenticate.
4860
*
@@ -53,6 +65,43 @@ public function getTokenClassNames()
5365
return [UsernameHmacTimestampToken::class];
5466
}
5567

68+
/**
69+
* @param string $alias
70+
* @return false|string
71+
*/
72+
protected function getAuthenticationProviderNameByAlias($alias) {
73+
if(!is_array($this->allowedAuthenticationProviders)) {
74+
return false;
75+
}
76+
if(!key_exists($alias, $this->allowedAuthenticationProviders)) {
77+
return false;
78+
}
79+
$name = $this->allowedAuthenticationProviders[$alias];
80+
return strlen($name) > 0 ? $name : false;
81+
}
82+
83+
/**
84+
* @param HmacToken $hmacToken
85+
*/
86+
protected function getAuthenticationProviderNameWithHmacToken($hmacToken) {
87+
if($hmacToken->hasPayloadEntry(HmacService::HTK_Provider)) {
88+
$tokenAuthenticationProviderAlias = $hmacToken->getPayloadEntry(HmacService::HTK_Provider);
89+
if($authenticationProviderName = $this->getAuthenticationProviderNameByAlias($tokenAuthenticationProviderAlias)) {
90+
return $authenticationProviderName;
91+
}
92+
}
93+
94+
return $this->options['mainAuthenticationProviderName'] ? $this->options['mainAuthenticationProviderName'] : $this->name;
95+
}
96+
97+
/**
98+
* @param HmacToken $hmacToken
99+
* @return mixed
100+
*/
101+
protected function getUsernameFromHmacToken($hmacToken) {
102+
return $hmacToken->getPayloadEntry(HmacService::HTK_Username);
103+
}
104+
56105
/**
57106
* Checks the given token for validity and sets the token authentication status
58107
* accordingly (success, wrong credentials or no credentials given).
@@ -76,31 +125,35 @@ public function authenticate(TokenInterface $authenticationToken)
76125
$authenticationToken->setAuthenticationStatus(TokenInterface::NO_CREDENTIALS_GIVEN);
77126
}
78127

79-
if (!is_array($credentials) || !isset($credentials['username']) || !isset($credentials['hmac']) || !isset($credentials['timestamp'])) {
128+
if (!is_array($credentials) || !isset($credentials[UsernameHmacTimestampToken::CRED_TOKEN])) {
80129
return;
81130
}
82131

83-
$providerName = $this->options['mainAuthenticationProviderName'] ? $this->options['mainAuthenticationProviderName'] : $this->name;
132+
$hmacToken = HmacToken::FromJson($credentials[UsernameHmacTimestampToken::CRED_TOKEN]);
133+
134+
$providerName = $this->getAuthenticationProviderNameWithHmacToken($hmacToken);
135+
$userName = $this->getUsernameFromHmacToken($hmacToken);
136+
84137
$accountRepository = $this->accountRepository;
85-
$this->securityContext->withoutAuthorizationChecks(function () use ($credentials, $providerName, $accountRepository, &$account) {
86-
$account = $accountRepository->findActiveByAccountIdentifierAndAuthenticationProviderName($credentials['username'], $providerName);
138+
$this->securityContext->withoutAuthorizationChecks(function () use ($credentials, $userName, $providerName, $accountRepository, &$account) {
139+
$account = $accountRepository->findActiveByAccountIdentifierAndAuthenticationProviderName($userName, $providerName);
87140
});
88141

89142
$authenticationToken->setAuthenticationStatus(TokenInterface::WRONG_CREDENTIALS);
90143

144+
$isHmacTokenValid = $this->hmacService->validateToken($hmacToken);
145+
91146
if ($account === null) {
92-
// validate anyway to prevent timing attacks
93-
$this->hmacService->validateHmac($credentials['username'], $credentials['timestamp'], $credentials['hmac']);
147+
// Return after token hmac validation to prevent timing attacks
94148
return;
95149
}
96150

97-
if ($this->hmacService->validateHmac($credentials['username'], $credentials['timestamp'], $credentials['hmac'])) {
151+
if ($isHmacTokenValid) {
98152
$account->authenticationAttempted(TokenInterface::AUTHENTICATION_SUCCESSFUL);
99153
$authenticationToken->setAuthenticationStatus(TokenInterface::AUTHENTICATION_SUCCESSFUL);
100154
$authenticationToken->setAccount($account);
101-
} else {
102-
$account->authenticationAttempted(TokenInterface::WRONG_CREDENTIALS);
103155
}
156+
104157
$this->accountRepository->update($account);
105158
$this->persistenceManager->whitelistObject($account);
106159
}

Classes/Authentication/UsernameHmacTimestampToken.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
*/
1313
class UsernameHmacTimestampToken extends \Neos\Flow\Security\Authentication\Token\AbstractToken {
1414

15+
const CRED_TOKEN = 'token';
16+
1517
/**
1618
* The password credentials
1719
* @var array
1820
* @Flow\Transient
1921
*/
20-
protected $credentials = ['username' => '', 'hmac' => '', 'timestamp' => ''];
22+
protected $credentials = [self::CRED_TOKEN => ''];
2123

2224
/**
2325
* @var \Neos\Flow\Utility\Environment
@@ -43,12 +45,10 @@ class UsernameHmacTimestampToken extends \Neos\Flow\Security\Authentication\Toke
4345
*/
4446
public function updateCredentials(\Neos\Flow\Mvc\ActionRequest $actionRequest)
4547
{
46-
$credentials = $this->hmacService->getCredentialsFromActionRequest($actionRequest);
48+
$token = $this->hmacService->getCredentialsFromActionRequest($actionRequest);
4749

48-
if ($credentials) {
49-
$this->credentials['username'] = $credentials->username;
50-
$this->credentials['hmac'] = $credentials->hmac;
51-
$this->credentials['timestamp'] = $credentials->timestamp;
50+
if ($token) {
51+
$this->credentials[self::CRED_TOKEN] = $token->toJson();
5252
$this->setAuthenticationStatus(self::AUTHENTICATION_NEEDED);
5353
}
5454
}
@@ -60,7 +60,8 @@ public function updateCredentials(\Neos\Flow\Mvc\ActionRequest $actionRequest)
6060
*/
6161
public function __toString()
6262
{
63-
return 'Username: "' . $this->credentials['username'] . '", Hmac: "***", Timestamp: "' . $this->credentials['timestamp'] . '" ';
63+
$token = $this->credentials[self::CRED_TOKEN];
64+
return 'Token: "' . $token . '"';
6465
}
6566

6667
}

Classes/Domain/Model/HmacToken.php

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?php
2+
3+
namespace FormatD\HmacAuthentication\Domain\Model;
4+
5+
use Neos\Flow\Annotations as Flow;
6+
7+
/**
8+
* @Flow\Scope("prototype")
9+
*/
10+
class HmacToken
11+
{
12+
/**
13+
* @var array<string,string>
14+
*/
15+
protected $payload;
16+
17+
/**
18+
* @var string
19+
*/
20+
protected $hmac;
21+
22+
/**
23+
* @var int
24+
*/
25+
protected $timestamp;
26+
27+
public function __construct($payload = [], $hmac = null, $timestamp = null)
28+
{
29+
$this->payload = $payload;
30+
$this->hmac = $hmac;
31+
$this->timestamp = $timestamp;
32+
}
33+
34+
/**
35+
* @param string $hmac
36+
*/
37+
public function setHmac(string $hmac) {
38+
$this->hmac = $hmac;
39+
}
40+
41+
/**
42+
* @param int $timestamp
43+
*/
44+
public function setTimestamp(int $timestamp) {
45+
$this->timestamp = $timestamp;
46+
}
47+
48+
/**
49+
* @param string $key
50+
* @param string $value
51+
*/
52+
public function setPayloadEntry(string $key, string $value) {
53+
$this->payload[$key] = $value;
54+
}
55+
56+
/**
57+
* @param string $key
58+
*/
59+
public function hasPayloadEntry(string $key) {
60+
return array_key_exists($key, $this->payload);
61+
}
62+
63+
/**
64+
* @param string $key
65+
* @return mixed|string
66+
*/
67+
public function getPayloadEntry(string $key) {
68+
return $this->payload[$key];
69+
}
70+
71+
/**
72+
* @return false|string
73+
*/
74+
public function getHashData() {
75+
return json_encode($this->payload);
76+
}
77+
78+
/**
79+
* @return int
80+
*/
81+
public function getTimestamp() {
82+
return $this->timestamp;
83+
}
84+
85+
/**
86+
* @return string
87+
*/
88+
public function getHmac() {
89+
return $this->hmac;
90+
}
91+
92+
/**
93+
* @return false|string
94+
*/
95+
public function toJson() {
96+
return json_encode([
97+
'payload' => $this->payload,
98+
'hmac' => $this->hmac,
99+
'timestamp' => $this->timestamp
100+
]);
101+
}
102+
103+
/**
104+
* @param string $json
105+
* @return HmacToken
106+
*/
107+
public static function FromJson(string $json) {
108+
$data = json_decode($json, true);
109+
$token = new HmacToken($data['payload'], $data['hmac'], $data['timestamp']);
110+
return $token;
111+
}
112+
}

0 commit comments

Comments
 (0)