package Concierge::Auth v0.5.2;
use v5.36;

# ABSTRACT: Factory/dispatcher for Concierge::Auth backends

use Carp qw/croak/;

sub new {
    my ($class, %args) = @_;
    my $backend_class = delete $args{backend_class}
        or croak "Concierge::Auth->new requires a 'backend_class' class name";

    eval "require $backend_class; 1"
        or croak "Cannot load Auth backend $backend_class: $@";

    my $backend;
    eval { $backend = $backend_class->new(%args); };
    croak "Failed to initialize backend $backend_class: $@" if $@;

    return $backend;
}

1;

__END__

=head1 NAME

Concierge::Auth - Factory/dispatcher for Concierge::Auth backends

=head1 VERSION

v0.5.2

=head1 SYNOPSIS

    use Concierge::Auth;

    my $auth = Concierge::Auth->new(
        backend_class => 'Concierge::Auth::Pwd',
        file          => '/path/to/auth.pwd',
    );

    # $auth is a Concierge::Auth::Pwd instance -- use it directly:

    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');

=head1 DESCRIPTION

C<Concierge::Auth> is a thin factory that turns a backend class name
into a live, ready-to-use backend instance. Given a fully-qualified
class name (e.g. C<Concierge::Auth::Pwd>) and whatever arguments that
backend needs, C<new> C<require>s the named module, constructs it, and
hands back the instance directly -- there is no wrapper object or
delegation layer. A conforming backend already fully implements the
L<Concierge::Auth::Base> contract, so the returned object responds
directly to C<authenticate>/C<is_id_known>/C<enroll>/
C<change_credentials>/C<revoke>, and to the
L<Concierge::Auth::Generators> methods, with no extra indirection.

The named backend module is C<require>d dynamically inside C<new> --
this module does not C<use> any concrete backend at compile time. A
desk configured for C<Concierge::Auth::LDAP>, for example, never loads
C<Concierge::Auth::Pwd> at all.

All remaining arguments passed to C<new> (e.g. C<file> for C<::Pwd>;
C<host>/C<bind_dn>/C<password> for a hypothetical C<::LDAP>) are passed
straight through to the backend's own C<new> unexamined --
C<Concierge::Auth> has no opinion about what any given backend needs.

C<Concierge::Auth> performs I<no> guessing about C<backend_class>: it
must already be a fully-qualified, resolved class name, not a friendly
short name like C<'pwd'>. There is no default backend and no
C<lc()>/string-mapping performed here. When used as a component of a
Concierge desk, resolving a friendly name (such as a config file's
C<auth.backend> setting) to a fully-qualified class name, and
validating that every setting the chosen backend needs is present, is
a desk-build-time concern handled by L<Concierge::Desk::Setup> (see its
backend catalog, C<%AUTH_BACKENDS>), not by this module -- by the time
C<Concierge::Auth-E<gt>new> is called, that resolution has already
happened once, not on every call. Used standalone, outside a Concierge
desk, callers simply supply the fully-qualified class name themselves.

=head1 CONSTRUCTOR

=head2 new

    my $auth = Concierge::Auth->new( backend_class => $class_name, %backend_args );

Loads C<$class_name> (via C<require>) and calls C<< $class_name->new(%backend_args) >>,
returning the resulting backend instance.

Croaks if:

=over 4

=item * C<backend_class> is missing.

=item * The named class cannot be loaded (nonexistent module, syntax
error, missing dependency, etc).

=item * The backend's own C<new> dies.

=back

=head1 SEE ALSO

L<Concierge::Auth::Base> -- the contract every backend must implement

L<Concierge::Auth::Pwd> -- the built-in password-file backend

L<Concierge::Desk::Setup> -- resolves friendly backend names (e.g.
C<'pwd'>) to fully-qualified classes and validates backend-specific
required settings at desk-build time

L<Concierge::Sessions>, L<Concierge::Users> -- companion Concierge
components

=head1 AUTHOR

Bruce Van Allen <bva@cruzio.com>

=head1 LICENSE

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

=cut
