File Coverage

blib/lib/Authen/SASL/SCRAM.pm
Criterion Covered Total %
statement 47 50 94.0
branch 4 6 66.6
condition n/a
subroutine 12 13 92.3
pod 0 5 0.0
total 63 74 85.1


line stmt bran cond sub pod time code
1              
2 1     1   449 use strict;
  1         5  
  1         29  
3 1     1   10 use warnings;
  1         4  
  1         25  
4              
5 1     1   582 use Feature::Compat::Try;
  1         343  
  1         5  
6              
7             package Authen::SASL::SCRAM;
8              
9             =head1 NAME
10              
11             Authen::SASL::SCRAM - SCRAM support for Authen::SASL
12              
13             =head1 VERSION
14              
15             0.03
16              
17             =head1 SYNOPSIS
18              
19             # with Authen::SASL::SCRAM installed
20             use Authen::SASL;
21              
22             my $client = Authen::SASL->new(
23             username => 'user',
24             password => 'pass',
25             mechanism => 'SCRAM-SHA-512 SCRAM-SHA-256 SCRAM-SHA-1 PLAIN'
26             );
27             # authenticates using SCRAM SHA hash or PLAIN
28              
29              
30             my $salt = 'your-precious-salt';
31             # $server_key and $stored_key need to be looked up from a user store
32             my $server_key = 'server-key-stored-for-this-user';
33             my $stored_key = 'key-stored-for-this-user';
34             my $server => Authen::SASL->new(
35             mechanism => 'SCRAM-SHA-1', # selected mechanism
36             callback => {
37             getsecret => sub {
38             my $username = shift;
39             return ($salt, $stored_key, $server_key, $iterations);
40             },
41             }
42             );
43              
44              
45             =head1 DESCRIPTION
46              
47             The C distribution adds L support for
48             SCRAM authentication using the mechanisms listed below by wrapping
49             L.
50              
51             =over
52              
53             =item SHA-1 (SCRAM-SHA-1)
54              
55             =item SHA-256 (SCRAM-SHA-256)
56              
57             =item SHA-512 (SCRAM-SHA-512)
58              
59             =back
60              
61             The *-PLUS variants are not supported at this time.
62              
63             =cut
64              
65 1     1   2951 use Authen::SASL;
  1         2  
  1         7  
66              
67 1     1   30 use parent qw(Authen::SASL::Perl);
  1         2  
  1         8  
68              
69 1     1   57 use Authen::SCRAM::Client;
  1         3  
  1         39  
70 1     1   542 use Authen::SCRAM::Server;
  1         8978  
  1         718  
71              
72              
73             our @VERSION = '0.03';
74              
75             my %secflags = (
76             noplaintext => 1,
77             noanonymous => 1,
78             );
79              
80             sub _secflags {
81 1     1   21 shift;
82 1         6 scalar grep { $secflags{$_} } @_;
  0         0  
83             }
84              
85              
86             sub client_start {
87 1     1 0 648 my $self = shift;
88              
89 1         19 $self->{need_step} = 2;
90 1         2 $self->{error} = undef;
91              
92 1         7 my $user = $self->_call('user');
93 1 50       26 return $self->set_error( 'Username is required' )
94             unless defined $user;
95              
96 1         3 my $pass = $self->_call('pass');
97 1 50       15 return $self->set_error( 'Password is required' )
98             unless defined $pass;
99              
100 1         5 $self->{_client} = Authen::SCRAM::Client->new(
101             digest => $self->digest,
102             username => $user,
103             password => $pass,
104             );
105              
106 1         247 return $self->{_client}->first_msg();
107             }
108              
109             sub client_step {
110 2     2 0 7103 my $self = shift;
111 2         12 my $challenge = shift;
112              
113 2         6 $self->{need_step}--;
114 2 100       11 if ($self->{need_step} == 1) {
115             try {
116             return $self->{_client}->final_msg( $challenge );
117             }
118 1         3 catch ($e) {
119             return $self->set_error( 'Challenge failed (step 1)' );
120             }
121             }
122             else {
123             try {
124             return $self->{_client}->validate( $challenge );
125             }
126 1         3 catch ($e) {
127             return $self->set_error( 'Response failed (step 2)' );
128             }
129             }
130             }
131              
132             sub mechanism {
133 0     0 0 0 my $self = shift;
134 0         0 return 'SCRAM-' . $self->digest;
135             }
136              
137              
138             sub server_start {
139 1     1 0 12290 my $self = shift;
140 1         2 my $client_first = shift;
141              
142 1         3 $self->{need_step} = 1;
143 1         4 $self->{_server} = Authen::SCRAM::Server->new(
144             digest => $self->digest,
145             credential_cb => $self->callback('getsecret')
146             );
147              
148             try {
149             return $self->{_server}->first_msg( $client_first );
150             }
151 1         6616 catch ($e) {
152             return $self->set_error( 'Client initiation failed' );
153             }
154             }
155              
156             sub server_step {
157 1     1 0 46300 my $self = shift;
158 1         3 my $client_final = shift;
159              
160 1         3 $self->{need_step}--;
161             try {
162             my $rv = $self->{_server}->final_msg( $client_final );
163             $self->property( 'authname', $self->{_server}->authorization_id );
164             return $rv;
165             }
166 1         4 catch ($e) {
167             return $self->set_error( 'Client finalization failed' );
168             }
169             }
170              
171             =head1 BUGS
172              
173             Please report bugs via
174             L.
175              
176             =head1 SEE ALSO
177              
178             L, L
179              
180             =head1 AUTHOR
181              
182             Erik Huelsmann
183              
184             =head1 COPYRIGHT
185              
186             Copyright (c) 2023 Erik Huelsmann. All rights reserved. This program is
187             free software; you can redistribute it and/or modify it under the same
188             terms as Perl itself.
189              
190             =cut
191              
192             1;