NAME

Concierge::Auth::Pwd - Password-file Concierge::Auth backend using Crypt::Passphrase

VERSION

v0.5.0

SYNOPSIS

use Concierge::Auth::Pwd;

# Initialize with a password file
my $auth = Concierge::Auth::Pwd->new( file => '/path/to/auth.pwd' );

# Or without a file (generators and utilities only)
my $auth = Concierge::Auth::Pwd->new( no_file => 1 );

# --- Concierge::Auth::Base contract methods ---

my $result = $auth->enroll('alice', 'secret123');
my $result = $auth->authenticate('alice', 'secret123');
my $result = $auth->is_id_known('alice');
my $result = $auth->change_credentials('alice', 'newsecret456');
my $result = $auth->revoke('alice');

# --- Backend-specific methods (password-file only) ---

my ($ok, $msg) = $auth->setFile('/path/to/other.pwd');
my $hash        = $auth->encryptPwd('secret123');

# Generate tokens and random values (inherited from Concierge::Auth::Base)
my ($uuid, $msg)   = $auth->gen_uuid();           # v4 UUID
my ($id, $msg)     = $auth->gen_random_id();       # 40-char hex ID
my ($token, $msg)  = $auth->gen_random_token(32);
my ($string, $msg) = $auth->gen_random_string(16);
my ($phrase, $msg) = $auth->gen_word_phrase(4, 4, 7, '-');

DESCRIPTION

Concierge::Auth::Pwd is the built-in password-file backend for Concierge::Auth. It implements the Concierge::Auth::Base contract (authenticate, is_id_known, enroll, change_credentials, revoke) on top of a password store backed by Crypt::Passphrase with Argon2 encoding and Bcrypt validation for legacy password migration. Passwords are stored in a tab-separated file with file-locking for concurrent access.

Token and random value generation (gen_uuid, gen_random_id, gen_random_token, gen_random_string, gen_word_phrase) is not implemented by this module -- it is inherited from Concierge::Auth::Base's default implementations, which delegate to Concierge::Auth::Generators (using Crypt::PRNG for cryptographically secure random output). See "The Generators Guarantee" in Concierge::Auth::Base.

Concierge::Auth::Pwd is one backend of Concierge::Auth, the authentication component of the Concierge suite, alongside Concierge::Sessions (session management) and Concierge::Users (user data storage). It can also be used standalone.

Two Method Layers

This module itself defines two layers of methods:

  • Contract methods (authenticate, is_id_known, enroll, change_credentials, revoke) -- the interface defined by Concierge::Auth::Base. These are what Concierge calls, and every Concierge::Auth backend (this one, or an alternative such as an LDAP-backed backend) implements them. Password-file I/O and validation are written directly inline in these methods -- there are no intermediate backend-primitive methods to hop through. They return the { success = 1|0, ... }> hashref convention used throughout the rest of the Concierge suite. validatePwd is kept as a small shared helper since both enroll and change_credentials need it; it also returns the hashref convention.

  • Backend-specific methods (file-management and encryption) -- specific to how this backend manages its password file, independent of the contract logic above. Other backends are not expected to implement these, and application code that wants to remain backend-agnostic should prefer the contract methods. These retain the original wantarray-sensitive (bool, message) dual-return convention: $value in scalar context, ($value, $message) in list context.

A third set of methods -- token/random value generation -- is available on every instance but is not defined in this module at all; see "DESCRIPTION" above.

CONSTRUCTOR

new

my $auth = Concierge::Auth::Pwd->new(%args);

Creates a new backend object. The Crypt::Passphrase encoder (Argon2) is initialized immediately.

Arguments:

file -- path to the password file. Created if it does not exist. File permissions are set to 0600. Croaks if the file cannot be opened or created.
no_file -- if true, skip file setup. The object can still generate tokens and hash passwords, but cannot perform ID or password checks.

If neither file nor no_file is provided, the object is still created (with a warning), but file-dependent methods will fail.

CONTRACT METHODS

authenticate

my $result = $auth->authenticate($user_id, $password);

Verifies that $password is valid for $user_id. Pure check, no side effects. Malformed/empty IDs or passwords are rejected without a file scan; a wrong-length password otherwise simply fails to match the stored hash, so no separate format check is applied to it here.

Returns { success = 1 }> or { success = 0, message => '...' }>.

is_id_known

my $result = $auth->is_id_known($user_id);

Checks whether $user_id has a record in the password file. Empty, malformed, or missing IDs and missing files are all simply "not known" for this backend -- there is no failure branch short of a genuine I/O error on a file that does exist.

Returns { success = 1, known => 1|0 }>.

enroll

my $result = $auth->enroll($user_id, $password);

Creates a new password record for $user_id. $user_id must meet the length and character constraints (this is the only contract method that enforces ID format policy, since it's the only one establishing a new ID). Fails if the ID already exists (use change_credentials to change an existing password).

Returns { success = 1, user_id => $user_id, status => 'created' }> or { success = 0, message => '...' }>.

change_credentials

my $result = $auth->change_credentials($user_id, $new_password);

Replaces the stored password hash for an existing $user_id. Fails if the ID is not found.

Returns { success = 1, user_id => $user_id }> or { success = 0, message => '...' }>.

revoke

my $result = $auth->revoke($user_id);

Removes the password record for $user_id. Fails if the ID is not found. No ID format policy check is applied -- a malformed-but-nonempty ID simply fails to match any record on file.

Returns { success = 1, user_id => $user_id }> or { success = 0, message => '...' }>.

validatePwd

my $result = $auth->validatePwd($password);

Checks whether $password meets the length constraints. Shared by enroll and change_credentials, both of which establish a new credential value; not used by authenticate, since a wrong-length submitted password simply fails to match the stored hash.

Returns { success = 1 }> or { success = 0, message => '...' }>.

BACKEND-SPECIFIC METHODS

File Management

setFile

my ($ok, $msg) = $auth->setFile($path);

Sets (or changes) the password file path. Creates the file if it does not exist and sets permissions to 0600.

rmFile

my ($file, $msg) = $auth->rmFile();

Deletes the password file and clears the stored path. In list context, returns the deleted file path on success.

clearFile

my ($ok, $msg) = $auth->clearFile();

Removes and re-creates the password file, effectively deleting all records.

pfile

my ($file, $msg) = $auth->pfile();

Returns the path to the configured password file.

Utilities

encryptPwd

my $hash = $auth->encryptPwd($password);

Returns the Argon2 hash of $password. Validates password constraints first.

Token and Value Generation

gen_uuid, gen_random_id, gen_random_token, gen_random_string, and gen_word_phrase are available on every instance but are not implemented in this module -- they are inherited default implementations from Concierge::Auth::Base, which delegate to Concierge::Auth::Generators and use its dual-return convention: ($value, $message) in list context, $value in scalar context. See "GENERATOR METHODS" in Concierge::Auth::Base for the full list and "The Generators Guarantee" in Concierge::Auth::Base for why this backend does not need to (but could) override them.

SEE ALSO

Concierge::Auth::Base -- the backend contract this module implements

Concierge::Auth::Generators -- functional interface to the generators

Concierge::Sessions, Concierge::Users -- companion Concierge components

Crypt::Passphrase, Crypt::PRNG

AUTHOR

Bruce Van Allen <bva@cruzio.com>

LICENSE

This module is free software; you can redistribute it and/or modify it under the terms of the Artistic License 2.0.