Skip to content

Commit 4dc28ac

Browse files
committed
Added migration and updates to documentation
1 parent 6d61e6d commit 4dc28ac

9 files changed

Lines changed: 129 additions & 31 deletions

File tree

UPGRADING.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
# Upgrade Guide
22

3+
## Version 1.0.0-beta.8 to 1.0.0-beta.9
4+
5+
### Mandatory Config Changes
6+
7+
#### Config\AuthToken
8+
9+
If you are using the HMAC authentication you need to update the encryption settings in **app/Config/AuthToken.php**.
10+
You will need to update and set the encryption key `$hmacEncryptionKey`. This should be set using .env and/or system
11+
environment variables. Instructions on how to do that can be found in the
12+
[Setting Your Encryption Key](https://codeigniter.com/user_guide/libraries/encryption.html#setting-your-encryption-key)
13+
section of the CodeIgniter 4 documentation.
14+
15+
You also may wish to adjust the default Driver `$hmacEncryptionDriver` and the default Digest `$hmacEncryptionDigest`,
16+
these currently default to `'OpenSSL'` and `'SHA512'` respectively.
17+
18+
### Database Migrations
19+
20+
After updating the `$hmacEncryptionKey` value, you will need to run `php spark migrate --all` in order to encrypt any
21+
existing HMAC tokens.
22+
323
## Version 1.0.0-beta.7 to 1.0.0-beta.8
424

525
### Mandatory Config Changes
@@ -45,7 +65,7 @@ protected function redirectToDeniedUrl(): RedirectResponse
4565
{
4666
return redirect()->to(config('Auth')->groupDeniedRedirect())
4767
->with('error', lang('Auth.notEnoughPrivilege'));
48-
}
68+
}
4969
```
5070

5171
## Version 1.0.0-beta.6 to 1.0.0-beta.7

docs/guides/api_hmac_keys.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ API. When making requests using HMAC keys, the token should be included in the `
1616

1717
Tokens are issued with the `generateHmacToken()` method on the user. This returns a
1818
`CodeIgniter\Shield\Entities\AccessToken` instance. These shared keys are saved to the database in plain text. The
19-
`AccessToken` object returned when you generate it will include a `secret` field which will be the `key` and a `secret2`
20-
field that will be the `secretKey`. You should display the `secretKey` to your user once, so they have a chance to copy
19+
`AccessToken` object returned when you generate it will include a `secret` field which will be the '_key_' and a `rawSecretKey`
20+
field that will be the '_secretKey_'. You should display the '_secretKey_' to your user once, so they have a chance to copy
2121
it somewhere safe, as this is the only time you should reveal this key.
2222

2323
The `generateHmacToken()` method requires a name for the token. These are free strings and are often used to identify
@@ -27,7 +27,7 @@ the user/device the token was generated from/for, like 'Johns MacBook Air'.
2727
$routes->get('hmac/token', static function () {
2828
$token = auth()->user()->generateHmacToken(service('request')->getVar('token_name'));
2929

30-
return json_encode(['key' => $token->secret, 'secretKey' => $token->secret2]);
30+
return json_encode(['key' => $token->secret, 'secretKey' => $token->rawSecretKey]);
3131
});
3232
```
3333

@@ -62,7 +62,7 @@ token is granted all access to all scopes. This might be enough for a smaller AP
6262

6363
```php
6464
$token = $user->generateHmacToken('token-name', ['users-read']);
65-
return json_encode(['key' => $token->secret, 'secretKey' => $token->secret2]);
65+
return json_encode(['key' => $token->secret, 'secretKey' => $token->rawSecretKey]);
6666
```
6767

6868
!!! note

docs/references/authentication/hmac.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,18 @@ $token = $user->generateHmacToken('Work Laptop');
6868

6969
This creates the keys/tokens using a cryptographically secure random string. The keys operate as shared keys.
7070
This means they are stored as-is in the database. The method returns an instance of
71-
`CodeIgniters\Shield\Authentication\Entities\AccessToken`. The field `secret` is the 'key' the field `secret2` is
72-
the shared 'secretKey'. Both are required to when using this authentication method.
71+
`CodeIgniters\Shield\Authentication\Entities\AccessToken`. The field `secret` is the '_key_' the field `rawSecretKey` is
72+
the shared '_secretKey_'. Both are required to when using this authentication method.
7373

7474
**The plain text version of these keys should be displayed to the user immediately, so they can copy it for
75-
their use.** It is recommended that after that only the 'key' field is displayed to a user. If a user loses the
76-
'secretKey', they should be required to generate a new set of keys to use.
75+
their use.** It is recommended that after that only the '_key_' field is displayed to a user. If a user loses the
76+
'_secretKey_', they should be required to generate a new set of keys to use.
7777

7878
```php
7979
$token = $user->generateHmacToken('Work Laptop');
8080

8181
echo 'Key: ' . $token->secret;
82-
echo 'SecretKey: ' . $token->secret2;
82+
echo 'SecretKey: ' . $token->rawSecretKey;
8383
```
8484

8585
## Revoking HMAC Keys

src/Authentication/Authenticators/HmacSha256.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,8 @@ public function check(array $credentials): Result
171171
$config->digest = $authConfig->hmacEncryptionDigest;
172172

173173
// decrypt secret key so signature can be validated
174-
$encrypter = Services::encrypter($config);
175-
$secretKey = $encrypter->decrypt(hex2bin($token->secret2));
174+
$encryptor = Services::encrypter($config);
175+
$secretKey = $encryptor->decrypt(hex2bin($token->secret2));
176176

177177
// Check signature...
178178
$hash = hash_hmac('sha256', $credentials['body'], $secretKey);
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace CodeIgniter\Shield\Database\Migrations;
6+
7+
use CodeIgniter\Database\Forge;
8+
use CodeIgniter\Database\Migration;
9+
use CodeIgniter\Encryption\EncrypterInterface;
10+
use CodeIgniter\Shield\Config\Auth;
11+
use CodeIgniter\Shield\Config\AuthToken;
12+
use CodeIgniter\Shield\Models\UserIdentityModel;
13+
use Config\Encryption;
14+
use Config\Services;
15+
16+
class EncryptHmacKeys extends Migration
17+
{
18+
/**
19+
* Encryptor Interface object
20+
*/
21+
private EncrypterInterface $encryptor;
22+
23+
public function __construct(?Forge $forge = null)
24+
{
25+
/** @var Auth $authConfig */
26+
$authConfig = config('Auth');
27+
28+
if ($authConfig->DBGroup !== null) {
29+
$this->DBGroup = $authConfig->DBGroup;
30+
}
31+
32+
parent::__construct($forge);
33+
34+
/** @var AuthToken $authTokenConfig */
35+
$authTokenConfig = config('AuthToken');
36+
$config = new Encryption();
37+
38+
$config->key = $authTokenConfig->hmacEncryptionKey;
39+
$config->driver = $authTokenConfig->hmacEncryptionDriver;
40+
$config->digest = $authTokenConfig->hmacEncryptionDigest;
41+
42+
// decrypt secret key so signature can be validated
43+
$this->encryptor = Services::encrypter($config);
44+
}
45+
46+
/**
47+
* {@inheritDoc}
48+
*/
49+
public function up(): void
50+
{
51+
$uIdModel = new UserIdentityModel();
52+
53+
$updateList = [];
54+
$hmacIdentities = $uIdModel->where('type', 'hmac_sha256')->findAll();
55+
56+
foreach ($hmacIdentities as $identity) {
57+
$identity->secret2 = bin2hex($this->encryptor->encrypt($identity->secret2));
58+
59+
$updateList[] = [
60+
'id' => $identity->id,
61+
'secret2' => $identity->secret2,
62+
];
63+
}
64+
65+
if ($updateList !== []) {
66+
$uIdModel->updateBatch($updateList, 'id');
67+
}
68+
}
69+
70+
/**
71+
* {@inheritDoc}
72+
*/
73+
public function down(): void
74+
{
75+
$uIdModel = new UserIdentityModel();
76+
77+
$updateList = [];
78+
$hmacIdentities = $uIdModel->where('type', 'hmac_sha256')->findAll();
79+
80+
foreach ($hmacIdentities as $identity) {
81+
$identity->secret2 = $this->encryptor->decrypt(hex2bin($identity->secret2));
82+
83+
$updateList[] = [
84+
'id' => $identity->id,
85+
'secret2' => $identity->secret2,
86+
];
87+
}
88+
89+
if ($updateList !== []) {
90+
$uIdModel->updateBatch($updateList, 'id');
91+
}
92+
}
93+
}

tests/Authentication/Authenticators/HmacAuthenticatorTest.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
namespace Tests\Authentication\Authenticators;
1515

16-
use CodeIgniter\Encryption\Encryption;
1716
use CodeIgniter\I18n\Time;
1817
use CodeIgniter\Shield\Authentication\Authentication;
1918
use CodeIgniter\Shield\Authentication\AuthenticationException;
@@ -38,9 +37,6 @@ protected function setUp(): void
3837
{
3938
parent::setUp();
4039

41-
$authConfig = config('AuthToken');
42-
$authConfig->hmacEncryptionKey = Encryption::createKey();
43-
4440
$config = new Auth();
4541
$auth = new Authentication($config);
4642
$auth->setProvider(model(UserModel::class));

tests/Authentication/Filters/HmacFilterTest.php

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
namespace Tests\Authentication\Filters;
1515

16-
use CodeIgniter\Encryption\Encryption;
1716
use CodeIgniter\Shield\Entities\AccessToken;
1817
use CodeIgniter\Shield\Entities\User;
1918
use CodeIgniter\Shield\Filters\HmacAuth;
@@ -31,14 +30,6 @@ final class HmacFilterTest extends AbstractFilterTestCase
3130
protected string $alias = 'hmacAuth';
3231
protected string $classname = HmacAuth::class;
3332

34-
protected function setUp(): void
35-
{
36-
parent::setUp();
37-
38-
$authConfig = config('AuthToken');
39-
$authConfig->hmacEncryptionKey = Encryption::createKey();
40-
}
41-
4233
public function testFilterNotAuthorized(): void
4334
{
4435
$result = $this->call('get', 'protected-route');

tests/Authentication/HasHmacTokensTest.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
namespace Tests\Authentication;
1515

16-
use CodeIgniter\Encryption\Encryption;
1716
use CodeIgniter\Shield\Entities\AccessToken;
1817
use CodeIgniter\Shield\Entities\User;
1918
use CodeIgniter\Shield\Models\UserIdentityModel;
@@ -31,9 +30,6 @@ protected function setUp(): void
3130
{
3231
parent::setUp();
3332

34-
$authConfig = config('AuthToken');
35-
$authConfig->hmacEncryptionKey = Encryption::createKey();
36-
3733
$this->user = fake(UserModel::class);
3834
$this->db->table($this->tables['identities'])->truncate();
3935
}
@@ -61,12 +57,12 @@ public function testHmacTokens(): void
6157
// Give the user a couple of access tokens
6258
$token1 = fake(
6359
UserIdentityModel::class,
64-
['user_id' => $this->user->id, 'type' => 'hmac_sha256', 'secret' => 'key1', 'secret2' => 'secretKey1']
60+
['user_id' => $this->user->id, 'type' => 'hmac_sha256', 'secret' => 'key1', 'secret2' => 'd862cd9ddc23e960ca6d45a3e0b64c7509f0c0ef0e5f7b64be8910a6a714c89b83fab95251bbf17f6c84b42c26cf460a28ea969591dc64b1f5c4b323f47615d2e8cbe4c62118001d3274e0f25850b0ac2617bc43119af22c99a1a83072002267177da01f9f37225435e1914be004f4d35a49869b737ed10ab232c1ed1048bb96ef6fb70979dc9c981e17134f4356a938']
6561
);
6662

6763
$token2 = fake(
6864
UserIdentityModel::class,
69-
['user_id' => $this->user->id, 'type' => 'hmac_sha256', 'secret' => 'key2', 'secret2' => 'secretKey2']
65+
['user_id' => $this->user->id, 'type' => 'hmac_sha256', 'secret' => 'key2', 'secret2' => 'd862cd9ddc23e960ca6d45a3e0b64c7509f0c0ef0e5f7b64be8910a6a714c89b83fab95251bbf17f6c84b42c26cf460a28ea969591dc64b1f5c4b323f47615d2e8cbe4c62118001d3274e0f25850b0ac2617bc43119af22c99a1a83072002267177da01f9f37225435e1914be004f4d35a49869b737ed10ab232c1ed1048bb96ef6fb70979dc9c981e17134f4356a938']
7066
);
7167

7268
$tokens = $this->user->hmacTokens();

tests/_support/TestCase.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ abstract class TestCase extends CIUnitTestCase
2525
{
2626
protected function setUp(): void
2727
{
28+
$_ENV['authtoken.hmacEncryptionKey'] = 'hex2bin:178ed94fd0b6d57dd31dd6b22fc601fab8ad191efac165a5f3f30a8ac09d813d';
29+
2830
$this->resetServices();
2931

3032
parent::setUp();

0 commit comments

Comments
 (0)