Concierge
view release on metacpan or search on metacpan
authenticated session.
Between requests, users are restored by `user_key` (typically stored in a
cookie): `restore_user($user_key)` rehydrates the correct object type with
the right data and backend access.
## Component Capabilities
### Authentication â Concierge::Auth
- **Argon2id** password hashing and verification; no plaintext credentials
written to disk
- Random value generators: hex IDs, alphanumeric tokens, UUIDs (v4),
word-passphrases from a system dictionary
- Designed for substitution: swap in any replacement that implements the
same method contract (`checkPwd`, `setPwd`, `resetPwd`, `deleteID`, etc.)
for LDAP, OAuth, or other schemes
### Sessions â Concierge::Sessions
- **Multiple backends**: SQLite (recommended), flat-file, or in-memory text
lib/Concierge.pm view on Meta::CPAN
# === COMPONENT MODULES ===
use Concierge::Auth;
use Concierge::Sessions;
use Concierge::Users;
use Concierge::Desk::User;
# === PARAMETER FILTERS ===
# Shared filters for secure data segregation
# Auth filter - ONLY credentials (user_id + password)
our $auth_data_filter = make_filter(
[qw(user_id password)], # required credentials
[], # accepted - nothing else
[], # excluded - not needed
);
# User data filter - everything EXCEPT credentials
# Handles both minimal input (user_id, moniker) and
# rich input (user_id, moniker, email, phone, bio, etc.)
our $user_data_filter = make_filter(
[qw(user_id moniker)], # required minimum
['*'], # accept ALL other fields, except:
[qw(password confirm_password)], # excluded - security boundary
);
# Session data filter - for populating session with initial data
# Accepts user_id (required for new_session) plus any session fields
# Excludes credentials (never stored in session data)
our $session_data_filter = make_filter(
[qw(user_id)], # required for new_session
['*'], # accept all other fields, except:
[qw(password confirm_password)], # excluded - security boundary
);
# User update filter - for updating existing user records
# No required fields (user_id passed separately as parameter)
# Excludes user_id (identity field), password (use reset_password instead)
our $user_update_filter = make_filter(
lib/Concierge.pm view on Meta::CPAN
return {
success => 1,
message => 'Guest restored',
user => $user,
is_guest => 1,
};
}
}
# Login user: authenticate, create session, assign user_key and store external_key mapping
sub login_user ($self, $credentials, $session_opts={}) {
# Step 0: Get credentials
my $auth_data = $auth_data_filter->($credentials);
return { success => 0, message => 'Missing user_id or password' }
unless $auth_data;
my $user_id = $auth_data->{user_id};
my $password = $auth_data->{password};
# Step 1: Get user from database
my $user_result = $self->users->get_user($user_id);
return { success => 0, message => 'User not found' }
unless $user_result->{success};
lib/Concierge.pm view on Meta::CPAN
Applications interact only with Concierge and the L<Concierge::Desk::User> objects
it returns. The component modules are never exposed directly.
=head2 What the Suite Provides
Concierge handles orchestration -- coordinating components, managing the
user_key mapping, and returning consistent structured results. The
capabilities of the suite live in the three components:
B<Authentication> (L<Concierge::Auth>): Argon2id password hashing and
verification; no plaintext credentials are ever written to disk. Also
provides random token, UUID, word-passphrase, and hex-ID generators. The
component is substitutable: any replacement implementing the same method
contract (C<checkPwd>, C<setPwd>, C<resetPwd>, etc.) can replace it for
LDAP, OAuth, or any other scheme.
B<Sessions> (L<Concierge::Sessions>): Full session lifecycle -- creation,
retrieval, expiry, and cleanup -- with SQLite, file, or in-memory backends.
Sessions carry arbitrary key/value data. A single-session-per-user policy
is enforced: creating a new session automatically removes any prior session
for that user. Expired sessions are cleaned up each time a desk is opened.
lib/Concierge.pm view on Meta::CPAN
Assigned a unique identifier only. No session, no stored data. Suitable for
anonymous tracking (e.g., cookies).
=item B<Guest> -- C<checkin_guest()>
Assigned an identifier and a session. Can store temporary data (e.g., a
shopping cart). No authentication or persistent user record.
=item B<Logged-in user> -- C<login_user()>
Authenticated with credentials. Has a session, persistent user data, and
full access to the User object's data methods.
=back
A guest can be converted to a logged-in user with C<login_guest()>,
transferring any session data accumulated during the guest session.
=head2 User Keys
Each active user (guest or logged-in) is tracked by a I<user_key> -- a
lib/Concierge.pm view on Meta::CPAN
my $result = $concierge->checkin_guest(\%session_opts);
my $user = $result->{user}; # Concierge::Desk::User (guest)
Creates a guest with a generated identifier and a session. The optional
C<%session_opts> hashref may include C<timeout> (in seconds; defaults to
1800).
=head3 login_user
my $result = $concierge->login_user(\%credentials, \%session_opts);
my $user = $result->{user}; # Concierge::Desk::User (logged-in)
Authenticates C<user_id> and C<password> from C<%credentials>, retrieves
the user's data record, creates a session, and returns a fully-equipped
User object. If the user already has an active session, the previous
session is replaced.
=head3 restore_user
my $result = $concierge->restore_user($user_key);
my $user = $result->{user}; # Concierge::Desk::User (guest or logged-in)
Reconstructs a User object from a C<user_key> (typically stored in a cookie
lib/Concierge.pm view on Meta::CPAN
If the session has expired, the stale mapping entry is cleaned up and the
method returns failure. The application can then redirect to login or create
a new guest as appropriate.
Returns C<< { success => 1, user => $user } >> on success. Guest restores
also include C<< is_guest => 1 >>.
=head3 login_guest
my $result = $concierge->login_guest(\%credentials, $guest_user_key);
my $user = $result->{user}; # Concierge::Desk::User (logged-in)
Converts a guest to a logged-in user. Authenticates with C<%credentials>,
transfers any data from the guest's session to the new session, then
deletes the guest session and removes the guest's user_key mapping.
=head3 logout_user
my $result = $concierge->logout_user($session_id);
Deletes the session and removes the user_key mapping entry.
=head2 Admin Operations
lib/Concierge.pm view on Meta::CPAN
=item C<$auth_data_filter> -- extracts only C<user_id> and C<password>
=item C<$user_data_filter> -- extracts everything except C<password>
=item C<$session_data_filter> -- extracts C<user_id> plus non-credential fields
=item C<$user_update_filter> -- excludes C<user_id> and C<password> from updates
=back
These ensure that credentials never leak into user data stores and that
identity fields cannot be changed via update operations.
=head1 EXTENSIBILITY
=head2 Component Substitution
Each identity core component can be replaced with a drop-in alternative as
long as the replacement implements the methods Concierge calls on it.
B<Auth> -- Concierge calls:
t/04-user-operations.t view on Meta::CPAN
subtest 'list_users with include_data' => sub {
my $result = $concierge->list_users('', { include_data => 1 });
ok $result->{success}, 'list_users with data succeeds';
ref_ok $result->{users}, 'HASH', 'users is hash';
ok exists $result->{users}{alice}, 'alice data included';
is $result->{users}{alice}{moniker}, 'Alice', 'alice data correct';
};
subtest 'verify_password checks credentials' => sub {
my $result = $concierge->verify_password('alice', 'secret123');
ok $result->{success}, 'correct password verified';
$result = $concierge->verify_password('alice', 'wrongpass');
ok !$result->{success}, 'incorrect password rejected';
};
subtest 'reset_password changes password' => sub {
my $result = $concierge->reset_password('alice', 'newsecret456');
ok $result->{success}, 'reset_password succeeds';
( run in 0.935 second using v1.01-cache-2.11-cpan-22024b96cdf )