Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
InMemoryClientCredentials
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
3 / 3
5
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 add
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 verify
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare(strict_types=1);
4
5namespace LambdaTwelve\OneRecord\Auth;
6
7use LambdaTwelve\OneRecord\Rdf\Iri;
8
9/**
10 * Client credentials held in memory with password_hash() hashes: the reference
11 * implementation for tests and bin/serve, and the pattern a host's database
12 * version follows (hash at rest, constant-time verification, no early return
13 * that reveals whether the client id exists).
14 */
15final class InMemoryClientCredentials implements ClientCredentialsVerifier
16{
17    /** @var array<string, array{hash: string, agent: Iri}> */
18    private array $clients = [];
19
20    private readonly string $dummyHash;
21
22    public function __construct()
23    {
24        // Verifying against this when the client id is unknown keeps timing even.
25        $this->dummyHash = password_hash(bin2hex(random_bytes(8)), PASSWORD_DEFAULT);
26    }
27
28    public function add(string $clientId, string $clientSecret, Iri $agent): void
29    {
30        $this->clients[$clientId] = ['hash' => password_hash($clientSecret, PASSWORD_DEFAULT), 'agent' => $agent];
31    }
32
33    public function verify(string $clientId, string $clientSecret): ?Iri
34    {
35        $client = $this->clients[$clientId] ?? null;
36        $valid = password_verify($clientSecret, $client['hash'] ?? $this->dummyHash);
37
38        return $valid && $client !== null ? $client['agent'] : null;
39    }
40}