Skip to content

Commit 752f909

Browse files
committed
feat: digest auth - docs
1 parent 689b890 commit 752f909

File tree

7 files changed

+157
-12
lines changed

7 files changed

+157
-12
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![Latest Stable Version](https://poser.pugx.org/chr15k/php-auth-generator/v)](https://packagist.org/packages/chr15k/php-auth-generator) [![Total Downloads](https://poser.pugx.org/chr15k/php-auth-generator/downloads)](https://packagist.org/packages/chr15k/php-auth-generator) [![Latest Unstable Version](https://poser.pugx.org/chr15k/php-auth-generator/v/unstable)](https://packagist.org/packages/chr15k/php-auth-generator) [![License](https://poser.pugx.org/chr15k/php-auth-generator/license)](https://packagist.org/packages/chr15k/php-auth-generator) [![PHP Version Require](https://poser.pugx.org/chr15k/php-auth-generator/require/php)](https://packagist.org/packages/chr15k/php-auth-generator)
44

5-
A PHP library that focuses exclusively on **generating** HTTP authentication tokens, including Basic Auth, Bearer tokens, and JWTs with a fluent API. Built with zero dependencies, it's lightweight and adds token creation capabilities without bloating your project.
5+
A PHP library that focuses exclusively on **generating** HTTP authentication tokens, including Basic Auth, Bearer tokens, Digest Auth, and JWTs with a fluent API. Built with zero dependencies, it's lightweight and adds token creation capabilities without bloating your project.
66

77
> [!IMPORTANT]
88
> This package is designed solely for **generating** authentication tokens. It does not include any token decoding, validation, or verification functionality.

docs/API_CHEATSHEET.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,13 @@ $headers = AuthGenerator::jwt()
144144
->claim('user_id', 123)
145145
->toArray();
146146

147+
// Digest Auth headers
148+
$headers = AuthGenerator::digestAuth()
149+
->username('user')
150+
->password('pass')
151+
->realm('example.com')
152+
->toArray();
153+
147154
// Fluent formatting with toHeader() method
148155
$header = AuthGenerator::jwt()
149156
->key('secret-key')
@@ -167,3 +174,14 @@ The following algorithms are supported for JWT tokens:
167174
| Algorithm::ES384 | ECDSA using P-384 curve and SHA-384 |
168175
| Algorithm::ES256K | ECDSA using secp256k1 curve and SHA-256 |
169176
| Algorithm::EdDSA | Edwards-curve Digital Signature Algorithm (EdDSA) |
177+
178+
## Digest Auth Algorithms
179+
180+
The following algorithms are supported for Digest Authentication:
181+
182+
| Algorithm | Description |
183+
|-----------|-------------|
184+
| DigestAlgorithm::MD5 | MD5 algorithm (RFC 2617) |
185+
| DigestAlgorithm::MD5_SESS | MD5-sess variant with client nonce (RFC 2617) |
186+
| DigestAlgorithm::SHA256 | SHA-256 algorithm (RFC 7616) |
187+
| DigestAlgorithm::SHA256_SESS | SHA-256-sess variant with client nonce (RFC 7616) |

docs/USER_GUIDE.md

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,14 @@ $header = AuthGenerator::bearerToken()
100100

101101
## Digest Auth
102102

103-
Digest Authentication is an authentication mechanism that improves upon Basic Authentication by avoiding sending the password in plaintext over the network. It uses a challenge-response mechanism and MD5 cryptographic hashing.
103+
Digest Authentication is an authentication mechanism that improves upon Basic Authentication by avoiding sending the password in plaintext over the network. It uses a challenge-response mechanism and cryptographic hashing (MD5 or SHA-256).
104+
105+
The Digest authentication process typically involves:
106+
1. A server challenge containing a nonce value
107+
2. A client response with a cryptographic hash that proves password knowledge without revealing it
108+
3. The response includes various components like realm, nonce, URI, and algorithm details
109+
110+
This implementation provides a convenient way to generate properly formatted Digest Auth tokens for HTTP requests. It supports all RFC 2617 and RFC 7616 algorithm variants including MD5, MD5-sess, SHA-256, and SHA-256-sess.
104111

105112
### Generating a Digest Auth Token
106113

@@ -327,7 +334,7 @@ $response = $client->request('GET', 'https://api.example.com/resources');
327334
use Chr15k\AuthGenerator\AuthGenerator;
328335
use Illuminate\Support\Facades\Http;
329336

330-
// Make request with Laravel HTTP client
337+
// Make request with Laravel HTTP client and Bearer token
331338
$response = Http::withHeaders(
332339
AuthGenerator::bearerToken()
333340
->length(48)
@@ -336,6 +343,19 @@ $response = Http::withHeaders(
336343
'Accept' => 'application/json',
337344
])
338345
)->get('https://api.example.com/resources');
346+
347+
// Using Digest Authentication
348+
$response = Http::withHeaders(
349+
AuthGenerator::digestAuth()
350+
->username('api_user')
351+
->password('secure_password')
352+
->realm('api.example.com')
353+
->uri('/protected-resource')
354+
->method('GET')
355+
->toArray([
356+
'Accept' => 'application/json',
357+
])
358+
)->get('https://api.example.com/protected-resource');
339359
```
340360

341361
## Advanced Usage
@@ -344,15 +364,28 @@ If you need more control, you can access the underlying generator instance.
344364

345365
```php
346366
use Chr15k\AuthGenerator\AuthGenerator;
367+
use Chr15k\AuthGenerator\Enums\DigestAlgorithm;
347368

348-
// Get the generator instance
369+
// Get the generator instance (Basic Auth example)
349370
$generator = AuthGenerator::basicAuth()
350371
->username('user')
351372
->password('pass')
352373
->build();
353374

354375
// Now you can work directly with the generator
355376
$token = $generator->generate();
377+
378+
// Advanced Digest Auth example
379+
$digestGenerator = AuthGenerator::digestAuth()
380+
->username('user')
381+
->password('pass')
382+
->realm('example.com')
383+
->uri('/api/resource')
384+
->algorithm(DigestAlgorithm::SHA256)
385+
->build();
386+
387+
// Generate the digest token directly
388+
$digestToken = $digestGenerator->generate();
356389
```
357390

358391

src/Builders/DigestAuthBuilder.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,76 @@
1111
use Chr15k\AuthGenerator\Generators\DigestAuth as DigestAuthGenerator;
1212
use SensitiveParameter;
1313

14+
/**
15+
* Builder for generating HTTP Digest Authentication tokens.
16+
*
17+
* Digest Authentication improves upon Basic Authentication by avoiding transmission
18+
* of the password in plaintext. It uses a challenge-response mechanism and various
19+
* cryptographic algorithms (MD5, SHA-256) including their session variants.
20+
*/
1421
final class DigestAuthBuilder implements Builder
1522
{
23+
/**
24+
* The username for authentication.
25+
*/
1626
private string $username = '';
1727

28+
/**
29+
* The password for authentication.
30+
*/
1831
private string $password = '';
1932

33+
/**
34+
* The algorithm used for digest calculation.
35+
*/
2036
private DigestAlgorithm $algorithm = DigestAlgorithm::MD5;
2137

38+
/**
39+
* The authentication realm.
40+
*/
2241
private string $realm = '';
2342

43+
/**
44+
* The HTTP method for the request.
45+
*/
2446
private string $method = 'GET';
2547

48+
/**
49+
* The URI of the request.
50+
*/
2651
private string $uri = '/';
2752

53+
/**
54+
* The server nonce value.
55+
*/
2856
private string $nonce = '';
2957

58+
/**
59+
* The nonce count.
60+
*/
3061
private string $nc = '';
3162

63+
/**
64+
* The client nonce value.
65+
*/
3266
private string $cnonce = '';
3367

68+
/**
69+
* The quality of protection. Common values: 'auth', 'auth-int'.
70+
*/
3471
private string $qop = '';
3572

73+
/**
74+
* The opaque server value.
75+
*/
3676
private string $opaque = '';
3777

78+
/**
79+
* Initialize a new DigestAuthBuilder with random nonce values.
80+
*/
3881
public function __construct()
3982
{
83+
// Generate secure random nonce values
4084
$this->nonce = bin2hex(random_bytes(16));
4185
$this->cnonce = bin2hex(random_bytes(8));
4286
}

src/DataTransfer/DigestAuthData.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@
99

1010
/**
1111
* @internal
12+
*
13+
* Data transfer object containing all the parameters needed for Digest Authentication.
14+
*
15+
* This class encapsulates all the components required to generate a Digest Auth token:
16+
* - username and password for authentication
17+
* - realm, nonce, and algorithm as provided by the server challenge
18+
* - method and URI from the HTTP request
19+
* - nc (nonce count), cnonce (client nonce), qop (quality of protection) for auth-int
20+
* - opaque value as provided by the server
21+
* - entityBody for auth-int quality of protection (optional)
1222
*/
1323
final readonly class DigestAuthData
1424
{

src/Enums/DigestAlgorithm.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,29 @@
66

77
use Closure;
88

9+
/**
10+
* Represents the algorithms available for Digest Authentication.
11+
*
12+
* These algorithms are used to generate the cryptographic digest responses
13+
* in HTTP Digest Authentication according to RFC 2617 and RFC 7616.
14+
*
15+
* - MD5: Original algorithm from RFC 2617
16+
* - MD5_SESS: Session variant of MD5 that includes client nonce
17+
* - SHA256: More secure SHA-256 algorithm from RFC 7616
18+
* - SHA256_SESS: Session variant of SHA-256 that includes client nonce
19+
*/
920
enum DigestAlgorithm: string
1021
{
1122
case MD5 = 'MD5';
1223
case MD5_SESS = 'MD5-sess';
1324
case SHA256 = 'SHA-256';
1425
case SHA256_SESS = 'SHA-256-sess';
1526

27+
/**
28+
* Get the PHP hash algorithm identifier for this digest algorithm.
29+
*
30+
* @return string The hash algorithm name compatible with PHP's hash functions
31+
*/
1632
public function algo(): string
1733
{
1834
return match ($this) {
@@ -21,11 +37,22 @@ public function algo(): string
2137
};
2238
}
2339

40+
/**
41+
* Get a closure that can be used to hash data with this algorithm.
42+
*
43+
* @return Closure A function that accepts a string and returns its hash
44+
*/
2445
public function func(): Closure
2546
{
2647
return fn (string $data): string => hash($this->algo(), $data);
2748
}
2849

50+
/**
51+
* Determines if this is a session variant algorithm (MD5-sess or SHA-256-sess).
52+
* Session variants incorporate the client nonce in the calculation.
53+
*
54+
* @return bool True if this is a session variant, false otherwise
55+
*/
2956
public function isSessionVariant(): bool
3057
{
3158
return str_ends_with($this->value, '-sess');

src/Generators/DigestAuth.php

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@
1111

1212
/**
1313
* @internal
14+
*
15+
* Generates an HTTP Digest Authentication token based on provided data.
16+
*
17+
* This class implements the token generation process according to RFC 2617 and RFC 7616,
18+
* supporting various digest algorithms (MD5, SHA-256) and their session variants.
19+
*
20+
* The Digest Authentication method improves security compared to Basic Auth by not
21+
* sending the password over the network in plaintext. Instead, it uses a cryptographic
22+
* hash of the username, password, realm, and other components to prove the client
23+
* knows the credentials without revealing them.
24+
*
25+
* @see https://datatracker.ietf.org/doc/html/rfc2617 Original Digest Auth specification
26+
* @see https://datatracker.ietf.org/doc/html/rfc7616 SHA-256 and other improvements
1427
*/
1528
final readonly class DigestAuth implements Generator
1629
{
@@ -45,27 +58,27 @@ public function generate(): string
4558

4659
private function generateResponse(): string
4760
{
48-
$func = $this->data->algorithm->func();
61+
$hash = $this->data->algorithm->func();
4962

50-
$ha1 = $func(sprintf('%s:%s:%s', $this->data->username, $this->data->realm, $this->data->password));
63+
$ha1 = $hash(sprintf('%s:%s:%s', $this->data->username, $this->data->realm, $this->data->password));
5164

5265
if ($this->data->algorithm->isSessionVariant()) {
53-
$ha1 = $func(sprintf('%s:%s:%s', $ha1, $this->data->nonce, $this->data->cnonce));
66+
$ha1 = $hash(sprintf('%s:%s:%s', $ha1, $this->data->nonce, $this->data->cnonce));
5467
}
5568

5669
if ($this->data->qop === 'auth-int') {
57-
$ha2 = $func(sprintf(
70+
$ha2 = $hash(sprintf(
5871
'%s:%s:%s',
5972
$this->data->method,
6073
$this->data->uri,
61-
$func($this->data->entityBody)
74+
$hash($this->data->entityBody)
6275
));
6376
} else {
64-
$ha2 = $func(sprintf('%s:%s', $this->data->method, $this->data->uri));
77+
$ha2 = $hash(sprintf('%s:%s', $this->data->method, $this->data->uri));
6578
}
6679

6780
if ($this->data->qop !== '' && $this->data->qop !== '0') {
68-
return $func(sprintf(
81+
return $hash(sprintf(
6982
'%s:%s:%s:%s:%s:%s',
7083
$ha1,
7184
$this->data->nonce,
@@ -76,6 +89,6 @@ private function generateResponse(): string
7689
));
7790
}
7891

79-
return $func("{$ha1}:{$this->data->nonce}:{$ha2}");
92+
return $hash("{$ha1}:{$this->data->nonce}:{$ha2}");
8093
}
8194
}

0 commit comments

Comments
 (0)