NAME
Uniform::HTTP::Auth - HTTP authentication without an HTTP framework
SYNOPSIS
use Uniform::HTTP::Auth;
my $auth = Uniform::HTTP::Auth->new(
origin => 'https://example.com:443',
credentials => {
username => 'user',
password => 'secret',
},
);
my $result = $auth->prepare_authentication(
challenge_headers => [
'Digest realm="Members", nonce="abc", qop="auth", algorithm=SHA-256',
],
method => 'GET',
request_target => '/private',
);
my $value = $result->{value};
DESCRIPTION
Uniform::HTTP::Auth prepares HTTP authentication field values.
It supports:
Basic
Bearer
Digest
It does not send requests, receive 401 responses, retry requests, manage connections, or choose an HTTP framework.
The normal flow is:
server challenge
|
v
Uniform::HTTP::Auth
|
v
authentication field value
|
v
your HTTP client or server
The calling HTTP implementation decides whether the value belongs in Authorization or Proxy-Authorization and whether a request should be retried.
SIMPLE USERNAME AND PASSWORD
For one server, store credentials on the auth object:
my $auth = Uniform::HTTP::Auth->new(
origin => 'https://example.com:443',
credentials => {
username => 'user',
password => 'secret',
},
);
The credentials are bound to that origin.
When a challenge arrives:
my $result = $auth->prepare_authentication(
challenge_headers => \@www_authenticate,
method => 'GET',
request_target => '/private',
);
If a supported challenge can be satisfied, $result contains:
{
scheme => 'digest',
value => 'Digest username="...", ...',
challenge => $challenge,
}
Use $result->{value} as the complete authentication field value.
The configured default preference is:
digest
bearer
basic
You can choose another order with schemes.
BEARER TOKENS
For Bearer authentication:
my $auth = Uniform::HTTP::Auth->new(
origin => 'https://api.example.com:443',
credentials => {
token => $token,
},
);
The token is treated as opaque data. Uniform::HTTP::Auth does not obtain, refresh, decode, or validate OAuth tokens or JWTs.
USING A REQUEST OBJECT
prepare_authentication() can read the request information from a Uniform::HTTP::Request object:
my $result = $auth->prepare_authentication(
challenge_headers => \@www_authenticate,
request => $request,
);
It reads method() and target().
A body is read only when has_buffered_body() is true. Authentication never drains a streaming body.
Explicit method, request_target, and entity_body arguments override values from the request object.
CONSTRUCTOR
new
my $auth = Uniform::HTTP::Auth->new(
origin => $origin,
credentials => $credentials,
schemes => [qw(digest basic)],
);
origin
A normalized origin such as:
https://example.com:443
Static credentials require an origin and are never used for a different origin.
credentials
For Basic or Digest:
{
username => 'user',
password => 'secret',
}
For Bearer:
{
token => $token,
}
A hash may contain both forms.
For applications with a credential store, credentials may instead be a callback. See "DYNAMIC CREDENTIAL LOOKUP".
schemes
An optional array reference containing enabled schemes in preference order.
The default is:
[qw(digest bearer basic)]
MAIN METHODS
prepare_authentication
my $result = $auth->prepare_authentication(
challenge_headers => \@values,
method => 'GET',
request_target => '/private',
);
This is the main application method.
It parses the challenges, chooses a supported scheme, finds credentials, and constructs the authentication value.
It returns undef when no challenge can be satisfied.
Digest needs method and request_target. entity_body is used only for Digest qop=auth-int.
This method performs no network I/O.
parse_challenges
my $challenges = $auth->parse_challenges(@header_values);
Parses complete WWW-Authenticate or Proxy-Authenticate values.
It returns an array reference in wire order. Unknown schemes are preserved. Malformed remote challenges are returned as malformed data rather than causing an exception merely because the server sent bad input.
select
my $challenge = $auth->select($challenges);
Returns the best usable challenge according to the configured scheme order, or undef when none is usable.
This method only selects a challenge. It does not look up credentials.
schemes
Returns a new array reference containing the configured scheme names.
DYNAMIC CREDENTIAL LOOKUP
Reusable HTTP libraries and applications with a credential store can supply a callback:
my $auth = Uniform::HTTP::Auth->new(
credentials => sub {
my ($context) = @_;
return $store->lookup(
$context->{origin},
$context->{realm},
$context->{scheme},
);
},
);
The callback receives:
{
scheme => 'digest',
origin => 'https://example.com:443',
realm => 'Members',
challenge => $challenge,
}
Return undef when credentials are unavailable.
For Basic and Digest return:
{
username => 'user',
password => 'secret',
}
For Bearer return:
{
token => $token,
}
DIGEST SUPPORT
Digest supports:
MD5 and MD5-sess
SHA-256 and SHA-256-sess
SHA-512/256 and SHA-512/256-sess
qop=authqop=auth-intUTF-8
userhashstale nonces and nonce-count state
MD5 remains available for compatibility with older servers.
ERRORS
Programmer mistakes throw exceptions. Examples include bad constructor arguments, invalid credential values, and invalid callback results.
Bad challenge data received from a remote server is represented as malformed challenge data so callers can inspect it safely.
SECURITY
Basic authentication only encodes credentials with Base64. It should normally be used over TLS.
Bearer tokens are credentials and should be protected accordingly.
Digest is an authentication mechanism, not transport encryption. Legacy MD5 Digest is supported for interoperability but should not be preferred when a stronger option is available.
LOWER-LEVEL MODULES
Most applications should use this module.
The lower-level calculation modules are available when needed:
SEE ALSO
Uniform::HTTP, Uniform::HTTP::Request.
The detailed authentication contract is in docs/AUTH-SPEC.md.
AUTHOR
Joshua S. Day <HAX@cpan.org>
LICENSE
This software is available under the MIT License.