Mojolicious-Plugin-Fondation-Auth
view release on metacpan or search on metacpan
lib/Mojolicious/Plugin/Fondation/Auth/Provider/DBIx.pm view on Meta::CPAN
package Mojolicious::Plugin::Fondation::Auth::Provider::DBIx;
$Mojolicious::Plugin::Fondation::Auth::Provider::DBIx::VERSION = '0.04';
# ABSTRACT: DBIx::Class-backed authentication provider for Fondation::Auth
use Mojo::Base 'Mojolicious::Plugin::Fondation::Auth::Provider', -signatures;
has 'model'; # model name (e.g. 'user')
has 'username_column'; # default: 'username'
has 'password_column'; # default: 'password'
# ââ Constructor âââââââââââââââââââââââââââââââââââââââââââââââââââââââ
sub new ($class, %args) {
my $self = $class->SUPER::new(%args);
$self->username_column('username') unless $self->username_column;
$self->password_column('password') unless $self->password_column;
$self->name('dbix');
$self->log->debug(
$self->model
? "DBIx provider initialized for model '$self->{model}'"
: "DBIx provider initialized (model will be resolved from model_list)"
);
return $self;
}
# ââ Authentication ââââââââââââââââââââââââââââââââââââââââââââââââââââ
sub validate_user ($self, $c, $username, $password, $extra = {}) {
return undef unless $username && $password;
my $schema = $self->_schema($c);
return undef unless $schema;
my $source = $self->_source($c);
return undef unless $source;
my $user = $schema->resultset($source)->search({
$self->username_column => $username,
})->single;
return undef unless $user;
if ($user->check_password($password)) {
$self->log->debug("Authentication successful for '$username'");
return $user->id // $username;
}
$self->log->info("Authentication failed for '$username' (invalid password)");
return undef;
}
# ââ Load user âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
sub load_user ($self, $app, $uid) {
#$self->log->debug("load_user called for uid='$uid'");
# The Authentication plugin calls load_user with the controller as
# first argument, not the Mojolicious app. Use it directly.
my $c = ref($app) ? $app : $app->build_controller;
my $schema = $self->_schema($c);
return undef unless $schema;
my $source = $self->_source($c);
return undef unless $source;
my $user = $schema->resultset($source)->find($uid);
return undef unless $user;
my $result = {
uid => $user->id // $uid,
username => $user->get_column($self->username_column) // $uid,
provider => 'dbix',
};
# Copy all user fields except the password column
my $pass_col = $self->password_column;
for my $col ($user->result_source->columns) {
next if $col eq $pass_col;
$result->{$col} = $user->get_column($col);
}
return $result;
}
# ââ Login form ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
sub auth_form ($self, $c) {
my $template_file = $c->app->home->child('templates/auth/login.html.ep');
lib/Mojolicious/Plugin/Fondation/Auth/Provider/DBIx.pm view on Meta::CPAN
HTML
}
# ââ Internal ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
sub _schema ($self, $c) {
return $self->{_schema} if $self->{_schema};
my $be = $c->backend_config;
return undef unless $be;
my $schema_class = $be->{schema_class};
return undef unless $schema_class;
$self->{_schema} = $schema_class->connect(
$be->{dsn}, $be->{user}, $be->{pass}, $be->{dbi_attrs},
);
$self->log->debug(
sprintf("DBIx provider connected to %s via %s", $be->{dsn}, $schema_class));
return $self->{_schema};
}
sub _source ($self, $c) {
my $model_cfg = $c->model_config($self->model);
return undef unless $model_cfg;
return $model_cfg->{source};
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Mojolicious::Plugin::Fondation::Auth::Provider::DBIx - DBIx::Class-backed authentication provider for Fondation::Auth
=head1 VERSION
version 0.04
=head1 SYNOPSIS
$app->plugin('Fondation::Auth' => {
model => 'user',
username_column => 'username', # optional, default: 'username'
password_column => 'password', # optional, default: 'password'
});
=head1 DESCRIPTION
This provider authenticates users against a L<DBIx::Class> schema, using
L<Mojolicious::Plugin::Fondation::Model::DBIx::Async> for backend configuration
discovery.
Authentication operations (C<validate_user>, C<load_user>) use a synchronous
L<DBIx::Class::Schema> instance built from the same backend config. This is
necessary because L<Mojolicious::Plugin::Authentication> requires synchronous
callbacks.
Password hashing (Argon2id) is handled by the Result class
(L<Mojolicious::Plugin::Fondation::Auth::Schema::Result::User>) via
C<insert>/C<update> hooks â the provider only verifies.
=head1 NAME
Mojolicious::Plugin::Fondation::Auth::Provider::DBIx - DBIx::Class-backed authentication provider
=head1 CONFIGURATION
=over 4
=item model
Model name as configured in C<Fondation::Model::DBIx::Async> (required).
Used to discover the table C<source> via C<model_config()>.
=item username_column
Column name for usernames (default: C<username>).
=item password_column
Column name for Argon2id password hashes (default: C<password>).
=back
=head1 REQUIREMENTS
The application must load C<Fondation::Model::DBIx::Async> before
C<Fondation::Auth>. This is handled automatically via C<fondation_meta>
dependencies when using the Fondation plugin loader.
The schema class must include the C<users> Result source (typically via
C<load_namespaces> and C<Mojolicious::Plugin::Fondation::Auth::Schema::Result::User>).
=head1 SEE ALSO
L<Mojolicious::Plugin::Fondation::Auth>,
L<Mojolicious::Plugin::Fondation::Model::DBIx::Async>,
L<DBIx::Class::Schema>,
L<Mojolicious::Plugin::Fondation::Auth::Schema::Result::User>
=head1 AUTHOR
Daniel Brosseau <dab@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Daniel Brosseau.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut
( run in 0.592 second using v1.01-cache-2.11-cpan-ad19def0cd9 )