Version Description
Download this release
Release Info
Developer | kasparsd |
Plugin | Two-Factor |
Version | 0.4.2 |
Comparing to | |
See all releases |
Code changes from version 0.7.0 to 0.4.2
- LICENSE.md +0 -280
- class-two-factor-compat.php +0 -55
- class-two-factor-core.php → class.two-factor-core.php +58 -313
- docker-compose.yml +0 -11
- includes/Google/u2f-api.js +12 -6
- includes/Yubico/U2F.php +2 -2
- includes/function.login-header.php +3 -3
- providers/{class-two-factor-backup-codes.php → class.two-factor-backup-codes.php} +24 -44
- providers/{class-two-factor-dummy.php → class.two-factor-dummy.php} +4 -10
- providers/{class-two-factor-email.php → class.two-factor-email.php} +14 -112
- providers/{class-two-factor-fido-u2f-admin-list-table.php → class.two-factor-fido-u2f-admin-list-table.php} +13 -21
- providers/{class-two-factor-fido-u2f-admin.php → class.two-factor-fido-u2f-admin.php} +32 -61
- providers/{class-two-factor-fido-u2f.php → class.two-factor-fido-u2f.php} +43 -74
- providers/{class-two-factor-provider.php → class.two-factor-provider.php} +4 -10
- providers/{class-two-factor-totp.php → class.two-factor-totp.php} +54 -104
- providers/js/fido-u2f-admin-inline-edit.js +1 -2
- providers/js/fido-u2f-admin.js +2 -2
- readme.md +9 -40
- readme.txt +27 -51
- two-factor.php +7 -27
LICENSE.md
DELETED
@@ -1,280 +0,0 @@
|
|
1 |
-
GNU GENERAL PUBLIC LICENSE
|
2 |
-
Version 2, June 1991
|
3 |
-
|
4 |
-
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
5 |
-
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
6 |
-
Everyone is permitted to copy and distribute verbatim copies
|
7 |
-
of this license document, but changing it is not allowed.
|
8 |
-
|
9 |
-
Preamble
|
10 |
-
|
11 |
-
The licenses for most software are designed to take away your
|
12 |
-
freedom to share and change it. By contrast, the GNU General Public
|
13 |
-
License is intended to guarantee your freedom to share and change free
|
14 |
-
software--to make sure the software is free for all its users. This
|
15 |
-
General Public License applies to most of the Free Software
|
16 |
-
Foundation's software and to any other program whose authors commit to
|
17 |
-
using it. (Some other Free Software Foundation software is covered by
|
18 |
-
the GNU Lesser General Public License instead.) You can apply it to
|
19 |
-
your programs, too.
|
20 |
-
|
21 |
-
When we speak of free software, we are referring to freedom, not
|
22 |
-
price. Our General Public Licenses are designed to make sure that you
|
23 |
-
have the freedom to distribute copies of free software (and charge for
|
24 |
-
this service if you wish), that you receive source code or can get it
|
25 |
-
if you want it, that you can change the software or use pieces of it
|
26 |
-
in new free programs; and that you know you can do these things.
|
27 |
-
|
28 |
-
To protect your rights, we need to make restrictions that forbid
|
29 |
-
anyone to deny you these rights or to ask you to surrender the rights.
|
30 |
-
These restrictions translate to certain responsibilities for you if you
|
31 |
-
distribute copies of the software, or if you modify it.
|
32 |
-
|
33 |
-
For example, if you distribute copies of such a program, whether
|
34 |
-
gratis or for a fee, you must give the recipients all the rights that
|
35 |
-
you have. You must make sure that they, too, receive or can get the
|
36 |
-
source code. And you must show them these terms so they know their
|
37 |
-
rights.
|
38 |
-
|
39 |
-
We protect your rights with two steps: (1) copyright the software, and
|
40 |
-
(2) offer you this license which gives you legal permission to copy,
|
41 |
-
distribute and/or modify the software.
|
42 |
-
|
43 |
-
Also, for each author's protection and ours, we want to make certain
|
44 |
-
that everyone understands that there is no warranty for this free
|
45 |
-
software. If the software is modified by someone else and passed on, we
|
46 |
-
want its recipients to know that what they have is not the original, so
|
47 |
-
that any problems introduced by others will not reflect on the original
|
48 |
-
authors' reputations.
|
49 |
-
|
50 |
-
Finally, any free program is threatened constantly by software
|
51 |
-
patents. We wish to avoid the danger that redistributors of a free
|
52 |
-
program will individually obtain patent licenses, in effect making the
|
53 |
-
program proprietary. To prevent this, we have made it clear that any
|
54 |
-
patent must be licensed for everyone's free use or not licensed at all.
|
55 |
-
|
56 |
-
The precise terms and conditions for copying, distribution and
|
57 |
-
modification follow.
|
58 |
-
|
59 |
-
GNU GENERAL PUBLIC LICENSE
|
60 |
-
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
61 |
-
|
62 |
-
0. This License applies to any program or other work which contains
|
63 |
-
a notice placed by the copyright holder saying it may be distributed
|
64 |
-
under the terms of this General Public License. The "Program", below,
|
65 |
-
refers to any such program or work, and a "work based on the Program"
|
66 |
-
means either the Program or any derivative work under copyright law:
|
67 |
-
that is to say, a work containing the Program or a portion of it,
|
68 |
-
either verbatim or with modifications and/or translated into another
|
69 |
-
language. (Hereinafter, translation is included without limitation in
|
70 |
-
the term "modification".) Each licensee is addressed as "you".
|
71 |
-
|
72 |
-
Activities other than copying, distribution and modification are not
|
73 |
-
covered by this License; they are outside its scope. The act of
|
74 |
-
running the Program is not restricted, and the output from the Program
|
75 |
-
is covered only if its contents constitute a work based on the
|
76 |
-
Program (independent of having been made by running the Program).
|
77 |
-
Whether that is true depends on what the Program does.
|
78 |
-
|
79 |
-
1. You may copy and distribute verbatim copies of the Program's
|
80 |
-
source code as you receive it, in any medium, provided that you
|
81 |
-
conspicuously and appropriately publish on each copy an appropriate
|
82 |
-
copyright notice and disclaimer of warranty; keep intact all the
|
83 |
-
notices that refer to this License and to the absence of any warranty;
|
84 |
-
and give any other recipients of the Program a copy of this License
|
85 |
-
along with the Program.
|
86 |
-
|
87 |
-
You may charge a fee for the physical act of transferring a copy, and
|
88 |
-
you may at your option offer warranty protection in exchange for a fee.
|
89 |
-
|
90 |
-
2. You may modify your copy or copies of the Program or any portion
|
91 |
-
of it, thus forming a work based on the Program, and copy and
|
92 |
-
distribute such modifications or work under the terms of Section 1
|
93 |
-
above, provided that you also meet all of these conditions:
|
94 |
-
|
95 |
-
a) You must cause the modified files to carry prominent notices
|
96 |
-
stating that you changed the files and the date of any change.
|
97 |
-
|
98 |
-
b) You must cause any work that you distribute or publish, that in
|
99 |
-
whole or in part contains or is derived from the Program or any
|
100 |
-
part thereof, to be licensed as a whole at no charge to all third
|
101 |
-
parties under the terms of this License.
|
102 |
-
|
103 |
-
c) If the modified program normally reads commands interactively
|
104 |
-
when run, you must cause it, when started running for such
|
105 |
-
interactive use in the most ordinary way, to print or display an
|
106 |
-
announcement including an appropriate copyright notice and a
|
107 |
-
notice that there is no warranty (or else, saying that you provide
|
108 |
-
a warranty) and that users may redistribute the program under
|
109 |
-
these conditions, and telling the user how to view a copy of this
|
110 |
-
License. (Exception: if the Program itself is interactive but
|
111 |
-
does not normally print such an announcement, your work based on
|
112 |
-
the Program is not required to print an announcement.)
|
113 |
-
|
114 |
-
These requirements apply to the modified work as a whole. If
|
115 |
-
identifiable sections of that work are not derived from the Program,
|
116 |
-
and can be reasonably considered independent and separate works in
|
117 |
-
themselves, then this License, and its terms, do not apply to those
|
118 |
-
sections when you distribute them as separate works. But when you
|
119 |
-
distribute the same sections as part of a whole which is a work based
|
120 |
-
on the Program, the distribution of the whole must be on the terms of
|
121 |
-
this License, whose permissions for other licensees extend to the
|
122 |
-
entire whole, and thus to each and every part regardless of who wrote it.
|
123 |
-
|
124 |
-
Thus, it is not the intent of this section to claim rights or contest
|
125 |
-
your rights to work written entirely by you; rather, the intent is to
|
126 |
-
exercise the right to control the distribution of derivative or
|
127 |
-
collective works based on the Program.
|
128 |
-
|
129 |
-
In addition, mere aggregation of another work not based on the Program
|
130 |
-
with the Program (or with a work based on the Program) on a volume of
|
131 |
-
a storage or distribution medium does not bring the other work under
|
132 |
-
the scope of this License.
|
133 |
-
|
134 |
-
3. You may copy and distribute the Program (or a work based on it,
|
135 |
-
under Section 2) in object code or executable form under the terms of
|
136 |
-
Sections 1 and 2 above provided that you also do one of the following:
|
137 |
-
|
138 |
-
a) Accompany it with the complete corresponding machine-readable
|
139 |
-
source code, which must be distributed under the terms of Sections
|
140 |
-
1 and 2 above on a medium customarily used for software interchange; or,
|
141 |
-
|
142 |
-
b) Accompany it with a written offer, valid for at least three
|
143 |
-
years, to give any third party, for a charge no more than your
|
144 |
-
cost of physically performing source distribution, a complete
|
145 |
-
machine-readable copy of the corresponding source code, to be
|
146 |
-
distributed under the terms of Sections 1 and 2 above on a medium
|
147 |
-
customarily used for software interchange; or,
|
148 |
-
|
149 |
-
c) Accompany it with the information you received as to the offer
|
150 |
-
to distribute corresponding source code. (This alternative is
|
151 |
-
allowed only for noncommercial distribution and only if you
|
152 |
-
received the program in object code or executable form with such
|
153 |
-
an offer, in accord with Subsection b above.)
|
154 |
-
|
155 |
-
The source code for a work means the preferred form of the work for
|
156 |
-
making modifications to it. For an executable work, complete source
|
157 |
-
code means all the source code for all modules it contains, plus any
|
158 |
-
associated interface definition files, plus the scripts used to
|
159 |
-
control compilation and installation of the executable. However, as a
|
160 |
-
special exception, the source code distributed need not include
|
161 |
-
anything that is normally distributed (in either source or binary
|
162 |
-
form) with the major components (compiler, kernel, and so on) of the
|
163 |
-
operating system on which the executable runs, unless that component
|
164 |
-
itself accompanies the executable.
|
165 |
-
|
166 |
-
If distribution of executable or object code is made by offering
|
167 |
-
access to copy from a designated place, then offering equivalent
|
168 |
-
access to copy the source code from the same place counts as
|
169 |
-
distribution of the source code, even though third parties are not
|
170 |
-
compelled to copy the source along with the object code.
|
171 |
-
|
172 |
-
4. You may not copy, modify, sublicense, or distribute the Program
|
173 |
-
except as expressly provided under this License. Any attempt
|
174 |
-
otherwise to copy, modify, sublicense or distribute the Program is
|
175 |
-
void, and will automatically terminate your rights under this License.
|
176 |
-
However, parties who have received copies, or rights, from you under
|
177 |
-
this License will not have their licenses terminated so long as such
|
178 |
-
parties remain in full compliance.
|
179 |
-
|
180 |
-
5. You are not required to accept this License, since you have not
|
181 |
-
signed it. However, nothing else grants you permission to modify or
|
182 |
-
distribute the Program or its derivative works. These actions are
|
183 |
-
prohibited by law if you do not accept this License. Therefore, by
|
184 |
-
modifying or distributing the Program (or any work based on the
|
185 |
-
Program), you indicate your acceptance of this License to do so, and
|
186 |
-
all its terms and conditions for copying, distributing or modifying
|
187 |
-
the Program or works based on it.
|
188 |
-
|
189 |
-
6. Each time you redistribute the Program (or any work based on the
|
190 |
-
Program), the recipient automatically receives a license from the
|
191 |
-
original licensor to copy, distribute or modify the Program subject to
|
192 |
-
these terms and conditions. You may not impose any further
|
193 |
-
restrictions on the recipients' exercise of the rights granted herein.
|
194 |
-
You are not responsible for enforcing compliance by third parties to
|
195 |
-
this License.
|
196 |
-
|
197 |
-
7. If, as a consequence of a court judgment or allegation of patent
|
198 |
-
infringement or for any other reason (not limited to patent issues),
|
199 |
-
conditions are imposed on you (whether by court order, agreement or
|
200 |
-
otherwise) that contradict the conditions of this License, they do not
|
201 |
-
excuse you from the conditions of this License. If you cannot
|
202 |
-
distribute so as to satisfy simultaneously your obligations under this
|
203 |
-
License and any other pertinent obligations, then as a consequence you
|
204 |
-
may not distribute the Program at all. For example, if a patent
|
205 |
-
license would not permit royalty-free redistribution of the Program by
|
206 |
-
all those who receive copies directly or indirectly through you, then
|
207 |
-
the only way you could satisfy both it and this License would be to
|
208 |
-
refrain entirely from distribution of the Program.
|
209 |
-
|
210 |
-
If any portion of this section is held invalid or unenforceable under
|
211 |
-
any particular circumstance, the balance of the section is intended to
|
212 |
-
apply and the section as a whole is intended to apply in other
|
213 |
-
circumstances.
|
214 |
-
|
215 |
-
It is not the purpose of this section to induce you to infringe any
|
216 |
-
patents or other property right claims or to contest validity of any
|
217 |
-
such claims; this section has the sole purpose of protecting the
|
218 |
-
integrity of the free software distribution system, which is
|
219 |
-
implemented by public license practices. Many people have made
|
220 |
-
generous contributions to the wide range of software distributed
|
221 |
-
through that system in reliance on consistent application of that
|
222 |
-
system; it is up to the author/donor to decide if he or she is willing
|
223 |
-
to distribute software through any other system and a licensee cannot
|
224 |
-
impose that choice.
|
225 |
-
|
226 |
-
This section is intended to make thoroughly clear what is believed to
|
227 |
-
be a consequence of the rest of this License.
|
228 |
-
|
229 |
-
8. If the distribution and/or use of the Program is restricted in
|
230 |
-
certain countries either by patents or by copyrighted interfaces, the
|
231 |
-
original copyright holder who places the Program under this License
|
232 |
-
may add an explicit geographical distribution limitation excluding
|
233 |
-
those countries, so that distribution is permitted only in or among
|
234 |
-
countries not thus excluded. In such case, this License incorporates
|
235 |
-
the limitation as if written in the body of this License.
|
236 |
-
|
237 |
-
9. The Free Software Foundation may publish revised and/or new versions
|
238 |
-
of the General Public License from time to time. Such new versions will
|
239 |
-
be similar in spirit to the present version, but may differ in detail to
|
240 |
-
address new problems or concerns.
|
241 |
-
|
242 |
-
Each version is given a distinguishing version number. If the Program
|
243 |
-
specifies a version number of this License which applies to it and "any
|
244 |
-
later version", you have the option of following the terms and conditions
|
245 |
-
either of that version or of any later version published by the Free
|
246 |
-
Software Foundation. If the Program does not specify a version number of
|
247 |
-
this License, you may choose any version ever published by the Free Software
|
248 |
-
Foundation.
|
249 |
-
|
250 |
-
10. If you wish to incorporate parts of the Program into other free
|
251 |
-
programs whose distribution conditions are different, write to the author
|
252 |
-
to ask for permission. For software which is copyrighted by the Free
|
253 |
-
Software Foundation, write to the Free Software Foundation; we sometimes
|
254 |
-
make exceptions for this. Our decision will be guided by the two goals
|
255 |
-
of preserving the free status of all derivatives of our free software and
|
256 |
-
of promoting the sharing and reuse of software generally.
|
257 |
-
|
258 |
-
NO WARRANTY
|
259 |
-
|
260 |
-
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
261 |
-
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
262 |
-
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
263 |
-
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
264 |
-
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
265 |
-
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
266 |
-
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
267 |
-
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
268 |
-
REPAIR OR CORRECTION.
|
269 |
-
|
270 |
-
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
271 |
-
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
272 |
-
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
273 |
-
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
274 |
-
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
275 |
-
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
276 |
-
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
277 |
-
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
278 |
-
POSSIBILITY OF SUCH DAMAGES.
|
279 |
-
|
280 |
-
END OF TERMS AND CONDITIONS
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class-two-factor-compat.php
DELETED
@@ -1,55 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* A compatibility layer for some of the most popular plugins.
|
4 |
-
*
|
5 |
-
* @package Two_Factor
|
6 |
-
*/
|
7 |
-
|
8 |
-
/**
|
9 |
-
* A compatibility layer for some of the most popular plugins.
|
10 |
-
*
|
11 |
-
* Should be used with care because ideally we wouldn't need
|
12 |
-
* any integration specific code for this plugin. Everything should
|
13 |
-
* be handled through clever use of hooks and best practices.
|
14 |
-
*/
|
15 |
-
class Two_Factor_Compat {
|
16 |
-
/**
|
17 |
-
* Initialize all the custom hooks as necessary.
|
18 |
-
*
|
19 |
-
* @return void
|
20 |
-
*/
|
21 |
-
public function init() {
|
22 |
-
/**
|
23 |
-
* Jetpack
|
24 |
-
*
|
25 |
-
* @see https://wordpress.org/plugins/jetpack/
|
26 |
-
*/
|
27 |
-
add_filter( 'two_factor_rememberme', array( $this, 'jetpack_rememberme' ) );
|
28 |
-
}
|
29 |
-
|
30 |
-
/**
|
31 |
-
* Jetpack single sign-on wants long-lived sessions for users.
|
32 |
-
*
|
33 |
-
* @param boolean $rememberme Current state of the "remember me" toggle.
|
34 |
-
*
|
35 |
-
* @return boolean
|
36 |
-
*/
|
37 |
-
public function jetpack_rememberme( $rememberme ) {
|
38 |
-
$action = filter_input( INPUT_GET, 'action', FILTER_SANITIZE_STRING );
|
39 |
-
|
40 |
-
if ( 'jetpack-sso' === $action && $this->jetpack_is_sso_active() ) {
|
41 |
-
return true;
|
42 |
-
}
|
43 |
-
|
44 |
-
return $rememberme;
|
45 |
-
}
|
46 |
-
|
47 |
-
/**
|
48 |
-
* Helper to detect the presence of the active SSO module.
|
49 |
-
*
|
50 |
-
* @return boolean
|
51 |
-
*/
|
52 |
-
public function jetpack_is_sso_active() {
|
53 |
-
return ( method_exists( 'Jetpack', 'is_module_active' ) && Jetpack::is_module_active( 'sso' ) );
|
54 |
-
}
|
55 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class-two-factor-core.php → class.two-factor-core.php
RENAMED
@@ -1,10 +1,4 @@
|
|
1 |
<?php
|
2 |
-
/**
|
3 |
-
* Two Factore Core Class.
|
4 |
-
*
|
5 |
-
* @package Two_Factor
|
6 |
-
*/
|
7 |
-
|
8 |
/**
|
9 |
* Class for creating two factor authorization.
|
10 |
*
|
@@ -33,38 +27,14 @@ class Two_Factor_Core {
|
|
33 |
*
|
34 |
* @type string
|
35 |
*/
|
36 |
-
const USER_META_NONCE_KEY
|
37 |
-
|
38 |
-
/**
|
39 |
-
* URL query paramater used for our custom actions.
|
40 |
-
*
|
41 |
-
* @var string
|
42 |
-
*/
|
43 |
-
const USER_SETTINGS_ACTION_QUERY_VAR = 'two_factor_action';
|
44 |
-
|
45 |
-
/**
|
46 |
-
* Nonce key for user settings.
|
47 |
-
*
|
48 |
-
* @var string
|
49 |
-
*/
|
50 |
-
const USER_SETTINGS_ACTION_NONCE_QUERY_ARG = '_two_factor_action_nonce';
|
51 |
-
|
52 |
-
/**
|
53 |
-
* Keep track of all the password-based authentication sessions that
|
54 |
-
* need to invalidated before the second factor authentication.
|
55 |
-
*
|
56 |
-
* @var array
|
57 |
-
*/
|
58 |
-
private static $password_auth_tokens = array();
|
59 |
|
60 |
/**
|
61 |
* Set up filters and actions.
|
62 |
*
|
63 |
-
* @param object $compat A compaitbility later for plugins.
|
64 |
-
*
|
65 |
* @since 0.1-dev
|
66 |
*/
|
67 |
-
public static function add_hooks(
|
68 |
add_action( 'plugins_loaded', array( __CLASS__, 'load_textdomain' ) );
|
69 |
add_action( 'init', array( __CLASS__, 'get_providers' ) );
|
70 |
add_action( 'wp_login', array( __CLASS__, 'wp_login' ), 10, 2 );
|
@@ -78,22 +48,8 @@ class Two_Factor_Core {
|
|
78 |
add_filter( 'wpmu_users_columns', array( __CLASS__, 'filter_manage_users_columns' ) );
|
79 |
add_filter( 'manage_users_custom_column', array( __CLASS__, 'manage_users_custom_column' ), 10, 3 );
|
80 |
|
81 |
-
/**
|
82 |
-
* Keep track of all the user sessions for which we need to invalidate the
|
83 |
-
* authentication cookies set during the initial password check.
|
84 |
-
*
|
85 |
-
* Is there a better way of doing this?
|
86 |
-
*/
|
87 |
-
add_action( 'set_auth_cookie', array( __CLASS__, 'collect_auth_cookie_tokens' ) );
|
88 |
-
add_action( 'set_logged_in_cookie', array( __CLASS__, 'collect_auth_cookie_tokens' ) );
|
89 |
-
|
90 |
// Run only after the core wp_authenticate_username_password() check.
|
91 |
add_filter( 'authenticate', array( __CLASS__, 'filter_authenticate' ), 50 );
|
92 |
-
|
93 |
-
add_action( 'admin_init', array( __CLASS__, 'trigger_user_settings_action' ) );
|
94 |
-
add_filter( 'two_factor_providers', array( __CLASS__, 'enable_dummy_method_for_debug' ) );
|
95 |
-
|
96 |
-
$compat->init();
|
97 |
}
|
98 |
|
99 |
/**
|
@@ -114,11 +70,11 @@ class Two_Factor_Core {
|
|
114 |
*/
|
115 |
public static function get_providers() {
|
116 |
$providers = array(
|
117 |
-
'Two_Factor_Email' => TWO_FACTOR_DIR . 'providers/class
|
118 |
-
'Two_Factor_Totp' => TWO_FACTOR_DIR . 'providers/class
|
119 |
-
'Two_Factor_FIDO_U2F' => TWO_FACTOR_DIR . 'providers/class
|
120 |
-
'Two_Factor_Backup_Codes' => TWO_FACTOR_DIR . 'providers/class
|
121 |
-
'Two_Factor_Dummy' => TWO_FACTOR_DIR . 'providers/class
|
122 |
);
|
123 |
|
124 |
/**
|
@@ -135,20 +91,18 @@ class Two_Factor_Core {
|
|
135 |
// FIDO U2F is PHP 5.3+ only.
|
136 |
if ( isset( $providers['Two_Factor_FIDO_U2F'] ) && version_compare( PHP_VERSION, '5.3.0', '<' ) ) {
|
137 |
unset( $providers['Two_Factor_FIDO_U2F'] );
|
138 |
-
trigger_error( //
|
139 |
-
sprintf(
|
140 |
/* translators: %s: version number */
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
);
|
145 |
}
|
146 |
|
147 |
/**
|
148 |
* For each filtered provider,
|
149 |
*/
|
150 |
foreach ( $providers as $class => $path ) {
|
151 |
-
include_once $path;
|
152 |
|
153 |
/**
|
154 |
* Confirm that it's been successfully included before instantiating.
|
@@ -165,149 +119,6 @@ class Two_Factor_Core {
|
|
165 |
return $providers;
|
166 |
}
|
167 |
|
168 |
-
/**
|
169 |
-
* Enable the dummy method only during debugging.
|
170 |
-
*
|
171 |
-
* @param array $methods List of enabled methods.
|
172 |
-
*
|
173 |
-
* @return array
|
174 |
-
*/
|
175 |
-
public static function enable_dummy_method_for_debug( $methods ) {
|
176 |
-
if ( ! self::is_wp_debug() ) {
|
177 |
-
unset( $methods['Two_Factor_Dummy'] );
|
178 |
-
}
|
179 |
-
|
180 |
-
return $methods;
|
181 |
-
}
|
182 |
-
|
183 |
-
/**
|
184 |
-
* Check if the debug mode is enabled.
|
185 |
-
*
|
186 |
-
* @return boolean
|
187 |
-
*/
|
188 |
-
protected static function is_wp_debug() {
|
189 |
-
return ( defined( 'WP_DEBUG' ) && WP_DEBUG );
|
190 |
-
}
|
191 |
-
|
192 |
-
/**
|
193 |
-
* Get the user settings page URL.
|
194 |
-
*
|
195 |
-
* Fetch this from the plugin core after we introduce proper dependency injection
|
196 |
-
* and get away from the singletons at the provider level (should be handled by core).
|
197 |
-
*
|
198 |
-
* @param integer $user_id User ID.
|
199 |
-
*
|
200 |
-
* @return string
|
201 |
-
*/
|
202 |
-
protected static function get_user_settings_page_url( $user_id ) {
|
203 |
-
$page = 'user-edit.php';
|
204 |
-
|
205 |
-
if ( defined( 'IS_PROFILE_PAGE' ) && IS_PROFILE_PAGE ) {
|
206 |
-
$page = 'profile.php';
|
207 |
-
}
|
208 |
-
|
209 |
-
return add_query_arg(
|
210 |
-
array(
|
211 |
-
'user_id' => intval( $user_id ),
|
212 |
-
),
|
213 |
-
self_admin_url( $page )
|
214 |
-
);
|
215 |
-
}
|
216 |
-
|
217 |
-
/**
|
218 |
-
* Get the URL for resetting the secret token.
|
219 |
-
*
|
220 |
-
* @param integer $user_id User ID.
|
221 |
-
* @param string $action Custom two factor action key.
|
222 |
-
*
|
223 |
-
* @return string
|
224 |
-
*/
|
225 |
-
public static function get_user_update_action_url( $user_id, $action ) {
|
226 |
-
return wp_nonce_url(
|
227 |
-
add_query_arg(
|
228 |
-
array(
|
229 |
-
self::USER_SETTINGS_ACTION_QUERY_VAR => $action,
|
230 |
-
),
|
231 |
-
self::get_user_settings_page_url( $user_id )
|
232 |
-
),
|
233 |
-
sprintf( '%d-%s', $user_id, $action ),
|
234 |
-
self::USER_SETTINGS_ACTION_NONCE_QUERY_ARG
|
235 |
-
);
|
236 |
-
}
|
237 |
-
|
238 |
-
/**
|
239 |
-
* Check if a user action is valid.
|
240 |
-
*
|
241 |
-
* @param integer $user_id User ID.
|
242 |
-
* @param string $action User action ID.
|
243 |
-
*
|
244 |
-
* @return boolean
|
245 |
-
*/
|
246 |
-
public static function is_valid_user_action( $user_id, $action ) {
|
247 |
-
$request_nonce = filter_input( INPUT_GET, self::USER_SETTINGS_ACTION_NONCE_QUERY_ARG, FILTER_SANITIZE_STRING );
|
248 |
-
|
249 |
-
return wp_verify_nonce(
|
250 |
-
$request_nonce,
|
251 |
-
sprintf( '%d-%s', $user_id, $action )
|
252 |
-
);
|
253 |
-
}
|
254 |
-
|
255 |
-
/**
|
256 |
-
* Get the ID of the user being edited.
|
257 |
-
*
|
258 |
-
* @return integer
|
259 |
-
*/
|
260 |
-
public static function current_user_being_edited() {
|
261 |
-
// Try to resolve the user ID from the request first.
|
262 |
-
if ( ! empty( $_REQUEST['user_id'] ) ) {
|
263 |
-
$user_id = intval( $_REQUEST['user_id'] );
|
264 |
-
|
265 |
-
if ( current_user_can( 'edit_user', $user_id ) ) {
|
266 |
-
return $user_id;
|
267 |
-
}
|
268 |
-
}
|
269 |
-
|
270 |
-
return get_current_user_id();
|
271 |
-
}
|
272 |
-
|
273 |
-
/**
|
274 |
-
* Trigger our custom update action if a valid
|
275 |
-
* action request is detected and passes the nonce check.
|
276 |
-
*
|
277 |
-
* @return void
|
278 |
-
*/
|
279 |
-
public static function trigger_user_settings_action() {
|
280 |
-
$action = filter_input( INPUT_GET, self::USER_SETTINGS_ACTION_QUERY_VAR, FILTER_SANITIZE_STRING );
|
281 |
-
$user_id = self::current_user_being_edited();
|
282 |
-
|
283 |
-
if ( ! empty( $action ) && self::is_valid_user_action( $user_id, $action ) ) {
|
284 |
-
/**
|
285 |
-
* This action is triggered when a valid Two Factor settings
|
286 |
-
* action is detected and it passes the nonce validation.
|
287 |
-
*
|
288 |
-
* @param integer $user_id User ID.
|
289 |
-
* @param string $action Settings action.
|
290 |
-
*/
|
291 |
-
do_action( 'two_factor_user_settings_action', $user_id, $action );
|
292 |
-
}
|
293 |
-
}
|
294 |
-
|
295 |
-
/**
|
296 |
-
* Keep track of all the authentication cookies that need to be
|
297 |
-
* invalidated before the second factor authentication.
|
298 |
-
*
|
299 |
-
* @param string $cookie Cookie string.
|
300 |
-
*
|
301 |
-
* @return void
|
302 |
-
*/
|
303 |
-
public static function collect_auth_cookie_tokens( $cookie ) {
|
304 |
-
$parsed = wp_parse_auth_cookie( $cookie );
|
305 |
-
|
306 |
-
if ( ! empty( $parsed['token'] ) ) {
|
307 |
-
self::$password_auth_tokens[] = $parsed['token'];
|
308 |
-
}
|
309 |
-
}
|
310 |
-
|
311 |
/**
|
312 |
* Get all Two-Factor Auth providers that are enabled for the specified|current user.
|
313 |
*
|
@@ -326,13 +137,7 @@ class Two_Factor_Core {
|
|
326 |
}
|
327 |
$enabled_providers = array_intersect( $enabled_providers, array_keys( $providers ) );
|
328 |
|
329 |
-
|
330 |
-
* Filter the enabled two-factor authentication providers for this user.
|
331 |
-
*
|
332 |
-
* @param array $enabled_providers The enabled providers.
|
333 |
-
* @param int $user_id The user ID.
|
334 |
-
*/
|
335 |
-
return apply_filters( 'two_factor_enabled_providers_for_user', $enabled_providers, $user->ID );
|
336 |
}
|
337 |
|
338 |
/**
|
@@ -351,7 +156,7 @@ class Two_Factor_Core {
|
|
351 |
$configured_providers = array();
|
352 |
|
353 |
foreach ( $providers as $classname => $provider ) {
|
354 |
-
if ( in_array( $classname, $enabled_providers
|
355 |
$configured_providers[ $classname ] = $provider;
|
356 |
}
|
357 |
}
|
@@ -430,35 +235,12 @@ class Two_Factor_Core {
|
|
430 |
return;
|
431 |
}
|
432 |
|
433 |
-
// Invalidate the current login session to prevent from being re-used.
|
434 |
-
self::destroy_current_session_for_user( $user );
|
435 |
-
|
436 |
-
// Also clear the cookies which are no longer valid.
|
437 |
wp_clear_auth_cookie();
|
438 |
|
439 |
self::show_two_factor_login( $user );
|
440 |
exit;
|
441 |
}
|
442 |
|
443 |
-
/**
|
444 |
-
* Destroy the known password-based authentication sessions for the current user.
|
445 |
-
*
|
446 |
-
* Is there a better way of finding the current session token without
|
447 |
-
* having access to the authentication cookies which are just being set
|
448 |
-
* on the first password-based authentication request.
|
449 |
-
*
|
450 |
-
* @param \WP_User $user User object.
|
451 |
-
*
|
452 |
-
* @return void
|
453 |
-
*/
|
454 |
-
public static function destroy_current_session_for_user( $user ) {
|
455 |
-
$session_manager = WP_Session_Tokens::get_instance( $user->ID );
|
456 |
-
|
457 |
-
foreach ( self::$password_auth_tokens as $auth_token ) {
|
458 |
-
$session_manager->destroy( $auth_token );
|
459 |
-
}
|
460 |
-
}
|
461 |
-
|
462 |
/**
|
463 |
* Prevent login through XML-RPC and REST API for users with at least one
|
464 |
* two-factor method enabled.
|
@@ -536,33 +318,29 @@ class Two_Factor_Core {
|
|
536 |
* @since 0.1-dev
|
537 |
*/
|
538 |
public static function backup_2fa() {
|
539 |
-
|
540 |
-
$nonce = filter_input( INPUT_GET, 'wp-auth-nonce', FILTER_SANITIZE_STRING );
|
541 |
-
$provider = filter_input( INPUT_GET, 'provider', FILTER_SANITIZE_STRING );
|
542 |
-
|
543 |
-
if ( ! $wp_auth_id || ! $nonce || ! $provider ) {
|
544 |
return;
|
545 |
}
|
546 |
|
547 |
-
$user = get_userdata( $
|
548 |
if ( ! $user ) {
|
549 |
return;
|
550 |
}
|
551 |
|
|
|
552 |
if ( true !== self::verify_login_nonce( $user->ID, $nonce ) ) {
|
553 |
wp_safe_redirect( get_bloginfo( 'url' ) );
|
554 |
exit;
|
555 |
}
|
556 |
|
557 |
$providers = self::get_available_providers_for_user( $user );
|
558 |
-
if ( isset( $providers[ $provider ] ) ) {
|
559 |
-
$provider = $providers[ $provider ];
|
560 |
} else {
|
561 |
-
wp_die( esc_html__( 'Cheatin’ uh?'
|
562 |
}
|
563 |
|
564 |
-
|
565 |
-
self::login_html( $user, $nonce, $redirect_to, '', $provider );
|
566 |
|
567 |
exit;
|
568 |
}
|
@@ -588,14 +366,17 @@ class Two_Factor_Core {
|
|
588 |
$provider_class = get_class( $provider );
|
589 |
|
590 |
$available_providers = self::get_available_providers_for_user( $user );
|
591 |
-
$backup_providers
|
592 |
-
$interim_login
|
593 |
|
594 |
-
$rememberme =
|
|
|
|
|
|
|
595 |
|
596 |
if ( ! function_exists( 'login_header' ) ) {
|
597 |
// We really should migrate login_header() out of `wp-login.php` so it can be called from an includes file.
|
598 |
-
include_once TWO_FACTOR_DIR . 'includes/function.login-header.php';
|
599 |
}
|
600 |
|
601 |
login_header();
|
@@ -609,11 +390,11 @@ class Two_Factor_Core {
|
|
609 |
<input type="hidden" name="provider" id="provider" value="<?php echo esc_attr( $provider_class ); ?>" />
|
610 |
<input type="hidden" name="wp-auth-id" id="wp-auth-id" value="<?php echo esc_attr( $user->ID ); ?>" />
|
611 |
<input type="hidden" name="wp-auth-nonce" id="wp-auth-nonce" value="<?php echo esc_attr( $login_nonce ); ?>" />
|
612 |
-
<?php
|
613 |
<input type="hidden" name="interim-login" value="1" />
|
614 |
-
<?php
|
615 |
<input type="hidden" name="redirect_to" value="<?php echo esc_attr( $redirect_to ); ?>" />
|
616 |
-
<?php
|
617 |
<input type="hidden" name="rememberme" id="rememberme" value="<?php echo esc_attr( $rememberme ); ?>" />
|
618 |
|
619 |
<?php $provider->authentication_page( $user ); ?>
|
@@ -622,8 +403,8 @@ class Two_Factor_Core {
|
|
622 |
<?php
|
623 |
if ( 1 === count( $backup_providers ) ) :
|
624 |
$backup_classname = key( $backup_providers );
|
625 |
-
$backup_provider
|
626 |
-
$login_url
|
627 |
array(
|
628 |
'action' => 'backup_2fa',
|
629 |
'provider' => $backup_classname,
|
@@ -693,7 +474,7 @@ class Two_Factor_Core {
|
|
693 |
?>
|
694 |
</a>
|
695 |
</p>
|
696 |
-
|
697 |
<style>
|
698 |
/* @todo: migrate to an external stylesheet. */
|
699 |
.backup-methods-wrap {
|
@@ -717,8 +498,7 @@ class Two_Factor_Core {
|
|
717 |
|
718 |
<?php
|
719 |
/** This action is documented in wp-login.php */
|
720 |
-
do_action( 'login_footer' );
|
721 |
-
?>
|
722 |
<div class="clear"></div>
|
723 |
</body>
|
724 |
</html>
|
@@ -752,11 +532,11 @@ class Two_Factor_Core {
|
|
752 |
* @return array
|
753 |
*/
|
754 |
public static function create_login_nonce( $user_id ) {
|
755 |
-
$login_nonce
|
756 |
try {
|
757 |
$login_nonce['key'] = bin2hex( random_bytes( 32 ) );
|
758 |
-
} catch (
|
759 |
-
$login_nonce['key'] = wp_hash( $user_id .
|
760 |
}
|
761 |
$login_nonce['expiration'] = time() + HOUR_IN_SECONDS;
|
762 |
|
@@ -808,30 +588,27 @@ class Two_Factor_Core {
|
|
808 |
* @since 0.1-dev
|
809 |
*/
|
810 |
public static function login_form_validate_2fa() {
|
811 |
-
|
812 |
-
$nonce = filter_input( INPUT_POST, 'wp-auth-nonce', FILTER_SANITIZE_STRING );
|
813 |
-
|
814 |
-
if ( ! $wp_auth_id || ! $nonce ) {
|
815 |
return;
|
816 |
}
|
817 |
|
818 |
-
$user = get_userdata( $
|
819 |
if ( ! $user ) {
|
820 |
return;
|
821 |
}
|
822 |
|
|
|
823 |
if ( true !== self::verify_login_nonce( $user->ID, $nonce ) ) {
|
824 |
wp_safe_redirect( get_bloginfo( 'url' ) );
|
825 |
exit;
|
826 |
}
|
827 |
|
828 |
-
|
829 |
-
if ( $provider ) {
|
830 |
$providers = self::get_available_providers_for_user( $user );
|
831 |
-
if ( isset( $providers[ $provider ] ) ) {
|
832 |
-
$provider = $providers[ $provider ];
|
833 |
} else {
|
834 |
-
wp_die( esc_html__( 'Cheatin’ uh?'
|
835 |
}
|
836 |
} else {
|
837 |
$provider = self::get_primary_provider_for_user( $user->ID );
|
@@ -870,11 +647,9 @@ class Two_Factor_Core {
|
|
870 |
|
871 |
wp_set_auth_cookie( $user->ID, $rememberme );
|
872 |
|
873 |
-
do_action( 'two_factor_user_authenticated', $user );
|
874 |
-
|
875 |
// Must be global because that's how login_header() uses it.
|
876 |
global $interim_login;
|
877 |
-
$interim_login = isset( $_REQUEST['interim-login'] ); //
|
878 |
|
879 |
if ( $interim_login ) {
|
880 |
$customize_login = isset( $_REQUEST['customize-login'] );
|
@@ -882,16 +657,14 @@ class Two_Factor_Core {
|
|
882 |
wp_enqueue_script( 'customize-base' );
|
883 |
}
|
884 |
$message = '<p class="message">' . __( 'You have logged in successfully.', 'two-factor' ) . '</p>';
|
885 |
-
$interim_login = 'success'; //
|
886 |
-
login_header( '', $message );
|
887 |
-
?>
|
888 |
</div>
|
889 |
<?php
|
890 |
/** This action is documented in wp-login.php */
|
891 |
-
do_action( 'login_footer' );
|
892 |
-
?>
|
893 |
<?php if ( $customize_login ) : ?>
|
894 |
-
<script type="text/javascript">setTimeout( function(){ new wp.customize.Messenger({ url: '<?php echo
|
895 |
<?php endif; ?>
|
896 |
</body></html>
|
897 |
<?php
|
@@ -910,7 +683,7 @@ class Two_Factor_Core {
|
|
910 |
* @return array Updated array of columns.
|
911 |
*/
|
912 |
public static function filter_manage_users_columns( array $columns ) {
|
913 |
-
$columns['two-factor'] = __( 'Two-Factor'
|
914 |
return $columns;
|
915 |
}
|
916 |
|
@@ -947,10 +720,10 @@ class Two_Factor_Core {
|
|
947 |
* @param WP_User $user WP_User object of the logged-in user.
|
948 |
*/
|
949 |
public static function user_two_factor_options( $user ) {
|
950 |
-
wp_enqueue_style( 'user-edit-2fa', plugins_url( 'user-edit.css', __FILE__ )
|
951 |
|
952 |
$enabled_providers = array_keys( self::get_available_providers_for_user( $user ) );
|
953 |
-
$primary_provider
|
954 |
|
955 |
if ( ! empty( $primary_provider ) && is_object( $primary_provider ) ) {
|
956 |
$primary_provider_key = get_class( $primary_provider );
|
@@ -962,10 +735,10 @@ class Two_Factor_Core {
|
|
962 |
|
963 |
?>
|
964 |
<input type="hidden" name="<?php echo esc_attr( self::ENABLED_PROVIDERS_USER_META_KEY ); ?>[]" value="<?php /* Dummy input so $_POST value is passed when no providers are enabled. */ ?>" />
|
965 |
-
<table class="form-table"
|
966 |
<tr>
|
967 |
<th>
|
968 |
-
<?php esc_html_e( 'Two-Factor Options'
|
969 |
</th>
|
970 |
<td>
|
971 |
<table class="two-factor-methods-table">
|
@@ -979,24 +752,11 @@ class Two_Factor_Core {
|
|
979 |
<tbody>
|
980 |
<?php foreach ( self::get_providers() as $class => $object ) : ?>
|
981 |
<tr>
|
982 |
-
<th scope="row"><input type="checkbox" name="<?php echo esc_attr( self::ENABLED_PROVIDERS_USER_META_KEY ); ?>[]" value="<?php echo esc_attr( $class ); ?>" <?php checked( in_array( $class, $enabled_providers
|
983 |
<th scope="row"><input type="radio" name="<?php echo esc_attr( self::PROVIDER_USER_META_KEY ); ?>" value="<?php echo esc_attr( $class ); ?>" <?php checked( $class, $primary_provider_key ); ?> /></th>
|
984 |
<td>
|
985 |
-
<?php
|
986 |
-
|
987 |
-
|
988 |
-
/**
|
989 |
-
* Fires after user options are shown.
|
990 |
-
*
|
991 |
-
* Use the {@see 'two_factor_user_options_' . $class } hook instead.
|
992 |
-
*
|
993 |
-
* @deprecated 0.7.0
|
994 |
-
*
|
995 |
-
* @param WP_User $user The user.
|
996 |
-
*/
|
997 |
-
do_action_deprecated( 'two-factor-user-options-' . $class, array( $user ), '0.7.0', 'two_factor_user_options_' . $class );
|
998 |
-
do_action( 'two_factor_user_options_' . $class, $user );
|
999 |
-
?>
|
1000 |
</td>
|
1001 |
</tr>
|
1002 |
<?php endforeach; ?>
|
@@ -1049,19 +809,4 @@ class Two_Factor_Core {
|
|
1049 |
}
|
1050 |
}
|
1051 |
}
|
1052 |
-
|
1053 |
-
/**
|
1054 |
-
* Should the login session persist between sessions.
|
1055 |
-
*
|
1056 |
-
* @return boolean
|
1057 |
-
*/
|
1058 |
-
public static function rememberme() {
|
1059 |
-
$rememberme = false;
|
1060 |
-
|
1061 |
-
if ( ! empty( $_REQUEST['rememberme'] ) ) {
|
1062 |
-
$rememberme = true;
|
1063 |
-
}
|
1064 |
-
|
1065 |
-
return (bool) apply_filters( 'two_factor_rememberme', $rememberme );
|
1066 |
-
}
|
1067 |
}
|
1 |
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
/**
|
3 |
* Class for creating two factor authorization.
|
4 |
*
|
27 |
*
|
28 |
* @type string
|
29 |
*/
|
30 |
+
const USER_META_NONCE_KEY = '_two_factor_nonce';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
|
32 |
/**
|
33 |
* Set up filters and actions.
|
34 |
*
|
|
|
|
|
35 |
* @since 0.1-dev
|
36 |
*/
|
37 |
+
public static function add_hooks() {
|
38 |
add_action( 'plugins_loaded', array( __CLASS__, 'load_textdomain' ) );
|
39 |
add_action( 'init', array( __CLASS__, 'get_providers' ) );
|
40 |
add_action( 'wp_login', array( __CLASS__, 'wp_login' ), 10, 2 );
|
48 |
add_filter( 'wpmu_users_columns', array( __CLASS__, 'filter_manage_users_columns' ) );
|
49 |
add_filter( 'manage_users_custom_column', array( __CLASS__, 'manage_users_custom_column' ), 10, 3 );
|
50 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
// Run only after the core wp_authenticate_username_password() check.
|
52 |
add_filter( 'authenticate', array( __CLASS__, 'filter_authenticate' ), 50 );
|
|
|
|
|
|
|
|
|
|
|
53 |
}
|
54 |
|
55 |
/**
|
70 |
*/
|
71 |
public static function get_providers() {
|
72 |
$providers = array(
|
73 |
+
'Two_Factor_Email' => TWO_FACTOR_DIR . 'providers/class.two-factor-email.php',
|
74 |
+
'Two_Factor_Totp' => TWO_FACTOR_DIR . 'providers/class.two-factor-totp.php',
|
75 |
+
'Two_Factor_FIDO_U2F' => TWO_FACTOR_DIR . 'providers/class.two-factor-fido-u2f.php',
|
76 |
+
'Two_Factor_Backup_Codes' => TWO_FACTOR_DIR . 'providers/class.two-factor-backup-codes.php',
|
77 |
+
'Two_Factor_Dummy' => TWO_FACTOR_DIR . 'providers/class.two-factor-dummy.php',
|
78 |
);
|
79 |
|
80 |
/**
|
91 |
// FIDO U2F is PHP 5.3+ only.
|
92 |
if ( isset( $providers['Two_Factor_FIDO_U2F'] ) && version_compare( PHP_VERSION, '5.3.0', '<' ) ) {
|
93 |
unset( $providers['Two_Factor_FIDO_U2F'] );
|
94 |
+
trigger_error( sprintf( // WPCS: XSS OK.
|
|
|
95 |
/* translators: %s: version number */
|
96 |
+
__( 'FIDO U2F is not available because you are using PHP %s. (Requires 5.3 or greater)', 'two-factor' ),
|
97 |
+
PHP_VERSION
|
98 |
+
) );
|
|
|
99 |
}
|
100 |
|
101 |
/**
|
102 |
* For each filtered provider,
|
103 |
*/
|
104 |
foreach ( $providers as $class => $path ) {
|
105 |
+
include_once( $path );
|
106 |
|
107 |
/**
|
108 |
* Confirm that it's been successfully included before instantiating.
|
119 |
return $providers;
|
120 |
}
|
121 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
122 |
/**
|
123 |
* Get all Two-Factor Auth providers that are enabled for the specified|current user.
|
124 |
*
|
137 |
}
|
138 |
$enabled_providers = array_intersect( $enabled_providers, array_keys( $providers ) );
|
139 |
|
140 |
+
return $enabled_providers;
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
}
|
142 |
|
143 |
/**
|
156 |
$configured_providers = array();
|
157 |
|
158 |
foreach ( $providers as $classname => $provider ) {
|
159 |
+
if ( in_array( $classname, $enabled_providers ) && $provider->is_available_for_user( $user ) ) {
|
160 |
$configured_providers[ $classname ] = $provider;
|
161 |
}
|
162 |
}
|
235 |
return;
|
236 |
}
|
237 |
|
|
|
|
|
|
|
|
|
238 |
wp_clear_auth_cookie();
|
239 |
|
240 |
self::show_two_factor_login( $user );
|
241 |
exit;
|
242 |
}
|
243 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
244 |
/**
|
245 |
* Prevent login through XML-RPC and REST API for users with at least one
|
246 |
* two-factor method enabled.
|
318 |
* @since 0.1-dev
|
319 |
*/
|
320 |
public static function backup_2fa() {
|
321 |
+
if ( ! isset( $_GET['wp-auth-id'], $_GET['wp-auth-nonce'], $_GET['provider'] ) ) {
|
|
|
|
|
|
|
|
|
322 |
return;
|
323 |
}
|
324 |
|
325 |
+
$user = get_userdata( $_GET['wp-auth-id'] );
|
326 |
if ( ! $user ) {
|
327 |
return;
|
328 |
}
|
329 |
|
330 |
+
$nonce = $_GET['wp-auth-nonce'];
|
331 |
if ( true !== self::verify_login_nonce( $user->ID, $nonce ) ) {
|
332 |
wp_safe_redirect( get_bloginfo( 'url' ) );
|
333 |
exit;
|
334 |
}
|
335 |
|
336 |
$providers = self::get_available_providers_for_user( $user );
|
337 |
+
if ( isset( $providers[ $_GET['provider'] ] ) ) {
|
338 |
+
$provider = $providers[ $_GET['provider'] ];
|
339 |
} else {
|
340 |
+
wp_die( esc_html__( 'Cheatin’ uh?' ), 403 );
|
341 |
}
|
342 |
|
343 |
+
self::login_html( $user, $_GET['wp-auth-nonce'], $_GET['redirect_to'], '', $provider );
|
|
|
344 |
|
345 |
exit;
|
346 |
}
|
366 |
$provider_class = get_class( $provider );
|
367 |
|
368 |
$available_providers = self::get_available_providers_for_user( $user );
|
369 |
+
$backup_providers = array_diff_key( $available_providers, array( $provider_class => null ) );
|
370 |
+
$interim_login = isset( $_REQUEST['interim-login'] ); // WPCS: CSRF ok.
|
371 |
|
372 |
+
$rememberme = 0;
|
373 |
+
if ( isset( $_REQUEST['rememberme'] ) && $_REQUEST['rememberme'] ) {
|
374 |
+
$rememberme = 1;
|
375 |
+
}
|
376 |
|
377 |
if ( ! function_exists( 'login_header' ) ) {
|
378 |
// We really should migrate login_header() out of `wp-login.php` so it can be called from an includes file.
|
379 |
+
include_once( TWO_FACTOR_DIR . 'includes/function.login-header.php' );
|
380 |
}
|
381 |
|
382 |
login_header();
|
390 |
<input type="hidden" name="provider" id="provider" value="<?php echo esc_attr( $provider_class ); ?>" />
|
391 |
<input type="hidden" name="wp-auth-id" id="wp-auth-id" value="<?php echo esc_attr( $user->ID ); ?>" />
|
392 |
<input type="hidden" name="wp-auth-nonce" id="wp-auth-nonce" value="<?php echo esc_attr( $login_nonce ); ?>" />
|
393 |
+
<?php if ( $interim_login ) { ?>
|
394 |
<input type="hidden" name="interim-login" value="1" />
|
395 |
+
<?php } else { ?>
|
396 |
<input type="hidden" name="redirect_to" value="<?php echo esc_attr( $redirect_to ); ?>" />
|
397 |
+
<?php } ?>
|
398 |
<input type="hidden" name="rememberme" id="rememberme" value="<?php echo esc_attr( $rememberme ); ?>" />
|
399 |
|
400 |
<?php $provider->authentication_page( $user ); ?>
|
403 |
<?php
|
404 |
if ( 1 === count( $backup_providers ) ) :
|
405 |
$backup_classname = key( $backup_providers );
|
406 |
+
$backup_provider = $backup_providers[ $backup_classname ];
|
407 |
+
$login_url = self::login_url(
|
408 |
array(
|
409 |
'action' => 'backup_2fa',
|
410 |
'provider' => $backup_classname,
|
474 |
?>
|
475 |
</a>
|
476 |
</p>
|
477 |
+
|
478 |
<style>
|
479 |
/* @todo: migrate to an external stylesheet. */
|
480 |
.backup-methods-wrap {
|
498 |
|
499 |
<?php
|
500 |
/** This action is documented in wp-login.php */
|
501 |
+
do_action( 'login_footer' ); ?>
|
|
|
502 |
<div class="clear"></div>
|
503 |
</body>
|
504 |
</html>
|
532 |
* @return array
|
533 |
*/
|
534 |
public static function create_login_nonce( $user_id ) {
|
535 |
+
$login_nonce = array();
|
536 |
try {
|
537 |
$login_nonce['key'] = bin2hex( random_bytes( 32 ) );
|
538 |
+
} catch (Exception $ex) {
|
539 |
+
$login_nonce['key'] = wp_hash( $user_id . mt_rand() . microtime(), 'nonce' );
|
540 |
}
|
541 |
$login_nonce['expiration'] = time() + HOUR_IN_SECONDS;
|
542 |
|
588 |
* @since 0.1-dev
|
589 |
*/
|
590 |
public static function login_form_validate_2fa() {
|
591 |
+
if ( ! isset( $_POST['wp-auth-id'], $_POST['wp-auth-nonce'] ) ) {
|
|
|
|
|
|
|
592 |
return;
|
593 |
}
|
594 |
|
595 |
+
$user = get_userdata( $_POST['wp-auth-id'] );
|
596 |
if ( ! $user ) {
|
597 |
return;
|
598 |
}
|
599 |
|
600 |
+
$nonce = $_POST['wp-auth-nonce'];
|
601 |
if ( true !== self::verify_login_nonce( $user->ID, $nonce ) ) {
|
602 |
wp_safe_redirect( get_bloginfo( 'url' ) );
|
603 |
exit;
|
604 |
}
|
605 |
|
606 |
+
if ( isset( $_POST['provider'] ) ) {
|
|
|
607 |
$providers = self::get_available_providers_for_user( $user );
|
608 |
+
if ( isset( $providers[ $_POST['provider'] ] ) ) {
|
609 |
+
$provider = $providers[ $_POST['provider'] ];
|
610 |
} else {
|
611 |
+
wp_die( esc_html__( 'Cheatin’ uh?' ), 403 );
|
612 |
}
|
613 |
} else {
|
614 |
$provider = self::get_primary_provider_for_user( $user->ID );
|
647 |
|
648 |
wp_set_auth_cookie( $user->ID, $rememberme );
|
649 |
|
|
|
|
|
650 |
// Must be global because that's how login_header() uses it.
|
651 |
global $interim_login;
|
652 |
+
$interim_login = isset( $_REQUEST['interim-login'] ); // WPCS: override ok.
|
653 |
|
654 |
if ( $interim_login ) {
|
655 |
$customize_login = isset( $_REQUEST['customize-login'] );
|
657 |
wp_enqueue_script( 'customize-base' );
|
658 |
}
|
659 |
$message = '<p class="message">' . __( 'You have logged in successfully.', 'two-factor' ) . '</p>';
|
660 |
+
$interim_login = 'success'; // WPCS: override ok.
|
661 |
+
login_header( '', $message ); ?>
|
|
|
662 |
</div>
|
663 |
<?php
|
664 |
/** This action is documented in wp-login.php */
|
665 |
+
do_action( 'login_footer' ); ?>
|
|
|
666 |
<?php if ( $customize_login ) : ?>
|
667 |
+
<script type="text/javascript">setTimeout( function(){ new wp.customize.Messenger({ url: '<?php echo wp_customize_url(); /* WPCS: XSS OK. */ ?>', channel: 'login' }).send('login') }, 1000 );</script>
|
668 |
<?php endif; ?>
|
669 |
</body></html>
|
670 |
<?php
|
683 |
* @return array Updated array of columns.
|
684 |
*/
|
685 |
public static function filter_manage_users_columns( array $columns ) {
|
686 |
+
$columns['two-factor'] = __( 'Two-Factor' );
|
687 |
return $columns;
|
688 |
}
|
689 |
|
720 |
* @param WP_User $user WP_User object of the logged-in user.
|
721 |
*/
|
722 |
public static function user_two_factor_options( $user ) {
|
723 |
+
wp_enqueue_style( 'user-edit-2fa', plugins_url( 'user-edit.css', __FILE__ ) );
|
724 |
|
725 |
$enabled_providers = array_keys( self::get_available_providers_for_user( $user ) );
|
726 |
+
$primary_provider = self::get_primary_provider_for_user( $user->ID );
|
727 |
|
728 |
if ( ! empty( $primary_provider ) && is_object( $primary_provider ) ) {
|
729 |
$primary_provider_key = get_class( $primary_provider );
|
735 |
|
736 |
?>
|
737 |
<input type="hidden" name="<?php echo esc_attr( self::ENABLED_PROVIDERS_USER_META_KEY ); ?>[]" value="<?php /* Dummy input so $_POST value is passed when no providers are enabled. */ ?>" />
|
738 |
+
<table class="form-table">
|
739 |
<tr>
|
740 |
<th>
|
741 |
+
<?php esc_html_e( 'Two-Factor Options' ); ?>
|
742 |
</th>
|
743 |
<td>
|
744 |
<table class="two-factor-methods-table">
|
752 |
<tbody>
|
753 |
<?php foreach ( self::get_providers() as $class => $object ) : ?>
|
754 |
<tr>
|
755 |
+
<th scope="row"><input type="checkbox" name="<?php echo esc_attr( self::ENABLED_PROVIDERS_USER_META_KEY ); ?>[]" value="<?php echo esc_attr( $class ); ?>" <?php checked( in_array( $class, $enabled_providers ) ); ?> /></th>
|
756 |
<th scope="row"><input type="radio" name="<?php echo esc_attr( self::PROVIDER_USER_META_KEY ); ?>" value="<?php echo esc_attr( $class ); ?>" <?php checked( $class, $primary_provider_key ); ?> /></th>
|
757 |
<td>
|
758 |
+
<?php $object->print_label(); ?>
|
759 |
+
<?php do_action( 'two-factor-user-options-' . $class, $user ); ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
760 |
</td>
|
761 |
</tr>
|
762 |
<?php endforeach; ?>
|
809 |
}
|
810 |
}
|
811 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
812 |
}
|
docker-compose.yml
DELETED
@@ -1,11 +0,0 @@
|
|
1 |
-
version: '3.6'
|
2 |
-
|
3 |
-
services:
|
4 |
-
|
5 |
-
wpdevlib:
|
6 |
-
build: ./tests/docker/wp-dev-lib
|
7 |
-
working_dir: /var/www/html
|
8 |
-
volumes:
|
9 |
-
- .:/var/www/html
|
10 |
-
environment:
|
11 |
-
CHECK_SCOPE: all
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/Google/u2f-api.js
CHANGED
@@ -16,6 +16,12 @@
|
|
16 |
*/
|
17 |
var u2f = u2f || {};
|
18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
/**
|
20 |
* FIDO U2F Javascript API Version
|
21 |
* @number
|
@@ -37,7 +43,7 @@ var js_api_version;
|
|
37 |
|
38 |
|
39 |
/**
|
40 |
-
* Message types for
|
41 |
* @const
|
42 |
* @enum {string}
|
43 |
*/
|
@@ -288,7 +294,7 @@ u2f.WrappedChromeRuntimePort_ = function(port) {
|
|
288 |
u2f.formatSignRequest_ =
|
289 |
function(appId, challenge, registeredKeys, timeoutSeconds, reqId) {
|
290 |
if (js_api_version === undefined || js_api_version < 1.1) {
|
291 |
-
// Adapt request to the 1.0 JS API
|
292 |
var signRequests = [];
|
293 |
for (var i = 0; i < registeredKeys.length; i++) {
|
294 |
signRequests[i] = {
|
@@ -305,7 +311,7 @@ u2f.formatSignRequest_ =
|
|
305 |
requestId: reqId
|
306 |
};
|
307 |
}
|
308 |
-
// JS 1.1 API
|
309 |
return {
|
310 |
type: u2f.MessageTypes.U2F_SIGN_REQUEST,
|
311 |
appId: appId,
|
@@ -327,7 +333,7 @@ u2f.formatSignRequest_ =
|
|
327 |
u2f.formatRegisterRequest_ =
|
328 |
function(appId, registeredKeys, registerRequests, timeoutSeconds, reqId) {
|
329 |
if (js_api_version === undefined || js_api_version < 1.1) {
|
330 |
-
// Adapt request to the 1.0 JS API
|
331 |
for (var i = 0; i < registerRequests.length; i++) {
|
332 |
registerRequests[i].appId = appId;
|
333 |
}
|
@@ -348,7 +354,7 @@ u2f.formatRegisterRequest_ =
|
|
348 |
requestId: reqId
|
349 |
};
|
350 |
}
|
351 |
-
// JS 1.1 API
|
352 |
return {
|
353 |
type: u2f.MessageTypes.U2F_REGISTER_REQUEST,
|
354 |
appId: appId,
|
@@ -380,7 +386,7 @@ u2f.WrappedChromeRuntimePort_.prototype.addEventListener =
|
|
380 |
var name = eventName.toLowerCase();
|
381 |
if (name == 'message' || name == 'onmessage') {
|
382 |
this.port_.onMessage.addListener(function(message) {
|
383 |
-
// Emulate a minimal MessageEvent object
|
384 |
handler({'data': message});
|
385 |
});
|
386 |
} else {
|
16 |
*/
|
17 |
var u2f = u2f || {};
|
18 |
|
19 |
+
/**
|
20 |
+
* Check if browser supports U2F API before this wrapper was added.
|
21 |
+
* @type {int}
|
22 |
+
*/
|
23 |
+
u2f.HasNativeApiSupport = ( ( u2f && u2f.register ) || ( chrome && chrome.runtime ) );
|
24 |
+
|
25 |
/**
|
26 |
* FIDO U2F Javascript API Version
|
27 |
* @number
|
43 |
|
44 |
|
45 |
/**
|
46 |
+
* Message types for messsages to/from the extension
|
47 |
* @const
|
48 |
* @enum {string}
|
49 |
*/
|
294 |
u2f.formatSignRequest_ =
|
295 |
function(appId, challenge, registeredKeys, timeoutSeconds, reqId) {
|
296 |
if (js_api_version === undefined || js_api_version < 1.1) {
|
297 |
+
// Adapt request to the 1.0 JS API
|
298 |
var signRequests = [];
|
299 |
for (var i = 0; i < registeredKeys.length; i++) {
|
300 |
signRequests[i] = {
|
311 |
requestId: reqId
|
312 |
};
|
313 |
}
|
314 |
+
// JS 1.1 API
|
315 |
return {
|
316 |
type: u2f.MessageTypes.U2F_SIGN_REQUEST,
|
317 |
appId: appId,
|
333 |
u2f.formatRegisterRequest_ =
|
334 |
function(appId, registeredKeys, registerRequests, timeoutSeconds, reqId) {
|
335 |
if (js_api_version === undefined || js_api_version < 1.1) {
|
336 |
+
// Adapt request to the 1.0 JS API
|
337 |
for (var i = 0; i < registerRequests.length; i++) {
|
338 |
registerRequests[i].appId = appId;
|
339 |
}
|
354 |
requestId: reqId
|
355 |
};
|
356 |
}
|
357 |
+
// JS 1.1 API
|
358 |
return {
|
359 |
type: u2f.MessageTypes.U2F_REGISTER_REQUEST,
|
360 |
appId: appId,
|
386 |
var name = eventName.toLowerCase();
|
387 |
if (name == 'message' || name == 'onmessage') {
|
388 |
this.port_.onMessage.addListener(function(message) {
|
389 |
+
// Emulate a minimal MessageEvent object
|
390 |
handler({'data': message});
|
391 |
});
|
392 |
} else {
|
includes/Yubico/U2F.php
CHANGED
@@ -164,7 +164,7 @@ class U2F
|
|
164 |
$offs = 1;
|
165 |
$pubKey = substr($rawReg, $offs, PUBKEY_LEN);
|
166 |
$offs += PUBKEY_LEN;
|
167 |
-
//
|
168 |
$tmpKey = $this->pubkey_to_pem($pubKey);
|
169 |
if($tmpKey === null) {
|
170 |
throw new Error('Decoding of public key failed', ERR_PUBKEY_DECODE );
|
@@ -175,7 +175,7 @@ class U2F
|
|
175 |
$offs += $khLen;
|
176 |
$registration->keyHandle = $this->base64u_encode($kh);
|
177 |
|
178 |
-
// length of certificate is stored in byte 3 and 4 (excluding the first 4 bytes)
|
179 |
$certLen = 4;
|
180 |
$certLen += ($regData[$offs + 2] << 8);
|
181 |
$certLen += $regData[$offs + 3];
|
164 |
$offs = 1;
|
165 |
$pubKey = substr($rawReg, $offs, PUBKEY_LEN);
|
166 |
$offs += PUBKEY_LEN;
|
167 |
+
// decode the pubKey to make sure it's good
|
168 |
$tmpKey = $this->pubkey_to_pem($pubKey);
|
169 |
if($tmpKey === null) {
|
170 |
throw new Error('Decoding of public key failed', ERR_PUBKEY_DECODE );
|
175 |
$offs += $khLen;
|
176 |
$registration->keyHandle = $this->base64u_encode($kh);
|
177 |
|
178 |
+
// length of certificate is stored in byte 3 and 4 (excluding the first 4 bytes)
|
179 |
$certLen = 4;
|
180 |
$certLen += ($regData[$offs + 2] << 8);
|
181 |
$certLen += $regData[$offs + 3];
|
includes/function.login-header.php
CHANGED
@@ -14,7 +14,7 @@
|
|
14 |
function login_header( $title = 'Log In', $message = '', $wp_error = null ) {
|
15 |
global $error, $interim_login, $action;
|
16 |
|
17 |
-
// Don't index any of these forms
|
18 |
add_action( 'login_head', 'wp_no_robots' );
|
19 |
|
20 |
add_action( 'login_head', 'wp_login_viewport_meta' );
|
@@ -179,7 +179,7 @@ function login_header( $title = 'Log In', $message = '', $wp_error = null ) {
|
|
179 |
if ( !empty( $message ) )
|
180 |
echo $message . "\n";
|
181 |
|
182 |
-
// In case a plugin uses $error rather than the $wp_errors object
|
183 |
if ( !empty( $error ) ) {
|
184 |
$wp_error->add('error', $error);
|
185 |
unset($error);
|
@@ -218,7 +218,7 @@ function login_header( $title = 'Log In', $message = '', $wp_error = null ) {
|
|
218 |
echo '<p class="message">' . apply_filters( 'login_messages', $messages ) . "</p>\n";
|
219 |
}
|
220 |
}
|
221 |
-
} // End of login_header()
|
222 |
|
223 |
function wp_login_viewport_meta() {
|
224 |
?>
|
14 |
function login_header( $title = 'Log In', $message = '', $wp_error = null ) {
|
15 |
global $error, $interim_login, $action;
|
16 |
|
17 |
+
// Don't index any of these forms
|
18 |
add_action( 'login_head', 'wp_no_robots' );
|
19 |
|
20 |
add_action( 'login_head', 'wp_login_viewport_meta' );
|
179 |
if ( !empty( $message ) )
|
180 |
echo $message . "\n";
|
181 |
|
182 |
+
// In case a plugin uses $error rather than the $wp_errors object
|
183 |
if ( !empty( $error ) ) {
|
184 |
$wp_error->add('error', $error);
|
185 |
unset($error);
|
218 |
echo '<p class="message">' . apply_filters( 'login_messages', $messages ) . "</p>\n";
|
219 |
}
|
220 |
}
|
221 |
+
} // End of login_header()
|
222 |
|
223 |
function wp_login_viewport_meta() {
|
224 |
?>
|
providers/{class-two-factor-backup-codes.php → class.two-factor-backup-codes.php}
RENAMED
@@ -1,10 +1,4 @@
|
|
1 |
<?php
|
2 |
-
/**
|
3 |
-
* Class for creating a backup codes provider.
|
4 |
-
*
|
5 |
-
* @package Two_Factor
|
6 |
-
*/
|
7 |
-
|
8 |
/**
|
9 |
* Class for creating a backup codes provider.
|
10 |
*
|
@@ -33,11 +27,11 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
|
|
33 |
*
|
34 |
* @since 0.1-dev
|
35 |
*/
|
36 |
-
|
37 |
static $instance;
|
38 |
$class = __CLASS__;
|
39 |
if ( ! is_a( $instance, $class ) ) {
|
40 |
-
$instance = new $class
|
41 |
}
|
42 |
return $instance;
|
43 |
}
|
@@ -48,7 +42,7 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
|
|
48 |
* @since 0.1-dev
|
49 |
*/
|
50 |
protected function __construct() {
|
51 |
-
add_action( '
|
52 |
add_action( 'admin_notices', array( $this, 'admin_notices' ) );
|
53 |
add_action( 'wp_ajax_two_factor_backup_codes_generate', array( $this, 'ajax_generate_json' ) );
|
54 |
|
@@ -64,7 +58,7 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
|
|
64 |
$user = wp_get_current_user();
|
65 |
|
66 |
// Return if the provider is not enabled.
|
67 |
-
if ( ! in_array( __CLASS__, Two_Factor_Core::get_enabled_providers_for_user( $user->ID )
|
68 |
return;
|
69 |
}
|
70 |
|
@@ -77,13 +71,9 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
|
|
77 |
<p>
|
78 |
<span>
|
79 |
<?php
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
__( 'Two-Factor: You are out of backup codes and need to <a href="%s">regenerate!</a>', 'two-factor' ),
|
84 |
-
esc_url( get_edit_user_link( $user->ID ) . '#two-factor-backup-codes' )
|
85 |
-
),
|
86 |
-
array( 'a' => array( 'href' => true ) )
|
87 |
);
|
88 |
?>
|
89 |
<span>
|
@@ -126,23 +116,19 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
|
|
126 |
*/
|
127 |
public function user_options( $user ) {
|
128 |
$ajax_nonce = wp_create_nonce( 'two-factor-backup-codes-generate-json-' . $user->ID );
|
129 |
-
$count
|
130 |
?>
|
131 |
<p id="two-factor-backup-codes">
|
132 |
<button type="button" class="button button-two-factor-backup-codes-generate button-secondary hide-if-no-js">
|
133 |
<?php esc_html_e( 'Generate Verification Codes', 'two-factor' ); ?>
|
134 |
</button>
|
135 |
-
<span class="two-factor-backup-codes-count"
|
136 |
-
|
137 |
-
echo esc_html(
|
138 |
-
sprintf(
|
139 |
/* translators: %s: count */
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
?>
|
145 |
-
</span>
|
146 |
</p>
|
147 |
<div class="two-factor-backup-codes-wrapper" style="display:none;">
|
148 |
<ol class="two-factor-backup-codes-unused-codes"></ol>
|
@@ -177,7 +163,7 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
|
|
177 |
// Update counter.
|
178 |
$( '.two-factor-backup-codes-count' ).html( response.data.i18n.count );
|
179 |
|
180 |
-
// Build the download link
|
181 |
var txt_data = 'data:application/text;charset=utf-8,' + '\n';
|
182 |
txt_data += response.data.i18n.title.replace( /%s/g, document.domain ) + '\n\n';
|
183 |
|
@@ -200,11 +186,11 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
|
|
200 |
* @since 0.1-dev
|
201 |
*
|
202 |
* @param WP_User $user WP_User object of the logged-in user.
|
203 |
-
* @param array $args Optional arguments for
|
204 |
* @return array
|
205 |
*/
|
206 |
public function generate_codes( $user, $args = '' ) {
|
207 |
-
$codes
|
208 |
$codes_hashed = array();
|
209 |
|
210 |
// Check for arguments.
|
@@ -220,9 +206,9 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
|
|
220 |
}
|
221 |
|
222 |
for ( $i = 0; $i < $num_codes; $i++ ) {
|
223 |
-
$code
|
224 |
$codes_hashed[] = wp_hash_password( $code );
|
225 |
-
$codes[]
|
226 |
unset( $code );
|
227 |
}
|
228 |
|
@@ -238,13 +224,13 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
|
|
238 |
* @since 0.1-dev
|
239 |
*/
|
240 |
public function ajax_generate_json() {
|
241 |
-
$user = get_user_by( 'id',
|
242 |
check_ajax_referer( 'two-factor-backup-codes-generate-json-' . $user->ID, 'nonce' );
|
243 |
|
244 |
// Setup the return data.
|
245 |
$codes = $this->generate_codes( $user );
|
246 |
$count = self::codes_remaining_for_user( $user );
|
247 |
-
$i18n
|
248 |
/* translators: %s: count */
|
249 |
'count' => esc_html( sprintf( _n( '%s unused code remaining.', '%s unused codes remaining.', $count, 'two-factor' ), $count ) ),
|
250 |
/* translators: %s: the site's domain */
|
@@ -252,12 +238,7 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
|
|
252 |
);
|
253 |
|
254 |
// Send the response.
|
255 |
-
wp_send_json_success(
|
256 |
-
array(
|
257 |
-
'codes' => $codes,
|
258 |
-
'i18n' => $i18n,
|
259 |
-
)
|
260 |
-
);
|
261 |
}
|
262 |
|
263 |
/**
|
@@ -282,7 +263,7 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
|
|
282 |
* @param WP_User $user WP_User object of the logged-in user.
|
283 |
*/
|
284 |
public function authentication_page( $user ) {
|
285 |
-
require_once ABSPATH .
|
286 |
?>
|
287 |
<p><?php esc_html_e( 'Enter a backup verification code.', 'two-factor' ); ?></p><br/>
|
288 |
<p>
|
@@ -304,8 +285,7 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
|
|
304 |
* @return boolean
|
305 |
*/
|
306 |
public function validate_authentication( $user ) {
|
307 |
-
$
|
308 |
-
return $this->validate_code( $user, filter_var( $backup_code, FILTER_SANITIZE_STRING ) );
|
309 |
}
|
310 |
|
311 |
/**
|
1 |
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
/**
|
3 |
* Class for creating a backup codes provider.
|
4 |
*
|
27 |
*
|
28 |
* @since 0.1-dev
|
29 |
*/
|
30 |
+
static function get_instance() {
|
31 |
static $instance;
|
32 |
$class = __CLASS__;
|
33 |
if ( ! is_a( $instance, $class ) ) {
|
34 |
+
$instance = new $class;
|
35 |
}
|
36 |
return $instance;
|
37 |
}
|
42 |
* @since 0.1-dev
|
43 |
*/
|
44 |
protected function __construct() {
|
45 |
+
add_action( 'two-factor-user-options-' . __CLASS__, array( $this, 'user_options' ) );
|
46 |
add_action( 'admin_notices', array( $this, 'admin_notices' ) );
|
47 |
add_action( 'wp_ajax_two_factor_backup_codes_generate', array( $this, 'ajax_generate_json' ) );
|
48 |
|
58 |
$user = wp_get_current_user();
|
59 |
|
60 |
// Return if the provider is not enabled.
|
61 |
+
if ( ! in_array( __CLASS__, Two_Factor_Core::get_enabled_providers_for_user( $user->ID ) ) ) {
|
62 |
return;
|
63 |
}
|
64 |
|
71 |
<p>
|
72 |
<span>
|
73 |
<?php
|
74 |
+
printf( // WPCS: XSS OK.
|
75 |
+
__( 'Two-Factor: You are out of backup codes and need to <a href="%s">regenerate!</a>', 'two-factor' ),
|
76 |
+
esc_url( get_edit_user_link( $user->ID ) . '#two-factor-backup-codes' )
|
|
|
|
|
|
|
|
|
77 |
);
|
78 |
?>
|
79 |
<span>
|
116 |
*/
|
117 |
public function user_options( $user ) {
|
118 |
$ajax_nonce = wp_create_nonce( 'two-factor-backup-codes-generate-json-' . $user->ID );
|
119 |
+
$count = self::codes_remaining_for_user( $user );
|
120 |
?>
|
121 |
<p id="two-factor-backup-codes">
|
122 |
<button type="button" class="button button-two-factor-backup-codes-generate button-secondary hide-if-no-js">
|
123 |
<?php esc_html_e( 'Generate Verification Codes', 'two-factor' ); ?>
|
124 |
</button>
|
125 |
+
<span class="two-factor-backup-codes-count"><?php
|
126 |
+
echo esc_html( sprintf(
|
|
|
|
|
127 |
/* translators: %s: count */
|
128 |
+
_n( '%s unused code remaining.', '%s unused codes remaining.', $count, 'two-factor' ),
|
129 |
+
$count
|
130 |
+
) );
|
131 |
+
?></span>
|
|
|
|
|
132 |
</p>
|
133 |
<div class="two-factor-backup-codes-wrapper" style="display:none;">
|
134 |
<ol class="two-factor-backup-codes-unused-codes"></ol>
|
163 |
// Update counter.
|
164 |
$( '.two-factor-backup-codes-count' ).html( response.data.i18n.count );
|
165 |
|
166 |
+
// Build the download link
|
167 |
var txt_data = 'data:application/text;charset=utf-8,' + '\n';
|
168 |
txt_data += response.data.i18n.title.replace( /%s/g, document.domain ) + '\n\n';
|
169 |
|
186 |
* @since 0.1-dev
|
187 |
*
|
188 |
* @param WP_User $user WP_User object of the logged-in user.
|
189 |
+
* @param array $args Optional arguments for assinging new codes.
|
190 |
* @return array
|
191 |
*/
|
192 |
public function generate_codes( $user, $args = '' ) {
|
193 |
+
$codes = array();
|
194 |
$codes_hashed = array();
|
195 |
|
196 |
// Check for arguments.
|
206 |
}
|
207 |
|
208 |
for ( $i = 0; $i < $num_codes; $i++ ) {
|
209 |
+
$code = $this->get_code();
|
210 |
$codes_hashed[] = wp_hash_password( $code );
|
211 |
+
$codes[] = $code;
|
212 |
unset( $code );
|
213 |
}
|
214 |
|
224 |
* @since 0.1-dev
|
225 |
*/
|
226 |
public function ajax_generate_json() {
|
227 |
+
$user = get_user_by( 'id', sanitize_text_field( $_POST['user_id'] ) );
|
228 |
check_ajax_referer( 'two-factor-backup-codes-generate-json-' . $user->ID, 'nonce' );
|
229 |
|
230 |
// Setup the return data.
|
231 |
$codes = $this->generate_codes( $user );
|
232 |
$count = self::codes_remaining_for_user( $user );
|
233 |
+
$i18n = array(
|
234 |
/* translators: %s: count */
|
235 |
'count' => esc_html( sprintf( _n( '%s unused code remaining.', '%s unused codes remaining.', $count, 'two-factor' ), $count ) ),
|
236 |
/* translators: %s: the site's domain */
|
238 |
);
|
239 |
|
240 |
// Send the response.
|
241 |
+
wp_send_json_success( array( 'codes' => $codes, 'i18n' => $i18n ) );
|
|
|
|
|
|
|
|
|
|
|
242 |
}
|
243 |
|
244 |
/**
|
263 |
* @param WP_User $user WP_User object of the logged-in user.
|
264 |
*/
|
265 |
public function authentication_page( $user ) {
|
266 |
+
require_once( ABSPATH . '/wp-admin/includes/template.php' );
|
267 |
?>
|
268 |
<p><?php esc_html_e( 'Enter a backup verification code.', 'two-factor' ); ?></p><br/>
|
269 |
<p>
|
285 |
* @return boolean
|
286 |
*/
|
287 |
public function validate_authentication( $user ) {
|
288 |
+
return $this->validate_code( $user, $_POST['two-factor-backup-code'] );
|
|
|
289 |
}
|
290 |
|
291 |
/**
|
providers/{class-two-factor-dummy.php → class.two-factor-dummy.php}
RENAMED
@@ -1,10 +1,4 @@
|
|
1 |
<?php
|
2 |
-
/**
|
3 |
-
* Class for creating a dummy provider.
|
4 |
-
*
|
5 |
-
* @package Two_Factor
|
6 |
-
*/
|
7 |
-
|
8 |
/**
|
9 |
* Class for creating a dummy provider.
|
10 |
*
|
@@ -19,11 +13,11 @@ class Two_Factor_Dummy extends Two_Factor_Provider {
|
|
19 |
*
|
20 |
* @since 0.1-dev
|
21 |
*/
|
22 |
-
|
23 |
static $instance;
|
24 |
$class = __CLASS__;
|
25 |
if ( ! is_a( $instance, $class ) ) {
|
26 |
-
$instance = new $class
|
27 |
}
|
28 |
return $instance;
|
29 |
}
|
@@ -34,7 +28,7 @@ class Two_Factor_Dummy extends Two_Factor_Provider {
|
|
34 |
* @since 0.1-dev
|
35 |
*/
|
36 |
protected function __construct() {
|
37 |
-
add_action( '
|
38 |
return parent::__construct();
|
39 |
}
|
40 |
|
@@ -55,7 +49,7 @@ class Two_Factor_Dummy extends Two_Factor_Provider {
|
|
55 |
* @param WP_User $user WP_User object of the logged-in user.
|
56 |
*/
|
57 |
public function authentication_page( $user ) {
|
58 |
-
require_once ABSPATH .
|
59 |
?>
|
60 |
<p><?php esc_html_e( 'Are you really you?', 'two-factor' ); ?></p>
|
61 |
<?php
|
1 |
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
/**
|
3 |
* Class for creating a dummy provider.
|
4 |
*
|
13 |
*
|
14 |
* @since 0.1-dev
|
15 |
*/
|
16 |
+
static function get_instance() {
|
17 |
static $instance;
|
18 |
$class = __CLASS__;
|
19 |
if ( ! is_a( $instance, $class ) ) {
|
20 |
+
$instance = new $class;
|
21 |
}
|
22 |
return $instance;
|
23 |
}
|
28 |
* @since 0.1-dev
|
29 |
*/
|
30 |
protected function __construct() {
|
31 |
+
add_action( 'two-factor-user-options-' . __CLASS__, array( $this, 'user_options' ) );
|
32 |
return parent::__construct();
|
33 |
}
|
34 |
|
49 |
* @param WP_User $user WP_User object of the logged-in user.
|
50 |
*/
|
51 |
public function authentication_page( $user ) {
|
52 |
+
require_once( ABSPATH . '/wp-admin/includes/template.php' );
|
53 |
?>
|
54 |
<p><?php esc_html_e( 'Are you really you?', 'two-factor' ); ?></p>
|
55 |
<?php
|
providers/{class-two-factor-email.php → class.two-factor-email.php}
RENAMED
@@ -1,10 +1,4 @@
|
|
1 |
<?php
|
2 |
-
/**
|
3 |
-
* Class for creating an email provider.
|
4 |
-
*
|
5 |
-
* @package Two_Factor
|
6 |
-
*/
|
7 |
-
|
8 |
/**
|
9 |
* Class for creating an email provider.
|
10 |
*
|
@@ -17,17 +11,10 @@ class Two_Factor_Email extends Two_Factor_Provider {
|
|
17 |
/**
|
18 |
* The user meta token key.
|
19 |
*
|
20 |
-
* @
|
21 |
*/
|
22 |
const TOKEN_META_KEY = '_two_factor_email_token';
|
23 |
|
24 |
-
/**
|
25 |
-
* Store the timestamp when the token was generated.
|
26 |
-
*
|
27 |
-
* @var string
|
28 |
-
*/
|
29 |
-
const TOKEN_META_KEY_TIMESTAMP = '_two_factor_email_token_timestamp';
|
30 |
-
|
31 |
/**
|
32 |
* Name of the input field used for code resend.
|
33 |
*
|
@@ -40,11 +27,11 @@ class Two_Factor_Email extends Two_Factor_Provider {
|
|
40 |
*
|
41 |
* @since 0.1-dev
|
42 |
*/
|
43 |
-
|
44 |
static $instance;
|
45 |
$class = __CLASS__;
|
46 |
if ( ! is_a( $instance, $class ) ) {
|
47 |
-
$instance = new $class
|
48 |
}
|
49 |
return $instance;
|
50 |
}
|
@@ -55,7 +42,7 @@ class Two_Factor_Email extends Two_Factor_Provider {
|
|
55 |
* @since 0.1-dev
|
56 |
*/
|
57 |
protected function __construct() {
|
58 |
-
add_action( '
|
59 |
return parent::__construct();
|
60 |
}
|
61 |
|
@@ -78,10 +65,7 @@ class Two_Factor_Email extends Two_Factor_Provider {
|
|
78 |
*/
|
79 |
public function generate_token( $user_id ) {
|
80 |
$token = $this->get_code();
|
81 |
-
|
82 |
-
update_user_meta( $user_id, self::TOKEN_META_KEY_TIMESTAMP, time() );
|
83 |
update_user_meta( $user_id, self::TOKEN_META_KEY, wp_hash( $token ) );
|
84 |
-
|
85 |
return $token;
|
86 |
}
|
87 |
|
@@ -96,65 +80,9 @@ class Two_Factor_Email extends Two_Factor_Provider {
|
|
96 |
|
97 |
if ( ! empty( $hashed_token ) ) {
|
98 |
return true;
|
99 |
-
}
|
100 |
-
|
101 |
-
return false;
|
102 |
-
}
|
103 |
-
|
104 |
-
/**
|
105 |
-
* Has the user token validity timestamp expired.
|
106 |
-
*
|
107 |
-
* @param integer $user_id User ID.
|
108 |
-
*
|
109 |
-
* @return boolean
|
110 |
-
*/
|
111 |
-
public function user_token_has_expired( $user_id ) {
|
112 |
-
$token_lifetime = $this->user_token_lifetime( $user_id );
|
113 |
-
$token_ttl = $this->user_token_ttl( $user_id );
|
114 |
-
|
115 |
-
// Invalid token lifetime is considered an expired token.
|
116 |
-
if ( is_int( $token_lifetime ) && $token_lifetime <= $token_ttl ) {
|
117 |
return false;
|
118 |
}
|
119 |
-
|
120 |
-
return true;
|
121 |
-
}
|
122 |
-
|
123 |
-
/**
|
124 |
-
* Get the lifetime of a user token in seconds.
|
125 |
-
*
|
126 |
-
* @param integer $user_id User ID.
|
127 |
-
*
|
128 |
-
* @return integer|null Return `null` if the lifetime can't be measured.
|
129 |
-
*/
|
130 |
-
public function user_token_lifetime( $user_id ) {
|
131 |
-
$timestamp = intval( get_user_meta( $user_id, self::TOKEN_META_KEY_TIMESTAMP, true ) );
|
132 |
-
|
133 |
-
if ( ! empty( $timestamp ) ) {
|
134 |
-
return time() - $timestamp;
|
135 |
-
}
|
136 |
-
|
137 |
-
return null;
|
138 |
-
}
|
139 |
-
|
140 |
-
/**
|
141 |
-
* Return the token time-to-live for a user.
|
142 |
-
*
|
143 |
-
* @param integer $user_id User ID.
|
144 |
-
*
|
145 |
-
* @return integer
|
146 |
-
*/
|
147 |
-
public function user_token_ttl( $user_id ) {
|
148 |
-
$token_ttl = 15 * MINUTE_IN_SECONDS;
|
149 |
-
|
150 |
-
/**
|
151 |
-
* Number of seconds the token is considered valid
|
152 |
-
* after the generation.
|
153 |
-
*
|
154 |
-
* @param integer $token_ttl Token time-to-live in seconds.
|
155 |
-
* @param integer $user_id User ID.
|
156 |
-
*/
|
157 |
-
return (int) apply_filters( 'two_factor_token_ttl', $token_ttl, $user_id );
|
158 |
}
|
159 |
|
160 |
/**
|
@@ -191,11 +119,7 @@ class Two_Factor_Email extends Two_Factor_Provider {
|
|
191 |
return false;
|
192 |
}
|
193 |
|
194 |
-
|
195 |
-
return false;
|
196 |
-
}
|
197 |
-
|
198 |
-
// Ensure the token can be used only once.
|
199 |
$this->delete_token( $user_id );
|
200 |
|
201 |
return true;
|
@@ -228,23 +152,6 @@ class Two_Factor_Email extends Two_Factor_Provider {
|
|
228 |
/* translators: %s: token */
|
229 |
$message = wp_strip_all_tags( sprintf( __( 'Enter %s to log in.', 'two-factor' ), $token ) );
|
230 |
|
231 |
-
/**
|
232 |
-
* Filter the token email subject.
|
233 |
-
*
|
234 |
-
* @param string $subject The email subject line.
|
235 |
-
* @param int $user_id The ID of the user.
|
236 |
-
*/
|
237 |
-
$subject = apply_filters( 'two_factor_token_email_subject', $subject, $user->ID );
|
238 |
-
|
239 |
-
/**
|
240 |
-
* Filter the token email message.
|
241 |
-
*
|
242 |
-
* @param string $message The email message.
|
243 |
-
* @param string $token The token.
|
244 |
-
* @param int $user_id The ID of the user.
|
245 |
-
*/
|
246 |
-
$message = apply_filters( 'two_factor_token_email_message', $message, $token, $user->ID );
|
247 |
-
|
248 |
return wp_mail( $user->user_email, $subject, $message );
|
249 |
}
|
250 |
|
@@ -260,16 +167,16 @@ class Two_Factor_Email extends Two_Factor_Provider {
|
|
260 |
return;
|
261 |
}
|
262 |
|
263 |
-
if ( ! $this->user_has_token( $user->ID )
|
264 |
$this->generate_and_email_token( $user );
|
265 |
}
|
266 |
|
267 |
-
require_once ABSPATH .
|
268 |
?>
|
269 |
<p><?php esc_html_e( 'A verification code has been sent to the email address associated with your account.', 'two-factor' ); ?></p>
|
270 |
<p>
|
271 |
<label for="authcode"><?php esc_html_e( 'Verification Code:', 'two-factor' ); ?></label>
|
272 |
-
<input type="tel" name="two-factor-email-code" id="authcode" class="input" value="" size="20" />
|
273 |
<?php submit_button( __( 'Log In', 'two-factor' ) ); ?>
|
274 |
</p>
|
275 |
<p class="two-factor-email-resend">
|
@@ -317,10 +224,7 @@ class Two_Factor_Email extends Two_Factor_Provider {
|
|
317 |
return false;
|
318 |
}
|
319 |
|
320 |
-
|
321 |
-
$code = trim( sanitize_text_field( $_REQUEST['two-factor-email-code'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, handled by the core method already.
|
322 |
-
|
323 |
-
return $this->validate_token( $user->ID, $code );
|
324 |
}
|
325 |
|
326 |
/**
|
@@ -347,13 +251,11 @@ class Two_Factor_Email extends Two_Factor_Provider {
|
|
347 |
?>
|
348 |
<div>
|
349 |
<?php
|
350 |
-
echo esc_html(
|
351 |
-
sprintf(
|
352 |
/* translators: %s: email address */
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
);
|
357 |
?>
|
358 |
</div>
|
359 |
<?php
|
1 |
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
/**
|
3 |
* Class for creating an email provider.
|
4 |
*
|
11 |
/**
|
12 |
* The user meta token key.
|
13 |
*
|
14 |
+
* @type string
|
15 |
*/
|
16 |
const TOKEN_META_KEY = '_two_factor_email_token';
|
17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
/**
|
19 |
* Name of the input field used for code resend.
|
20 |
*
|
27 |
*
|
28 |
* @since 0.1-dev
|
29 |
*/
|
30 |
+
static function get_instance() {
|
31 |
static $instance;
|
32 |
$class = __CLASS__;
|
33 |
if ( ! is_a( $instance, $class ) ) {
|
34 |
+
$instance = new $class;
|
35 |
}
|
36 |
return $instance;
|
37 |
}
|
42 |
* @since 0.1-dev
|
43 |
*/
|
44 |
protected function __construct() {
|
45 |
+
add_action( 'two-factor-user-options-' . __CLASS__, array( $this, 'user_options' ) );
|
46 |
return parent::__construct();
|
47 |
}
|
48 |
|
65 |
*/
|
66 |
public function generate_token( $user_id ) {
|
67 |
$token = $this->get_code();
|
|
|
|
|
68 |
update_user_meta( $user_id, self::TOKEN_META_KEY, wp_hash( $token ) );
|
|
|
69 |
return $token;
|
70 |
}
|
71 |
|
80 |
|
81 |
if ( ! empty( $hashed_token ) ) {
|
82 |
return true;
|
83 |
+
} else {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
return false;
|
85 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
}
|
87 |
|
88 |
/**
|
119 |
return false;
|
120 |
}
|
121 |
|
122 |
+
// Ensure that the token can't be re-used.
|
|
|
|
|
|
|
|
|
123 |
$this->delete_token( $user_id );
|
124 |
|
125 |
return true;
|
152 |
/* translators: %s: token */
|
153 |
$message = wp_strip_all_tags( sprintf( __( 'Enter %s to log in.', 'two-factor' ), $token ) );
|
154 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
return wp_mail( $user->user_email, $subject, $message );
|
156 |
}
|
157 |
|
167 |
return;
|
168 |
}
|
169 |
|
170 |
+
if ( ! $this->user_has_token( $user->ID ) ) {
|
171 |
$this->generate_and_email_token( $user );
|
172 |
}
|
173 |
|
174 |
+
require_once( ABSPATH . '/wp-admin/includes/template.php' );
|
175 |
?>
|
176 |
<p><?php esc_html_e( 'A verification code has been sent to the email address associated with your account.', 'two-factor' ); ?></p>
|
177 |
<p>
|
178 |
<label for="authcode"><?php esc_html_e( 'Verification Code:', 'two-factor' ); ?></label>
|
179 |
+
<input type="tel" name="two-factor-email-code" id="authcode" class="input" value="" size="20" pattern="[0-9]*" />
|
180 |
<?php submit_button( __( 'Log In', 'two-factor' ) ); ?>
|
181 |
</p>
|
182 |
<p class="two-factor-email-resend">
|
224 |
return false;
|
225 |
}
|
226 |
|
227 |
+
return $this->validate_token( $user->ID, $_REQUEST['two-factor-email-code'] );
|
|
|
|
|
|
|
228 |
}
|
229 |
|
230 |
/**
|
251 |
?>
|
252 |
<div>
|
253 |
<?php
|
254 |
+
echo esc_html( sprintf(
|
|
|
255 |
/* translators: %s: email address */
|
256 |
+
__( 'Authentication codes will be sent to %s.', 'two-factor' ),
|
257 |
+
$email
|
258 |
+
) );
|
|
|
259 |
?>
|
260 |
</div>
|
261 |
<?php
|
providers/{class-two-factor-fido-u2f-admin-list-table.php → class.two-factor-fido-u2f-admin-list-table.php}
RENAMED
@@ -1,10 +1,4 @@
|
|
1 |
<?php
|
2 |
-
/**
|
3 |
-
* Class for displaying the list of security key items.
|
4 |
-
*
|
5 |
-
* @package Two_Factor
|
6 |
-
*/
|
7 |
-
|
8 |
// Load the parent class if it doesn't exist.
|
9 |
if ( ! class_exists( 'WP_List_Table' ) ) {
|
10 |
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
|
@@ -30,7 +24,7 @@ class Two_Factor_FIDO_U2F_Admin_List_Table extends WP_List_Table {
|
|
30 |
public function get_columns() {
|
31 |
return array(
|
32 |
'name' => wp_strip_all_tags( __( 'Name', 'two-factor' ) ),
|
33 |
-
'added'
|
34 |
'last_used' => wp_strip_all_tags( __( 'Last Used', 'two-factor' ) ),
|
35 |
);
|
36 |
}
|
@@ -41,10 +35,10 @@ class Two_Factor_FIDO_U2F_Admin_List_Table extends WP_List_Table {
|
|
41 |
* @since 0.1-dev
|
42 |
*/
|
43 |
public function prepare_items() {
|
44 |
-
$columns
|
45 |
-
$hidden
|
46 |
-
$sortable
|
47 |
-
$primary
|
48 |
$this->_column_headers = array( $columns, $hidden, $sortable, $primary );
|
49 |
}
|
50 |
|
@@ -61,20 +55,20 @@ class Two_Factor_FIDO_U2F_Admin_List_Table extends WP_List_Table {
|
|
61 |
protected function column_default( $item, $column_name ) {
|
62 |
switch ( $column_name ) {
|
63 |
case 'name':
|
64 |
-
$out
|
65 |
$out .= '<div class="name">' . esc_html( $item->name ) . '</div>';
|
66 |
$out .= '</div>';
|
67 |
|
68 |
$actions = array(
|
69 |
'rename hide-if-no-js' => Two_Factor_FIDO_U2F_Admin::rename_link( $item ),
|
70 |
-
'delete'
|
71 |
);
|
72 |
|
73 |
return esc_html( $item->name ) . $out . self::row_actions( $actions );
|
74 |
case 'added':
|
75 |
-
return
|
76 |
case 'last_used':
|
77 |
-
return
|
78 |
default:
|
79 |
return 'WTF^^?';
|
80 |
}
|
@@ -102,7 +96,7 @@ class Two_Factor_FIDO_U2F_Admin_List_Table extends WP_List_Table {
|
|
102 |
*/
|
103 |
public function single_row( $item ) {
|
104 |
?>
|
105 |
-
<tr id="key-<?php echo esc_attr( $item->keyHandle );
|
106 |
<?php $this->single_row_columns( $item ); ?>
|
107 |
</tr>
|
108 |
<?php
|
@@ -121,6 +115,8 @@ class Two_Factor_FIDO_U2F_Admin_List_Table extends WP_List_Table {
|
|
121 |
<td colspan="<?php echo esc_attr( $this->get_column_count() ); ?>" class="colspanchange">
|
122 |
<fieldset>
|
123 |
<div class="inline-edit-col">
|
|
|
|
|
124 |
<label>
|
125 |
<span class="title"><?php esc_html_e( 'Name', 'two-factor' ); ?></span>
|
126 |
<span class="input-text-wrap"><input type="text" name="name" class="ptitle" value="" /></span>
|
@@ -128,11 +124,7 @@ class Two_Factor_FIDO_U2F_Admin_List_Table extends WP_List_Table {
|
|
128 |
</div>
|
129 |
</fieldset>
|
130 |
<?php
|
131 |
-
$core_columns
|
132 |
-
'name' => true,
|
133 |
-
'added' => true,
|
134 |
-
'last_used' => true,
|
135 |
-
);
|
136 |
list( $columns ) = $this->get_column_info();
|
137 |
foreach ( $columns as $column_name => $column_display_name ) {
|
138 |
if ( isset( $core_columns[ $column_name ] ) ) {
|
1 |
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
// Load the parent class if it doesn't exist.
|
3 |
if ( ! class_exists( 'WP_List_Table' ) ) {
|
4 |
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
|
24 |
public function get_columns() {
|
25 |
return array(
|
26 |
'name' => wp_strip_all_tags( __( 'Name', 'two-factor' ) ),
|
27 |
+
'added' => wp_strip_all_tags( __( 'Added', 'two-factor' ) ),
|
28 |
'last_used' => wp_strip_all_tags( __( 'Last Used', 'two-factor' ) ),
|
29 |
);
|
30 |
}
|
35 |
* @since 0.1-dev
|
36 |
*/
|
37 |
public function prepare_items() {
|
38 |
+
$columns = $this->get_columns();
|
39 |
+
$hidden = array();
|
40 |
+
$sortable = array();
|
41 |
+
$primary = 'name';
|
42 |
$this->_column_headers = array( $columns, $hidden, $sortable, $primary );
|
43 |
}
|
44 |
|
55 |
protected function column_default( $item, $column_name ) {
|
56 |
switch ( $column_name ) {
|
57 |
case 'name':
|
58 |
+
$out = '<div class="hidden" id="inline_' . esc_attr( $item->keyHandle ) . '">';
|
59 |
$out .= '<div class="name">' . esc_html( $item->name ) . '</div>';
|
60 |
$out .= '</div>';
|
61 |
|
62 |
$actions = array(
|
63 |
'rename hide-if-no-js' => Two_Factor_FIDO_U2F_Admin::rename_link( $item ),
|
64 |
+
'delete' => Two_Factor_FIDO_U2F_Admin::delete_link( $item ),
|
65 |
);
|
66 |
|
67 |
return esc_html( $item->name ) . $out . self::row_actions( $actions );
|
68 |
case 'added':
|
69 |
+
return date( get_option( 'date_format', 'r' ), $item->added );
|
70 |
case 'last_used':
|
71 |
+
return date( get_option( 'date_format', 'r' ), $item->last_used );
|
72 |
default:
|
73 |
return 'WTF^^?';
|
74 |
}
|
96 |
*/
|
97 |
public function single_row( $item ) {
|
98 |
?>
|
99 |
+
<tr id="key-<?php echo esc_attr( $item->keyHandle ); ?>">
|
100 |
<?php $this->single_row_columns( $item ); ?>
|
101 |
</tr>
|
102 |
<?php
|
115 |
<td colspan="<?php echo esc_attr( $this->get_column_count() ); ?>" class="colspanchange">
|
116 |
<fieldset>
|
117 |
<div class="inline-edit-col">
|
118 |
+
<h4><?php esc_html_e( 'Quick Edit', 'two-factor' ); ?></h4>
|
119 |
+
|
120 |
<label>
|
121 |
<span class="title"><?php esc_html_e( 'Name', 'two-factor' ); ?></span>
|
122 |
<span class="input-text-wrap"><input type="text" name="name" class="ptitle" value="" /></span>
|
124 |
</div>
|
125 |
</fieldset>
|
126 |
<?php
|
127 |
+
$core_columns = array( 'name' => true, 'added' => true, 'last_used' => true );
|
|
|
|
|
|
|
|
|
128 |
list( $columns ) = $this->get_column_info();
|
129 |
foreach ( $columns as $column_name => $column_display_name ) {
|
130 |
if ( isset( $core_columns[ $column_name ] ) ) {
|
providers/{class-two-factor-fido-u2f-admin.php → class.two-factor-fido-u2f-admin.php}
RENAMED
@@ -1,10 +1,4 @@
|
|
1 |
<?php
|
2 |
-
/**
|
3 |
-
* Class for registering & modifying FIDO U2F security keys.
|
4 |
-
*
|
5 |
-
* @package Two_Factor
|
6 |
-
*/
|
7 |
-
|
8 |
/**
|
9 |
* Class for registering & modifying FIDO U2F security keys.
|
10 |
*
|
@@ -30,13 +24,13 @@ class Two_Factor_FIDO_U2F_Admin {
|
|
30 |
* @static
|
31 |
*/
|
32 |
public static function add_hooks() {
|
33 |
-
add_action( 'admin_enqueue_scripts',
|
34 |
add_action( 'show_user_security_settings', array( __CLASS__, 'show_user_profile' ) );
|
35 |
-
add_action( 'personal_options_update',
|
36 |
-
add_action( 'edit_user_profile_update',
|
37 |
-
add_action( 'load-profile.php',
|
38 |
-
add_action( 'load-user-edit.php',
|
39 |
-
add_action( 'wp_ajax_inline-save-key',
|
40 |
}
|
41 |
|
42 |
/**
|
@@ -50,20 +44,16 @@ class Two_Factor_FIDO_U2F_Admin {
|
|
50 |
* @param string $hook Current page.
|
51 |
*/
|
52 |
public static function enqueue_assets( $hook ) {
|
53 |
-
if ( ! in_array( $hook, array( 'user-edit.php', 'profile.php' )
|
54 |
-
return;
|
55 |
-
}
|
56 |
-
|
57 |
-
$user_id = Two_Factor_Core::current_user_being_edited();
|
58 |
-
if ( ! $user_id ) {
|
59 |
return;
|
60 |
}
|
61 |
|
|
|
62 |
$security_keys = Two_Factor_FIDO_U2F::get_security_keys( $user_id );
|
63 |
|
64 |
-
// @todo Ensure that scripts don't fail because of missing u2fL10n
|
65 |
try {
|
66 |
-
$data
|
67 |
list( $req,$sigs ) = $data;
|
68 |
|
69 |
update_user_meta( $user_id, self::REGISTER_DATA_USER_META_KEY, $req );
|
@@ -75,14 +65,14 @@ class Two_Factor_FIDO_U2F_Admin {
|
|
75 |
'fido-u2f-admin',
|
76 |
plugins_url( 'css/fido-u2f-admin.css', __FILE__ ),
|
77 |
null,
|
78 |
-
|
79 |
);
|
80 |
|
81 |
wp_enqueue_script(
|
82 |
'fido-u2f-admin',
|
83 |
plugins_url( 'js/fido-u2f-admin.js', __FILE__ ),
|
84 |
array( 'jquery', 'fido-u2f-api' ),
|
85 |
-
|
86 |
true
|
87 |
);
|
88 |
|
@@ -91,15 +81,14 @@ class Two_Factor_FIDO_U2F_Admin {
|
|
91 |
*/
|
92 |
|
93 |
$translation_array = array(
|
94 |
-
'user_id' => $user_id,
|
95 |
'register' => array(
|
96 |
'request' => $req,
|
97 |
-
'sigs'
|
98 |
),
|
99 |
-
'text'
|
100 |
-
'insert'
|
101 |
-
'error'
|
102 |
-
'error_codes'
|
103 |
// Map u2f.ErrorCodes to error messages.
|
104 |
0 => esc_html__( 'Request OK.', 'two-factor' ),
|
105 |
1 => esc_html__( 'Other U2F error.', 'two-factor' ),
|
@@ -108,7 +97,7 @@ class Two_Factor_FIDO_U2F_Admin {
|
|
108 |
4 => esc_html__( 'U2F device ineligible.', 'two-factor' ),
|
109 |
5 => esc_html__( 'U2F request timeout reached.', 'two-factor' ),
|
110 |
),
|
111 |
-
'u2f_not_supported' => esc_html__( 'FIDO U2F
|
112 |
),
|
113 |
);
|
114 |
|
@@ -126,7 +115,7 @@ class Two_Factor_FIDO_U2F_Admin {
|
|
126 |
'inline-edit-key',
|
127 |
plugins_url( 'js/fido-u2f-admin-inline-edit.js', __FILE__ ),
|
128 |
array( 'jquery' ),
|
129 |
-
|
130 |
true
|
131 |
);
|
132 |
|
@@ -139,18 +128,6 @@ class Two_Factor_FIDO_U2F_Admin {
|
|
139 |
);
|
140 |
}
|
141 |
|
142 |
-
/**
|
143 |
-
* Return the current asset version number.
|
144 |
-
*
|
145 |
-
* Added as own helper to allow swapping the implementation once we inject
|
146 |
-
* it as a dependency.
|
147 |
-
*
|
148 |
-
* @return string
|
149 |
-
*/
|
150 |
-
protected static function asset_version() {
|
151 |
-
return Two_Factor_FIDO_U2F::asset_version();
|
152 |
-
}
|
153 |
-
|
154 |
/**
|
155 |
* Display the security key section in a users profile.
|
156 |
*
|
@@ -208,8 +185,8 @@ class Two_Factor_FIDO_U2F_Admin {
|
|
208 |
<p><a href="https://support.google.com/accounts/answer/6103523"><?php esc_html_e( 'You can find FIDO U2F Security Key devices for sale from here.', 'two-factor' ); ?></a></p>
|
209 |
|
210 |
<?php
|
211 |
-
require TWO_FACTOR_DIR . 'providers/class
|
212 |
-
$u2f_list_table
|
213 |
$u2f_list_table->items = $security_keys;
|
214 |
$u2f_list_table->prepare_items();
|
215 |
$u2f_list_table->display();
|
@@ -238,7 +215,7 @@ class Two_Factor_FIDO_U2F_Admin {
|
|
238 |
|
239 |
try {
|
240 |
$response = json_decode( stripslashes( $_POST['u2f_response'] ) );
|
241 |
-
$reg
|
242 |
$reg->new = true;
|
243 |
|
244 |
Two_Factor_FIDO_U2F::add_security_key( $user_id, $reg );
|
@@ -248,14 +225,9 @@ class Two_Factor_FIDO_U2F_Admin {
|
|
248 |
|
249 |
delete_user_meta( $user_id, self::REGISTER_DATA_USER_META_KEY );
|
250 |
|
251 |
-
wp_safe_redirect(
|
252 |
-
|
253 |
-
|
254 |
-
'new_app_pass' => 1,
|
255 |
-
),
|
256 |
-
wp_get_referer()
|
257 |
-
) . '#security-keys-section'
|
258 |
-
);
|
259 |
exit;
|
260 |
}
|
261 |
}
|
@@ -271,11 +243,9 @@ class Two_Factor_FIDO_U2F_Admin {
|
|
271 |
* @static
|
272 |
*/
|
273 |
public static function catch_delete_security_key() {
|
274 |
-
$user_id =
|
275 |
-
|
276 |
-
if ( ! empty( $user_id ) && ! empty( $_REQUEST['delete_security_key'] ) ) {
|
277 |
$slug = $_REQUEST['delete_security_key'];
|
278 |
-
|
279 |
check_admin_referer( "delete_security_key-{$slug}", '_nonce_delete_security_key' );
|
280 |
|
281 |
Two_Factor_FIDO_U2F::delete_security_key( $user_id, $slug );
|
@@ -311,7 +281,7 @@ class Two_Factor_FIDO_U2F_Admin {
|
|
311 |
* @return string
|
312 |
*/
|
313 |
public static function delete_link( $item ) {
|
314 |
-
$delete_link = add_query_arg( 'delete_security_key', $item->keyHandle );
|
315 |
$delete_link = wp_nonce_url( $delete_link, "delete_security_key-{$item->keyHandle}", '_nonce_delete_security_key' );
|
316 |
return sprintf( '<a href="%1$s">%2$s</a>', esc_url( $delete_link ), esc_html__( 'Delete', 'two-factor' ) );
|
317 |
}
|
@@ -327,21 +297,22 @@ class Two_Factor_FIDO_U2F_Admin {
|
|
327 |
public static function wp_ajax_inline_save() {
|
328 |
check_ajax_referer( 'keyinlineeditnonce', '_inline_edit' );
|
329 |
|
330 |
-
require TWO_FACTOR_DIR . 'providers/class
|
331 |
$wp_list_table = new Two_Factor_FIDO_U2F_Admin_List_Table();
|
332 |
|
333 |
if ( ! isset( $_POST['keyHandle'] ) ) {
|
334 |
wp_die();
|
335 |
}
|
336 |
|
337 |
-
$user_id
|
|
|
338 |
$security_keys = Two_Factor_FIDO_U2F::get_security_keys( $user_id );
|
339 |
if ( ! $security_keys ) {
|
340 |
wp_die();
|
341 |
}
|
342 |
|
343 |
foreach ( $security_keys as &$key ) {
|
344 |
-
if ( $key->keyHandle === $_POST['keyHandle'] ) {
|
345 |
break;
|
346 |
}
|
347 |
}
|
1 |
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
/**
|
3 |
* Class for registering & modifying FIDO U2F security keys.
|
4 |
*
|
24 |
* @static
|
25 |
*/
|
26 |
public static function add_hooks() {
|
27 |
+
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ) );
|
28 |
add_action( 'show_user_security_settings', array( __CLASS__, 'show_user_profile' ) );
|
29 |
+
add_action( 'personal_options_update', array( __CLASS__, 'catch_submission' ), 0 );
|
30 |
+
add_action( 'edit_user_profile_update', array( __CLASS__, 'catch_submission' ), 0 );
|
31 |
+
add_action( 'load-profile.php', array( __CLASS__, 'catch_delete_security_key' ) );
|
32 |
+
add_action( 'load-user-edit.php', array( __CLASS__, 'catch_delete_security_key' ) );
|
33 |
+
add_action( 'wp_ajax_inline-save-key', array( __CLASS__, 'wp_ajax_inline_save' ) );
|
34 |
}
|
35 |
|
36 |
/**
|
44 |
* @param string $hook Current page.
|
45 |
*/
|
46 |
public static function enqueue_assets( $hook ) {
|
47 |
+
if ( ! in_array( $hook, array( 'user-edit.php', 'profile.php' ) ) ) {
|
|
|
|
|
|
|
|
|
|
|
48 |
return;
|
49 |
}
|
50 |
|
51 |
+
$user_id = get_current_user_id();
|
52 |
$security_keys = Two_Factor_FIDO_U2F::get_security_keys( $user_id );
|
53 |
|
54 |
+
// @todo Ensure that scripts don't fail because of missing u2fL10n
|
55 |
try {
|
56 |
+
$data = Two_Factor_FIDO_U2F::$u2f->getRegisterData( $security_keys );
|
57 |
list( $req,$sigs ) = $data;
|
58 |
|
59 |
update_user_meta( $user_id, self::REGISTER_DATA_USER_META_KEY, $req );
|
65 |
'fido-u2f-admin',
|
66 |
plugins_url( 'css/fido-u2f-admin.css', __FILE__ ),
|
67 |
null,
|
68 |
+
'0.1.0-dev.1'
|
69 |
);
|
70 |
|
71 |
wp_enqueue_script(
|
72 |
'fido-u2f-admin',
|
73 |
plugins_url( 'js/fido-u2f-admin.js', __FILE__ ),
|
74 |
array( 'jquery', 'fido-u2f-api' ),
|
75 |
+
'0.1.0-dev.3',
|
76 |
true
|
77 |
);
|
78 |
|
81 |
*/
|
82 |
|
83 |
$translation_array = array(
|
|
|
84 |
'register' => array(
|
85 |
'request' => $req,
|
86 |
+
'sigs' => $sigs,
|
87 |
),
|
88 |
+
'text' => array(
|
89 |
+
'insert' => esc_html__( 'Now insert (and tap) your Security Key.', 'two-factor' ),
|
90 |
+
'error' => esc_html__( 'U2F request failed.', 'two-factor' ),
|
91 |
+
'error_codes' => array(
|
92 |
// Map u2f.ErrorCodes to error messages.
|
93 |
0 => esc_html__( 'Request OK.', 'two-factor' ),
|
94 |
1 => esc_html__( 'Other U2F error.', 'two-factor' ),
|
97 |
4 => esc_html__( 'U2F device ineligible.', 'two-factor' ),
|
98 |
5 => esc_html__( 'U2F request timeout reached.', 'two-factor' ),
|
99 |
),
|
100 |
+
'u2f_not_supported' => esc_html__( 'FIDO U2F is not supported in your web browser. Try using Google Chrome.', 'two-factor' ),
|
101 |
),
|
102 |
);
|
103 |
|
115 |
'inline-edit-key',
|
116 |
plugins_url( 'js/fido-u2f-admin-inline-edit.js', __FILE__ ),
|
117 |
array( 'jquery' ),
|
118 |
+
'0.1.0-dev.1',
|
119 |
true
|
120 |
);
|
121 |
|
128 |
);
|
129 |
}
|
130 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
131 |
/**
|
132 |
* Display the security key section in a users profile.
|
133 |
*
|
185 |
<p><a href="https://support.google.com/accounts/answer/6103523"><?php esc_html_e( 'You can find FIDO U2F Security Key devices for sale from here.', 'two-factor' ); ?></a></p>
|
186 |
|
187 |
<?php
|
188 |
+
require( TWO_FACTOR_DIR . 'providers/class.two-factor-fido-u2f-admin-list-table.php' );
|
189 |
+
$u2f_list_table = new Two_Factor_FIDO_U2F_Admin_List_Table();
|
190 |
$u2f_list_table->items = $security_keys;
|
191 |
$u2f_list_table->prepare_items();
|
192 |
$u2f_list_table->display();
|
215 |
|
216 |
try {
|
217 |
$response = json_decode( stripslashes( $_POST['u2f_response'] ) );
|
218 |
+
$reg = Two_Factor_FIDO_U2F::$u2f->doRegister( get_user_meta( $user_id, self::REGISTER_DATA_USER_META_KEY, true ), $response );
|
219 |
$reg->new = true;
|
220 |
|
221 |
Two_Factor_FIDO_U2F::add_security_key( $user_id, $reg );
|
225 |
|
226 |
delete_user_meta( $user_id, self::REGISTER_DATA_USER_META_KEY );
|
227 |
|
228 |
+
wp_safe_redirect( add_query_arg( array(
|
229 |
+
'new_app_pass' => 1,
|
230 |
+
), wp_get_referer() ) . '#security-keys-section' );
|
|
|
|
|
|
|
|
|
|
|
231 |
exit;
|
232 |
}
|
233 |
}
|
243 |
* @static
|
244 |
*/
|
245 |
public static function catch_delete_security_key() {
|
246 |
+
$user_id = get_current_user_id();
|
247 |
+
if ( ! empty( $_REQUEST['delete_security_key'] ) ) {
|
|
|
248 |
$slug = $_REQUEST['delete_security_key'];
|
|
|
249 |
check_admin_referer( "delete_security_key-{$slug}", '_nonce_delete_security_key' );
|
250 |
|
251 |
Two_Factor_FIDO_U2F::delete_security_key( $user_id, $slug );
|
281 |
* @return string
|
282 |
*/
|
283 |
public static function delete_link( $item ) {
|
284 |
+
$delete_link = add_query_arg( 'delete_security_key', $item->keyHandle );
|
285 |
$delete_link = wp_nonce_url( $delete_link, "delete_security_key-{$item->keyHandle}", '_nonce_delete_security_key' );
|
286 |
return sprintf( '<a href="%1$s">%2$s</a>', esc_url( $delete_link ), esc_html__( 'Delete', 'two-factor' ) );
|
287 |
}
|
297 |
public static function wp_ajax_inline_save() {
|
298 |
check_ajax_referer( 'keyinlineeditnonce', '_inline_edit' );
|
299 |
|
300 |
+
require( TWO_FACTOR_DIR . 'providers/class.two-factor-fido-u2f-admin-list-table.php' );
|
301 |
$wp_list_table = new Two_Factor_FIDO_U2F_Admin_List_Table();
|
302 |
|
303 |
if ( ! isset( $_POST['keyHandle'] ) ) {
|
304 |
wp_die();
|
305 |
}
|
306 |
|
307 |
+
$user_id = get_current_user_id();
|
308 |
+
|
309 |
$security_keys = Two_Factor_FIDO_U2F::get_security_keys( $user_id );
|
310 |
if ( ! $security_keys ) {
|
311 |
wp_die();
|
312 |
}
|
313 |
|
314 |
foreach ( $security_keys as &$key ) {
|
315 |
+
if ( $key->keyHandle === $_POST['keyHandle'] ) {
|
316 |
break;
|
317 |
}
|
318 |
}
|
providers/{class-two-factor-fido-u2f.php → class.two-factor-fido-u2f.php}
RENAMED
@@ -1,10 +1,4 @@
|
|
1 |
<?php
|
2 |
-
/**
|
3 |
-
* Class for creating a FIDO Universal 2nd Factor provider.
|
4 |
-
*
|
5 |
-
* @package Two_Factor
|
6 |
-
*/
|
7 |
-
|
8 |
/**
|
9 |
* Class for creating a FIDO Universal 2nd Factor provider.
|
10 |
*
|
@@ -35,19 +29,12 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
|
|
35 |
*/
|
36 |
const AUTH_DATA_USER_META_KEY = '_two_factor_fido_u2f_login_request';
|
37 |
|
38 |
-
/**
|
39 |
-
* Version number for the bundled assets.
|
40 |
-
*
|
41 |
-
* @var string
|
42 |
-
*/
|
43 |
-
const U2F_ASSET_VERSION = '0.2.1';
|
44 |
-
|
45 |
/**
|
46 |
* Ensures only one instance of this class exists in memory at any one time.
|
47 |
*
|
48 |
* @return \Two_Factor_FIDO_U2F
|
49 |
*/
|
50 |
-
|
51 |
static $instance;
|
52 |
|
53 |
if ( ! isset( $instance ) ) {
|
@@ -67,33 +54,33 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
|
|
67 |
return;
|
68 |
}
|
69 |
|
70 |
-
require_once TWO_FACTOR_DIR . 'includes/Yubico/U2F.php';
|
71 |
self::$u2f = new u2flib_server\U2F( self::get_u2f_app_id() );
|
72 |
|
73 |
-
require_once TWO_FACTOR_DIR . 'providers/class
|
74 |
Two_Factor_FIDO_U2F_Admin::add_hooks();
|
75 |
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
|
81 |
-
add_action( '
|
82 |
|
83 |
return parent::__construct();
|
84 |
}
|
85 |
|
86 |
-
/**
|
87 |
-
* Get the asset version number.
|
88 |
-
*
|
89 |
-
* TODO: There should be a plugin-level helper for getting the current plugin version.
|
90 |
-
*
|
91 |
-
* @return string
|
92 |
-
*/
|
93 |
-
public static function asset_version() {
|
94 |
-
return self::U2F_ASSET_VERSION;
|
95 |
-
}
|
96 |
-
|
97 |
/**
|
98 |
* Return the U2F AppId. U2F requires the AppID to use HTTPS
|
99 |
* and a top-level domain.
|
@@ -116,31 +103,16 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
|
|
116 |
* @since 0.1-dev
|
117 |
*/
|
118 |
public function get_label() {
|
119 |
-
return _x( 'FIDO
|
120 |
}
|
121 |
|
122 |
/**
|
123 |
-
*
|
124 |
-
* registering keys in the WP admin.
|
125 |
*
|
126 |
-
* @
|
127 |
*/
|
128 |
-
public
|
129 |
-
|
130 |
-
'fido-u2f-api',
|
131 |
-
plugins_url( 'includes/Google/u2f-api.js', dirname( __FILE__ ) ),
|
132 |
-
null,
|
133 |
-
self::asset_version(),
|
134 |
-
true
|
135 |
-
);
|
136 |
-
|
137 |
-
wp_register_script(
|
138 |
-
'fido-u2f-login',
|
139 |
-
plugins_url( 'js/fido-u2f-login.js', __FILE__ ),
|
140 |
-
array( 'jquery', 'fido-u2f-api' ),
|
141 |
-
self::asset_version(),
|
142 |
-
true
|
143 |
-
);
|
144 |
}
|
145 |
|
146 |
/**
|
@@ -152,9 +124,9 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
|
|
152 |
* @return null
|
153 |
*/
|
154 |
public function authentication_page( $user ) {
|
155 |
-
require_once ABSPATH . '/wp-admin/includes/template.php';
|
156 |
|
157 |
-
// U2F doesn't work without HTTPS
|
158 |
if ( ! is_ssl() ) {
|
159 |
?>
|
160 |
<p><?php esc_html_e( 'U2F requires an HTTPS connection. Please use an alternative 2nd factor method.', 'two-factor' ); ?></p>
|
@@ -208,7 +180,7 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
|
|
208 |
try {
|
209 |
$reg = self::$u2f->doAuthenticate( $requests, $keys, $response );
|
210 |
|
211 |
-
$reg->last_used =
|
212 |
|
213 |
self::update_security_key( $user->ID, $reg );
|
214 |
|
@@ -261,8 +233,8 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
|
|
261 |
|
262 |
if (
|
263 |
! is_object( $register )
|
264 |
-
|| ! property_exists( $register, 'keyHandle' ) || empty( $register->keyHandle )
|
265 |
-
|| ! property_exists( $register, 'publicKey' ) || empty( $register->publicKey )
|
266 |
|| ! property_exists( $register, 'certificate' ) || empty( $register->certificate )
|
267 |
|| ! property_exists( $register, 'counter' ) || ( -1 > $register->counter )
|
268 |
) {
|
@@ -270,14 +242,14 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
|
|
270 |
}
|
271 |
|
272 |
$register = array(
|
273 |
-
'keyHandle' => $register->keyHandle,
|
274 |
-
'publicKey' => $register->publicKey,
|
275 |
'certificate' => $register->certificate,
|
276 |
'counter' => $register->counter,
|
277 |
);
|
278 |
|
279 |
$register['name'] = __( 'New Security Key', 'two-factor' );
|
280 |
-
$register['added'] =
|
281 |
$register['last_used'] = $register['added'];
|
282 |
|
283 |
return add_user_meta( $user_id, self::REGISTERED_KEY_USER_META_KEY, $register );
|
@@ -328,8 +300,8 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
|
|
328 |
|
329 |
if (
|
330 |
! is_object( $data )
|
331 |
-
|| ! property_exists( $data, 'keyHandle' ) || empty( $data->keyHandle )
|
332 |
-
|| ! property_exists( $data, 'publicKey' ) || empty( $data->publicKey )
|
333 |
|| ! property_exists( $data, 'certificate' ) || empty( $data->certificate )
|
334 |
|| ! property_exists( $data, 'counter' ) || ( -1 > $data->counter )
|
335 |
) {
|
@@ -339,7 +311,7 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
|
|
339 |
$keys = self::get_security_keys( $user_id );
|
340 |
if ( $keys ) {
|
341 |
foreach ( $keys as $key ) {
|
342 |
-
if ( $key->keyHandle === $data->keyHandle ) {
|
343 |
return update_user_meta( $user_id, self::REGISTERED_KEY_USER_META_KEY, (array) $data, (array) $key );
|
344 |
}
|
345 |
}
|
@@ -357,7 +329,7 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
|
|
357 |
* @param string $keyHandle Optional. Key handle.
|
358 |
* @return bool True on success, false on failure.
|
359 |
*/
|
360 |
-
public static function delete_security_key( $user_id, $keyHandle = null ) {
|
361 |
global $wpdb;
|
362 |
|
363 |
if ( ! is_numeric( $user_id ) ) {
|
@@ -369,21 +341,18 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
|
|
369 |
return false;
|
370 |
}
|
371 |
|
372 |
-
$
|
373 |
-
$keyHandle = maybe_serialize( $keyHandle ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
|
374 |
|
375 |
-
$
|
|
|
376 |
|
377 |
-
|
378 |
-
$key_handle_lookup = sprintf( ':"%s";s:', $keyHandle ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
|
379 |
|
380 |
-
|
381 |
-
|
382 |
-
'%' . $wpdb->esc_like( $key_handle_lookup ) . '%'
|
383 |
-
);
|
384 |
}
|
385 |
|
386 |
-
$meta_ids = $wpdb->get_col( $query );
|
387 |
if ( ! count( $meta_ids ) ) {
|
388 |
return false;
|
389 |
}
|
1 |
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
/**
|
3 |
* Class for creating a FIDO Universal 2nd Factor provider.
|
4 |
*
|
29 |
*/
|
30 |
const AUTH_DATA_USER_META_KEY = '_two_factor_fido_u2f_login_request';
|
31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
/**
|
33 |
* Ensures only one instance of this class exists in memory at any one time.
|
34 |
*
|
35 |
* @return \Two_Factor_FIDO_U2F
|
36 |
*/
|
37 |
+
static function get_instance() {
|
38 |
static $instance;
|
39 |
|
40 |
if ( ! isset( $instance ) ) {
|
54 |
return;
|
55 |
}
|
56 |
|
57 |
+
require_once( TWO_FACTOR_DIR . 'includes/Yubico/U2F.php' );
|
58 |
self::$u2f = new u2flib_server\U2F( self::get_u2f_app_id() );
|
59 |
|
60 |
+
require_once( TWO_FACTOR_DIR . 'providers/class.two-factor-fido-u2f-admin.php' );
|
61 |
Two_Factor_FIDO_U2F_Admin::add_hooks();
|
62 |
|
63 |
+
wp_register_script(
|
64 |
+
'fido-u2f-api',
|
65 |
+
plugins_url( 'includes/Google/u2f-api.js', dirname( __FILE__ ) ),
|
66 |
+
null,
|
67 |
+
'0.1.0-dev.2',
|
68 |
+
true
|
69 |
+
);
|
70 |
+
|
71 |
+
wp_register_script(
|
72 |
+
'fido-u2f-login',
|
73 |
+
plugins_url( 'js/fido-u2f-login.js', __FILE__ ),
|
74 |
+
array( 'jquery', 'fido-u2f-api' ),
|
75 |
+
'0.1.0-dev.2',
|
76 |
+
true
|
77 |
+
);
|
78 |
|
79 |
+
add_action( 'two-factor-user-options-' . __CLASS__, array( $this, 'user_options' ) );
|
80 |
|
81 |
return parent::__construct();
|
82 |
}
|
83 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
/**
|
85 |
* Return the U2F AppId. U2F requires the AppID to use HTTPS
|
86 |
* and a top-level domain.
|
103 |
* @since 0.1-dev
|
104 |
*/
|
105 |
public function get_label() {
|
106 |
+
return _x( 'FIDO Universal 2nd Factor (U2F)', 'Provider Label', 'two-factor' );
|
107 |
}
|
108 |
|
109 |
/**
|
110 |
+
* Enqueue assets for login form.
|
|
|
111 |
*
|
112 |
+
* @since 0.1-dev
|
113 |
*/
|
114 |
+
public function login_enqueue_assets() {
|
115 |
+
wp_enqueue_script( 'fido-u2f-login' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
}
|
117 |
|
118 |
/**
|
124 |
* @return null
|
125 |
*/
|
126 |
public function authentication_page( $user ) {
|
127 |
+
require_once( ABSPATH . '/wp-admin/includes/template.php' );
|
128 |
|
129 |
+
// U2F doesn't work without HTTPS
|
130 |
if ( ! is_ssl() ) {
|
131 |
?>
|
132 |
<p><?php esc_html_e( 'U2F requires an HTTPS connection. Please use an alternative 2nd factor method.', 'two-factor' ); ?></p>
|
180 |
try {
|
181 |
$reg = self::$u2f->doAuthenticate( $requests, $keys, $response );
|
182 |
|
183 |
+
$reg->last_used = current_time( 'timestamp' );
|
184 |
|
185 |
self::update_security_key( $user->ID, $reg );
|
186 |
|
233 |
|
234 |
if (
|
235 |
! is_object( $register )
|
236 |
+
|| ! property_exists( $register, 'keyHandle' ) || empty( $register->keyHandle )
|
237 |
+
|| ! property_exists( $register, 'publicKey' ) || empty( $register->publicKey )
|
238 |
|| ! property_exists( $register, 'certificate' ) || empty( $register->certificate )
|
239 |
|| ! property_exists( $register, 'counter' ) || ( -1 > $register->counter )
|
240 |
) {
|
242 |
}
|
243 |
|
244 |
$register = array(
|
245 |
+
'keyHandle' => $register->keyHandle,
|
246 |
+
'publicKey' => $register->publicKey,
|
247 |
'certificate' => $register->certificate,
|
248 |
'counter' => $register->counter,
|
249 |
);
|
250 |
|
251 |
$register['name'] = __( 'New Security Key', 'two-factor' );
|
252 |
+
$register['added'] = current_time( 'timestamp' );
|
253 |
$register['last_used'] = $register['added'];
|
254 |
|
255 |
return add_user_meta( $user_id, self::REGISTERED_KEY_USER_META_KEY, $register );
|
300 |
|
301 |
if (
|
302 |
! is_object( $data )
|
303 |
+
|| ! property_exists( $data, 'keyHandle' ) || empty( $data->keyHandle )
|
304 |
+
|| ! property_exists( $data, 'publicKey' ) || empty( $data->publicKey )
|
305 |
|| ! property_exists( $data, 'certificate' ) || empty( $data->certificate )
|
306 |
|| ! property_exists( $data, 'counter' ) || ( -1 > $data->counter )
|
307 |
) {
|
311 |
$keys = self::get_security_keys( $user_id );
|
312 |
if ( $keys ) {
|
313 |
foreach ( $keys as $key ) {
|
314 |
+
if ( $key->keyHandle === $data->keyHandle ) {
|
315 |
return update_user_meta( $user_id, self::REGISTERED_KEY_USER_META_KEY, (array) $data, (array) $key );
|
316 |
}
|
317 |
}
|
329 |
* @param string $keyHandle Optional. Key handle.
|
330 |
* @return bool True on success, false on failure.
|
331 |
*/
|
332 |
+
public static function delete_security_key( $user_id, $keyHandle = null ) {
|
333 |
global $wpdb;
|
334 |
|
335 |
if ( ! is_numeric( $user_id ) ) {
|
341 |
return false;
|
342 |
}
|
343 |
|
344 |
+
$table = $wpdb->usermeta;
|
|
|
345 |
|
346 |
+
$keyHandle = wp_unslash( $keyHandle );
|
347 |
+
$keyHandle = maybe_serialize( $keyHandle );
|
348 |
|
349 |
+
$query = $wpdb->prepare( "SELECT umeta_id FROM $table WHERE meta_key = '%s' AND user_id = %d", self::REGISTERED_KEY_USER_META_KEY, $user_id );
|
|
|
350 |
|
351 |
+
if ( $keyHandle ) {
|
352 |
+
$query .= $wpdb->prepare( ' AND meta_value LIKE %s', '%:"' . $keyHandle . '";s:%' );
|
|
|
|
|
353 |
}
|
354 |
|
355 |
+
$meta_ids = $wpdb->get_col( $query );
|
356 |
if ( ! count( $meta_ids ) ) {
|
357 |
return false;
|
358 |
}
|
providers/{class-two-factor-provider.php → class.two-factor-provider.php}
RENAMED
@@ -1,10 +1,4 @@
|
|
1 |
<?php
|
2 |
-
/**
|
3 |
-
* Abstract class for creating two factor authentication providers.
|
4 |
-
*
|
5 |
-
* @package Two_Factor
|
6 |
-
*/
|
7 |
-
|
8 |
/**
|
9 |
* Abstract class for creating two factor authentication providers.
|
10 |
*
|
@@ -30,7 +24,7 @@ abstract class Two_Factor_Provider {
|
|
30 |
*
|
31 |
* @return string
|
32 |
*/
|
33 |
-
abstract
|
34 |
|
35 |
/**
|
36 |
* Prints the name of the provider.
|
@@ -48,7 +42,7 @@ abstract class Two_Factor_Provider {
|
|
48 |
*
|
49 |
* @param WP_User $user WP_User object of the logged-in user.
|
50 |
*/
|
51 |
-
abstract
|
52 |
|
53 |
/**
|
54 |
* Allow providers to do extra processing before the authentication.
|
@@ -70,7 +64,7 @@ abstract class Two_Factor_Provider {
|
|
70 |
* @param WP_User $user WP_User object of the logged-in user.
|
71 |
* @return boolean
|
72 |
*/
|
73 |
-
abstract
|
74 |
|
75 |
/**
|
76 |
* Whether this Two Factor provider is configured and available for the user specified.
|
@@ -78,7 +72,7 @@ abstract class Two_Factor_Provider {
|
|
78 |
* @param WP_User $user WP_User object of the logged-in user.
|
79 |
* @return boolean
|
80 |
*/
|
81 |
-
abstract
|
82 |
|
83 |
/**
|
84 |
* Generate a random eight-digit string to send out as an auth code.
|
1 |
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
/**
|
3 |
* Abstract class for creating two factor authentication providers.
|
4 |
*
|
24 |
*
|
25 |
* @return string
|
26 |
*/
|
27 |
+
abstract function get_label();
|
28 |
|
29 |
/**
|
30 |
* Prints the name of the provider.
|
42 |
*
|
43 |
* @param WP_User $user WP_User object of the logged-in user.
|
44 |
*/
|
45 |
+
abstract function authentication_page( $user );
|
46 |
|
47 |
/**
|
48 |
* Allow providers to do extra processing before the authentication.
|
64 |
* @param WP_User $user WP_User object of the logged-in user.
|
65 |
* @return boolean
|
66 |
*/
|
67 |
+
abstract function validate_authentication( $user );
|
68 |
|
69 |
/**
|
70 |
* Whether this Two Factor provider is configured and available for the user specified.
|
72 |
* @param WP_User $user WP_User object of the logged-in user.
|
73 |
* @return boolean
|
74 |
*/
|
75 |
+
abstract function is_available_for_user( $user );
|
76 |
|
77 |
/**
|
78 |
* Generate a random eight-digit string to send out as an auth code.
|
providers/{class-two-factor-totp.php → class.two-factor-totp.php}
RENAMED
@@ -24,35 +24,20 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
|
24 |
*/
|
25 |
const NOTICES_META_KEY = '_two_factor_totp_notices';
|
26 |
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
*/
|
32 |
-
const ACTION_SECRET_DELETE = 'totp-delete';
|
33 |
-
|
34 |
-
const DEFAULT_KEY_BIT_SIZE = 160;
|
35 |
-
const DEFAULT_CRYPTO = 'sha1';
|
36 |
-
const DEFAULT_DIGIT_COUNT = 6;
|
37 |
-
const DEFAULT_TIME_STEP_SEC = 30;
|
38 |
const DEFAULT_TIME_STEP_ALLOWANCE = 4;
|
39 |
-
|
40 |
-
/**
|
41 |
-
* Chracters used in base32 encoding.
|
42 |
-
*
|
43 |
-
* @var string
|
44 |
-
*/
|
45 |
-
private static $base_32_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
46 |
|
47 |
/**
|
48 |
* Class constructor. Sets up hooks, etc.
|
49 |
*/
|
50 |
protected function __construct() {
|
51 |
-
add_action( '
|
52 |
-
add_action( 'personal_options_update',
|
53 |
-
add_action( 'edit_user_profile_update',
|
54 |
-
add_action( 'two_factor_user_settings_action', array( $this, 'user_settings_action' ), 10, 2 );
|
55 |
-
|
56 |
return parent::__construct();
|
57 |
}
|
58 |
|
@@ -71,32 +56,7 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
|
71 |
* Returns the name of the provider.
|
72 |
*/
|
73 |
public function get_label() {
|
74 |
-
return _x( 'Time Based One-Time Password (
|
75 |
-
}
|
76 |
-
|
77 |
-
/**
|
78 |
-
* Trigger our custom user settings actions.
|
79 |
-
*
|
80 |
-
* @param integer $user_id User ID.
|
81 |
-
* @param string $action Action ID.
|
82 |
-
*
|
83 |
-
* @return void
|
84 |
-
*/
|
85 |
-
public function user_settings_action( $user_id, $action ) {
|
86 |
-
if ( self::ACTION_SECRET_DELETE === $action ) {
|
87 |
-
$this->delete_user_totp_key( $user_id );
|
88 |
-
}
|
89 |
-
}
|
90 |
-
|
91 |
-
/**
|
92 |
-
* Get the URL for deleting the secret token.
|
93 |
-
*
|
94 |
-
* @param integer $user_id User ID.
|
95 |
-
*
|
96 |
-
* @return string
|
97 |
-
*/
|
98 |
-
protected function get_token_delete_url_for_user( $user_id ) {
|
99 |
-
return Two_Factor_Core::get_user_update_action_url( $user_id, self::ACTION_SECRET_DELETE );
|
100 |
}
|
101 |
|
102 |
/**
|
@@ -113,21 +73,19 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
|
113 |
wp_nonce_field( 'user_two_factor_totp_options', '_nonce_user_two_factor_totp_options', false );
|
114 |
|
115 |
$key = $this->get_user_totp_key( $user->ID );
|
116 |
-
$this->admin_notices(
|
117 |
|
118 |
?>
|
119 |
<div id="two-factor-totp-options">
|
120 |
-
<?php
|
121 |
-
|
122 |
-
$
|
123 |
-
$site_name = get_bloginfo( 'name', 'display' );
|
124 |
-
$totp_title = apply_filters( 'two_factor_totp_title', $site_name . ':' . $user->user_login, $user );
|
125 |
?>
|
126 |
<p>
|
127 |
<?php esc_html_e( 'Please scan the QR code or manually enter the key, then enter an authentication code from your app in order to complete setup.', 'two-factor' ); ?>
|
128 |
</p>
|
129 |
<p>
|
130 |
-
<img src="<?php echo esc_url( $this->get_google_qr_code( $
|
131 |
</p>
|
132 |
<p>
|
133 |
<code><?php echo esc_html( $key ); ?></code>
|
@@ -142,10 +100,10 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
|
142 |
</p>
|
143 |
<?php else : ?>
|
144 |
<p class="success">
|
145 |
-
<?php esc_html_e( 'Secret key
|
146 |
</p>
|
147 |
<p>
|
148 |
-
<
|
149 |
<em class="description">
|
150 |
<?php esc_html_e( 'You will have to re-scan the QR code on all devices as the previous codes will stop working.', 'two-factor' ); ?>
|
151 |
</em>
|
@@ -159,25 +117,27 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
|
159 |
* Save the options specified in `::user_two_factor_options()`
|
160 |
*
|
161 |
* @param integer $user_id The user ID whose options are being updated.
|
162 |
-
*
|
163 |
-
* @return void
|
164 |
*/
|
165 |
public function user_two_factor_options_update( $user_id ) {
|
166 |
$notices = array();
|
167 |
-
$errors
|
|
|
|
|
168 |
|
169 |
if ( isset( $_POST['_nonce_user_two_factor_totp_options'] ) ) {
|
170 |
check_admin_referer( 'user_two_factor_totp_options', '_nonce_user_two_factor_totp_options' );
|
171 |
|
|
|
|
|
|
|
|
|
|
|
172 |
// Validate and store a new secret key.
|
173 |
if ( ! empty( $_POST['two-factor-totp-authcode'] ) && ! empty( $_POST['two-factor-totp-key'] ) ) {
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
if ( $this->is_valid_key( $key ) ) {
|
179 |
-
if ( $this->is_valid_authcode( $key, $authcode ) ) {
|
180 |
-
if ( ! $this->set_user_totp_key( $user_id, $key ) ) {
|
181 |
$errors[] = __( 'Unable to save Two Factor Authentication code. Please re-scan the QR code and enter the code provided by your application.', 'two-factor' );
|
182 |
}
|
183 |
} else {
|
@@ -240,7 +200,7 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
|
240 |
* @return boolean
|
241 |
*/
|
242 |
public function is_valid_key( $key ) {
|
243 |
-
$check = sprintf( '/^[%s]+$/', self::$
|
244 |
|
245 |
if ( 1 === preg_match( $check, $key ) ) {
|
246 |
return true;
|
@@ -251,20 +211,15 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
|
251 |
|
252 |
/**
|
253 |
* Display any available admin notices.
|
254 |
-
*
|
255 |
-
* @param integer $user_id User ID.
|
256 |
-
*
|
257 |
-
* @return void
|
258 |
*/
|
259 |
-
public function admin_notices(
|
260 |
-
$notices = get_user_meta(
|
261 |
|
262 |
if ( ! empty( $notices ) ) {
|
263 |
-
delete_user_meta(
|
264 |
-
|
265 |
foreach ( $notices as $class => $messages ) {
|
266 |
?>
|
267 |
-
<div class="<?php echo esc_attr( $class )
|
268 |
<?php
|
269 |
foreach ( $messages as $msg ) {
|
270 |
?>
|
@@ -288,10 +243,10 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
|
288 |
* @return bool Whether the user gave a valid code
|
289 |
*/
|
290 |
public function validate_authentication( $user ) {
|
291 |
-
if ( ! empty( $_REQUEST['authcode'] ) ) {
|
292 |
return $this->is_valid_authcode(
|
293 |
$this->get_user_totp_key( $user->ID ),
|
294 |
-
sanitize_text_field( $_REQUEST['authcode'] )
|
295 |
);
|
296 |
}
|
297 |
|
@@ -313,12 +268,9 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
|
313 |
* Ticks are the allowed offset from the correct time in 30 second increments,
|
314 |
* so the default of 4 allows codes that are two minutes to either side of server time
|
315 |
*
|
316 |
-
* @deprecated 0.7.0 Use {@see 'two_factor_totp_time_step_allowance'} instead.
|
317 |
* @param int $max_ticks Max ticks of time correction to allow. Default 4.
|
318 |
*/
|
319 |
-
$max_ticks =
|
320 |
-
|
321 |
-
$max_ticks = apply_filters( 'two_factor_totp_time_step_allowance', self::DEFAULT_TIME_STEP_ALLOWANCE );
|
322 |
|
323 |
// Array of all ticks to allow, sorted using absolute value to test closest match first.
|
324 |
$ticks = range( - $max_ticks, $max_ticks );
|
@@ -343,7 +295,7 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
|
343 |
* @return string $bitsize long string composed of available base32 chars.
|
344 |
*/
|
345 |
public static function generate_key( $bitsize = self::DEFAULT_KEY_BIT_SIZE ) {
|
346 |
-
$bytes
|
347 |
$secret = wp_generate_password( $bytes, true, true );
|
348 |
|
349 |
return self::base32_encode( $secret );
|
@@ -373,8 +325,8 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
|
373 |
$higher = 0;
|
374 |
}
|
375 |
|
376 |
-
$lowmap
|
377 |
-
$lower
|
378 |
|
379 |
return pack( 'NN', $higher, $lower );
|
380 |
}
|
@@ -424,7 +376,7 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
|
424 |
*/
|
425 |
public static function get_google_qr_code( $name, $key, $title = null ) {
|
426 |
// Encode to support spaces, question marks and other characters.
|
427 |
-
$name
|
428 |
$google_url = urlencode( 'otpauth://totp/' . $name . '?secret=' . $key );
|
429 |
if ( isset( $title ) ) {
|
430 |
$google_url .= urlencode( '&issuer=' . rawurlencode( $title ) );
|
@@ -452,20 +404,18 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
|
452 |
* @param WP_User $user WP_User object of the logged-in user.
|
453 |
*/
|
454 |
public function authentication_page( $user ) {
|
455 |
-
require_once ABSPATH .
|
456 |
?>
|
457 |
-
<p>
|
458 |
-
<?php esc_html_e( 'Please enter the code generated by your authenticator app.', 'two-factor' ); ?>
|
459 |
-
</p>
|
460 |
<p>
|
461 |
<label for="authcode"><?php esc_html_e( 'Authentication Code:', 'two-factor' ); ?></label>
|
462 |
-
<input type="tel"
|
463 |
</p>
|
464 |
<script type="text/javascript">
|
465 |
setTimeout( function(){
|
466 |
var d;
|
467 |
try{
|
468 |
d = document.getElementById('authcode');
|
|
|
469 |
d.focus();
|
470 |
} catch(e){}
|
471 |
}, 200);
|
@@ -493,10 +443,10 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
|
493 |
}
|
494 |
|
495 |
$five_bit_sections = str_split( $binary_string, 5 );
|
496 |
-
$base32_string
|
497 |
|
498 |
foreach ( $five_bit_sections as $five_bit_section ) {
|
499 |
-
$base32_string .= self::$
|
500 |
}
|
501 |
|
502 |
return $base32_string;
|
@@ -513,25 +463,25 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
|
513 |
*/
|
514 |
public static function base32_decode( $base32_string ) {
|
515 |
|
516 |
-
$base32_string
|
517 |
|
518 |
-
if ( ! preg_match( '/^[' . self::$
|
519 |
throw new Exception( 'Invalid characters in the base32 string.' );
|
520 |
}
|
521 |
|
522 |
-
$l
|
523 |
-
$n
|
524 |
-
$j
|
525 |
$binary = '';
|
526 |
|
527 |
for ( $i = 0; $i < $l; $i++ ) {
|
528 |
|
529 |
-
$n
|
530 |
-
$n
|
531 |
$j += 5; // Keep track of number of bits in buffer.
|
532 |
|
533 |
if ( $j >= 8 ) {
|
534 |
-
$j
|
535 |
$binary .= chr( ( $n & ( 0xFF << $j ) ) >> $j );
|
536 |
}
|
537 |
}
|
@@ -553,6 +503,6 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
|
553 |
if ( $a === $b ) {
|
554 |
return 0;
|
555 |
}
|
556 |
-
return (
|
557 |
}
|
558 |
}
|
24 |
*/
|
25 |
const NOTICES_META_KEY = '_two_factor_totp_notices';
|
26 |
|
27 |
+
const DEFAULT_KEY_BIT_SIZE = 160;
|
28 |
+
const DEFAULT_CRYPTO = 'sha1';
|
29 |
+
const DEFAULT_DIGIT_COUNT = 6;
|
30 |
+
const DEFAULT_TIME_STEP_SEC = 30;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
const DEFAULT_TIME_STEP_ALLOWANCE = 4;
|
32 |
+
private static $_base_32_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
|
34 |
/**
|
35 |
* Class constructor. Sets up hooks, etc.
|
36 |
*/
|
37 |
protected function __construct() {
|
38 |
+
add_action( 'two-factor-user-options-' . __CLASS__, array( $this, 'user_two_factor_options' ) );
|
39 |
+
add_action( 'personal_options_update', array( $this, 'user_two_factor_options_update' ) );
|
40 |
+
add_action( 'edit_user_profile_update', array( $this, 'user_two_factor_options_update' ) );
|
|
|
|
|
41 |
return parent::__construct();
|
42 |
}
|
43 |
|
56 |
* Returns the name of the provider.
|
57 |
*/
|
58 |
public function get_label() {
|
59 |
+
return _x( 'Time Based One-Time Password (Google Authenticator)', 'Provider Label', 'two-factor' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
}
|
61 |
|
62 |
/**
|
73 |
wp_nonce_field( 'user_two_factor_totp_options', '_nonce_user_two_factor_totp_options', false );
|
74 |
|
75 |
$key = $this->get_user_totp_key( $user->ID );
|
76 |
+
$this->admin_notices();
|
77 |
|
78 |
?>
|
79 |
<div id="two-factor-totp-options">
|
80 |
+
<?php if ( empty( $key ) ) :
|
81 |
+
$key = $this->generate_key();
|
82 |
+
$site_name = get_bloginfo( 'name', 'display' );
|
|
|
|
|
83 |
?>
|
84 |
<p>
|
85 |
<?php esc_html_e( 'Please scan the QR code or manually enter the key, then enter an authentication code from your app in order to complete setup.', 'two-factor' ); ?>
|
86 |
</p>
|
87 |
<p>
|
88 |
+
<img src="<?php echo esc_url( $this->get_google_qr_code( $site_name . ':' . $user->user_login, $key, $site_name ) ); ?>" id="two-factor-totp-qrcode" />
|
89 |
</p>
|
90 |
<p>
|
91 |
<code><?php echo esc_html( $key ); ?></code>
|
100 |
</p>
|
101 |
<?php else : ?>
|
102 |
<p class="success">
|
103 |
+
<?php esc_html_e( 'Secret key configured and registered.', 'two-factor' ); ?>
|
104 |
</p>
|
105 |
<p>
|
106 |
+
<input type="submit" class="button" name="two-factor-totp-delete" value="<?php esc_attr_e( 'Reset Key', 'two-factor' ); ?>" />
|
107 |
<em class="description">
|
108 |
<?php esc_html_e( 'You will have to re-scan the QR code on all devices as the previous codes will stop working.', 'two-factor' ); ?>
|
109 |
</em>
|
117 |
* Save the options specified in `::user_two_factor_options()`
|
118 |
*
|
119 |
* @param integer $user_id The user ID whose options are being updated.
|
120 |
+
* @return false
|
|
|
121 |
*/
|
122 |
public function user_two_factor_options_update( $user_id ) {
|
123 |
$notices = array();
|
124 |
+
$errors = array();
|
125 |
+
|
126 |
+
$current_key = $this->get_user_totp_key( $user_id );
|
127 |
|
128 |
if ( isset( $_POST['_nonce_user_two_factor_totp_options'] ) ) {
|
129 |
check_admin_referer( 'user_two_factor_totp_options', '_nonce_user_two_factor_totp_options' );
|
130 |
|
131 |
+
// Delete the secret key.
|
132 |
+
if ( ! empty( $current_key ) && isset( $_POST['two-factor-totp-delete'] ) ) {
|
133 |
+
$this->delete_user_totp_key( $user_id );
|
134 |
+
}
|
135 |
+
|
136 |
// Validate and store a new secret key.
|
137 |
if ( ! empty( $_POST['two-factor-totp-authcode'] ) && ! empty( $_POST['two-factor-totp-key'] ) ) {
|
138 |
+
if ( $this->is_valid_key( $_POST['two-factor-totp-key'] ) ) {
|
139 |
+
if ( $this->is_valid_authcode( $_POST['two-factor-totp-key'], $_POST['two-factor-totp-authcode'] ) ) {
|
140 |
+
if ( ! $this->set_user_totp_key( $user_id, $_POST['two-factor-totp-key'] ) ) {
|
|
|
|
|
|
|
|
|
141 |
$errors[] = __( 'Unable to save Two Factor Authentication code. Please re-scan the QR code and enter the code provided by your application.', 'two-factor' );
|
142 |
}
|
143 |
} else {
|
200 |
* @return boolean
|
201 |
*/
|
202 |
public function is_valid_key( $key ) {
|
203 |
+
$check = sprintf( '/^[%s]+$/', self::$_base_32_chars );
|
204 |
|
205 |
if ( 1 === preg_match( $check, $key ) ) {
|
206 |
return true;
|
211 |
|
212 |
/**
|
213 |
* Display any available admin notices.
|
|
|
|
|
|
|
|
|
214 |
*/
|
215 |
+
public function admin_notices() {
|
216 |
+
$notices = get_user_meta( get_current_user_id(), self::NOTICES_META_KEY, true );
|
217 |
|
218 |
if ( ! empty( $notices ) ) {
|
219 |
+
delete_user_meta( get_current_user_id(), self::NOTICES_META_KEY );
|
|
|
220 |
foreach ( $notices as $class => $messages ) {
|
221 |
?>
|
222 |
+
<div class="<?php echo esc_attr( $class ) ?>">
|
223 |
<?php
|
224 |
foreach ( $messages as $msg ) {
|
225 |
?>
|
243 |
* @return bool Whether the user gave a valid code
|
244 |
*/
|
245 |
public function validate_authentication( $user ) {
|
246 |
+
if ( ! empty( $_REQUEST['authcode'] ) ) { // WPCS: CSRF ok, nonce verified by login_form_validate_2fa().
|
247 |
return $this->is_valid_authcode(
|
248 |
$this->get_user_totp_key( $user->ID ),
|
249 |
+
sanitize_text_field( $_REQUEST['authcode'] ) // WPCS: CSRF ok, nonce verified by login_form_validate_2fa().
|
250 |
);
|
251 |
}
|
252 |
|
268 |
* Ticks are the allowed offset from the correct time in 30 second increments,
|
269 |
* so the default of 4 allows codes that are two minutes to either side of server time
|
270 |
*
|
|
|
271 |
* @param int $max_ticks Max ticks of time correction to allow. Default 4.
|
272 |
*/
|
273 |
+
$max_ticks = apply_filters( 'two-factor-totp-time-step-allowance', self::DEFAULT_TIME_STEP_ALLOWANCE );
|
|
|
|
|
274 |
|
275 |
// Array of all ticks to allow, sorted using absolute value to test closest match first.
|
276 |
$ticks = range( - $max_ticks, $max_ticks );
|
295 |
* @return string $bitsize long string composed of available base32 chars.
|
296 |
*/
|
297 |
public static function generate_key( $bitsize = self::DEFAULT_KEY_BIT_SIZE ) {
|
298 |
+
$bytes = ceil( $bitsize / 8 );
|
299 |
$secret = wp_generate_password( $bytes, true, true );
|
300 |
|
301 |
return self::base32_encode( $secret );
|
325 |
$higher = 0;
|
326 |
}
|
327 |
|
328 |
+
$lowmap = 0xffffffff;
|
329 |
+
$lower = $value & $lowmap;
|
330 |
|
331 |
return pack( 'NN', $higher, $lower );
|
332 |
}
|
376 |
*/
|
377 |
public static function get_google_qr_code( $name, $key, $title = null ) {
|
378 |
// Encode to support spaces, question marks and other characters.
|
379 |
+
$name = rawurlencode( $name );
|
380 |
$google_url = urlencode( 'otpauth://totp/' . $name . '?secret=' . $key );
|
381 |
if ( isset( $title ) ) {
|
382 |
$google_url .= urlencode( '&issuer=' . rawurlencode( $title ) );
|
404 |
* @param WP_User $user WP_User object of the logged-in user.
|
405 |
*/
|
406 |
public function authentication_page( $user ) {
|
407 |
+
require_once( ABSPATH . '/wp-admin/includes/template.php' );
|
408 |
?>
|
|
|
|
|
|
|
409 |
<p>
|
410 |
<label for="authcode"><?php esc_html_e( 'Authentication Code:', 'two-factor' ); ?></label>
|
411 |
+
<input type="tel" name="authcode" id="authcode" class="input" value="" size="20" pattern="[0-9]*" />
|
412 |
</p>
|
413 |
<script type="text/javascript">
|
414 |
setTimeout( function(){
|
415 |
var d;
|
416 |
try{
|
417 |
d = document.getElementById('authcode');
|
418 |
+
d.value = '';
|
419 |
d.focus();
|
420 |
} catch(e){}
|
421 |
}, 200);
|
443 |
}
|
444 |
|
445 |
$five_bit_sections = str_split( $binary_string, 5 );
|
446 |
+
$base32_string = '';
|
447 |
|
448 |
foreach ( $five_bit_sections as $five_bit_section ) {
|
449 |
+
$base32_string .= self::$_base_32_chars[ base_convert( str_pad( $five_bit_section, 5, '0' ), 2, 10 ) ];
|
450 |
}
|
451 |
|
452 |
return $base32_string;
|
463 |
*/
|
464 |
public static function base32_decode( $base32_string ) {
|
465 |
|
466 |
+
$base32_string = strtoupper( $base32_string );
|
467 |
|
468 |
+
if ( ! preg_match( '/^[' . self::$_base_32_chars . ']+$/', $base32_string, $match ) ) {
|
469 |
throw new Exception( 'Invalid characters in the base32 string.' );
|
470 |
}
|
471 |
|
472 |
+
$l = strlen( $base32_string );
|
473 |
+
$n = 0;
|
474 |
+
$j = 0;
|
475 |
$binary = '';
|
476 |
|
477 |
for ( $i = 0; $i < $l; $i++ ) {
|
478 |
|
479 |
+
$n = $n << 5; // Move buffer left by 5 to make room.
|
480 |
+
$n = $n + strpos( self::$_base_32_chars, $base32_string[ $i ] ); // Add value into buffer.
|
481 |
$j += 5; // Keep track of number of bits in buffer.
|
482 |
|
483 |
if ( $j >= 8 ) {
|
484 |
+
$j -= 8;
|
485 |
$binary .= chr( ( $n & ( 0xFF << $j ) ) >> $j );
|
486 |
}
|
487 |
}
|
503 |
if ( $a === $b ) {
|
504 |
return 0;
|
505 |
}
|
506 |
+
return ($a < $b) ? -1 : 1;
|
507 |
}
|
508 |
}
|
providers/js/fido-u2f-admin-inline-edit.js
CHANGED
@@ -80,8 +80,7 @@ var inlineEditKey;
|
|
80 |
|
81 |
params = {
|
82 |
action: 'inline-save-key',
|
83 |
-
keyHandle: id
|
84 |
-
user_id: window.u2fL10n.user_id
|
85 |
};
|
86 |
|
87 |
fields = $( '#edit-' + id ).find( ':input' ).serialize();
|
80 |
|
81 |
params = {
|
82 |
action: 'inline-save-key',
|
83 |
+
keyHandle: id
|
|
|
84 |
};
|
85 |
|
86 |
fields = $( '#edit-' + id ).find( ':input' ).serialize();
|
providers/js/fido-u2f-admin.js
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
( function( $ ) {
|
3 |
var $button = $( '#register_security_key' );
|
4 |
var $statusNotice = $( '#security-keys-section .security-key-status' );
|
5 |
-
var u2fSupported = (
|
6 |
|
7 |
if ( ! u2fSupported ) {
|
8 |
$statusNotice.text( u2fL10n.text.u2f_not_supported );
|
@@ -24,7 +24,7 @@
|
|
24 |
challenge: u2fL10n.register.request.challenge
|
25 |
};
|
26 |
|
27 |
-
|
28 |
$( '.register-security-key .spinner' ).removeClass( 'is-active' );
|
29 |
$button.prop( 'disabled', false );
|
30 |
|
2 |
( function( $ ) {
|
3 |
var $button = $( '#register_security_key' );
|
4 |
var $statusNotice = $( '#security-keys-section .security-key-status' );
|
5 |
+
var u2fSupported = ( u2f && u2f.HasNativeApiSupport );
|
6 |
|
7 |
if ( ! u2fSupported ) {
|
8 |
$statusNotice.text( u2fL10n.text.u2f_not_supported );
|
24 |
challenge: u2fL10n.register.request.challenge
|
25 |
};
|
26 |
|
27 |
+
u2f.register( u2fL10n.register.request.appId, [ registerRequest ], u2fL10n.register.sigs, function( data ) {
|
28 |
$( '.register-security-key .spinner' ).removeClass( 'is-active' );
|
29 |
$button.prop( 'disabled', false );
|
30 |
|
readme.md
CHANGED
@@ -4,61 +4,30 @@
|
|
4 |
![Banner](assets/banner-1544x500.png)
|
5 |
Enable Two-Factor Authentication using time-based one-time passwords (OTP, Google Authenticator), Universal 2nd Factor (FIDO U2F, YubiKey), email and backup verification codes.
|
6 |
|
7 |
-
**Contributors:** [georgestephanis](https://profiles.wordpress.org/georgestephanis), [valendesigns](https://profiles.wordpress.org/valendesigns), [stevenkword](https://profiles.wordpress.org/stevenkword), [extendwings](https://profiles.wordpress.org/extendwings), [sgrant](https://profiles.wordpress.org/sgrant), [aaroncampbell](https://profiles.wordpress.org/aaroncampbell), [johnbillion](https://profiles.wordpress.org/johnbillion), [stevegrunwell](https://profiles.wordpress.org/stevegrunwell), [netweb](https://profiles.wordpress.org/netweb), [kasparsd](https://profiles.wordpress.org/kasparsd)
|
8 |
**Tags:** [two factor](https://wordpress.org/plugins/tags/two-factor), [two step](https://wordpress.org/plugins/tags/two-step), [authentication](https://wordpress.org/plugins/tags/authentication), [login](https://wordpress.org/plugins/tags/login), [totp](https://wordpress.org/plugins/tags/totp), [fido u2f](https://wordpress.org/plugins/tags/fido-u2f), [u2f](https://wordpress.org/plugins/tags/u2f), [email](https://wordpress.org/plugins/tags/email), [backup codes](https://wordpress.org/plugins/tags/backup-codes), [2fa](https://wordpress.org/plugins/tags/2fa), [yubikey](https://wordpress.org/plugins/tags/yubikey)
|
9 |
**Requires at least:** 4.3
|
10 |
-
**Tested up to:** 5.
|
11 |
**Stable tag:** trunk (master)
|
12 |
-
**Requires PHP:** 5.6
|
13 |
|
14 |
[![Build Status](https://travis-ci.org/WordPress/two-factor.svg?branch=master)](https://travis-ci.org/WordPress/two-factor) [![Coverage Status](https://coveralls.io/repos/WordPress/two-factor/badge.svg?branch=master)](https://coveralls.io/github/WordPress/two-factor) [![Built with Grunt](https://gruntjs.com/cdn/builtwith.svg)](http://gruntjs.com)
|
15 |
|
16 |
## Description ##
|
17 |
|
18 |
-
|
19 |
-
|
20 |
-
- Email codes
|
21 |
-
- Time Based One-Time Passwords (TOTP)
|
22 |
-
- FIDO Universal 2nd Factor (U2F)
|
23 |
-
- Backup Codes
|
24 |
-
- Dummy Method (only for testing purposes)
|
25 |
-
|
26 |
-
For more history, see [this post](https://georgestephanis.wordpress.com/2013/08/14/two-cents-on-two-factor/).
|
27 |
-
### Actions & Filters ###
|
28 |
-
Here is a list of action and filter hooks provided by the plugin:
|
29 |
-
|
30 |
-
- `two_factor_providers` filter overrides the available two-factor providers such as email and time-based one-time passwords. Array values are PHP classnames of the two-factor providers.
|
31 |
-
- `two_factor_enabled_providers_for_user` filter overrides the list of two-factor providers enabled for a user. First argument is an array of enabled provider classnames as values, the second argument is the user ID.
|
32 |
-
- `two_factor_user_authenticated` action which receives the logged in `WP_User` object as the first argument for determining the logged in user right after the authentication workflow.
|
33 |
-
- `two_factor_token_ttl` filter overrides the time interval in seconds that an email token is considered after generation. Accepts the time in seconds as the first argument and the ID of the `WP_User` object being authenticated.
|
34 |
|
|
|
35 |
|
36 |
-
|
37 |
-
|
38 |
-
### Two-factor options under User Profile.
|
39 |
-
|
40 |
-
![Two-factor options under User Profile.](assets/screenshot-1.png)
|
41 |
-
|
42 |
-
### U2F Security Keys section under User Profile.
|
43 |
-
|
44 |
-
![U2F Security Keys section under User Profile.](assets/screenshot-2.png)
|
45 |
-
|
46 |
-
### Email Code Authentication during WordPress Login.
|
47 |
|
48 |
-
|
49 |
|
50 |
-
|
|
|
51 |
|
52 |
-
Development happens [on GitHub](https://github.com/wordpress/two-factor/). Join the `#core-passwords` channel [on WordPress Slack](http://wordpress.slack.com) ([sign up here](http://chat.wordpress.org)).
|
53 |
-
|
54 |
-
Here is how to get started:
|
55 |
-
|
56 |
-
$ git clone https://github.com/wordpress/two-factor.git
|
57 |
-
$ npm install
|
58 |
-
|
59 |
Then open [a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) with the suggested changes.
|
60 |
|
61 |
## Changelog ##
|
62 |
|
63 |
-
See the [release history](https://github.com/
|
64 |
|
4 |
![Banner](assets/banner-1544x500.png)
|
5 |
Enable Two-Factor Authentication using time-based one-time passwords (OTP, Google Authenticator), Universal 2nd Factor (FIDO U2F, YubiKey), email and backup verification codes.
|
6 |
|
7 |
+
**Contributors:** [georgestephanis](https://profiles.wordpress.org/georgestephanis), [valendesigns](https://profiles.wordpress.org/valendesigns), [stevenkword](https://profiles.wordpress.org/stevenkword), [extendwings](https://profiles.wordpress.org/extendwings), [sgrant](https://profiles.wordpress.org/sgrant), [aaroncampbell](https://profiles.wordpress.org/aaroncampbell), [johnbillion](https://profiles.wordpress.org/johnbillion), [stevegrunwell](https://profiles.wordpress.org/stevegrunwell), [netweb](https://profiles.wordpress.org/netweb), [kasparsd](https://profiles.wordpress.org/kasparsd)
|
8 |
**Tags:** [two factor](https://wordpress.org/plugins/tags/two-factor), [two step](https://wordpress.org/plugins/tags/two-step), [authentication](https://wordpress.org/plugins/tags/authentication), [login](https://wordpress.org/plugins/tags/login), [totp](https://wordpress.org/plugins/tags/totp), [fido u2f](https://wordpress.org/plugins/tags/fido-u2f), [u2f](https://wordpress.org/plugins/tags/u2f), [email](https://wordpress.org/plugins/tags/email), [backup codes](https://wordpress.org/plugins/tags/backup-codes), [2fa](https://wordpress.org/plugins/tags/2fa), [yubikey](https://wordpress.org/plugins/tags/yubikey)
|
9 |
**Requires at least:** 4.3
|
10 |
+
**Tested up to:** 5.1
|
11 |
**Stable tag:** trunk (master)
|
|
|
12 |
|
13 |
[![Build Status](https://travis-ci.org/WordPress/two-factor.svg?branch=master)](https://travis-ci.org/WordPress/two-factor) [![Coverage Status](https://coveralls.io/repos/WordPress/two-factor/badge.svg?branch=master)](https://coveralls.io/github/WordPress/two-factor) [![Built with Grunt](https://gruntjs.com/cdn/builtwith.svg)](http://gruntjs.com)
|
14 |
|
15 |
## Description ##
|
16 |
|
17 |
+
For more history, see [this post](https://stephanis.info/2013/08/14/two-cents-on-two-factor/).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
|
19 |
+
## Get Involved ##
|
20 |
|
21 |
+
Development happens [on GitHub](https://github.com/georgestephanis/two-factor/). Join the `#core-passwords` channel [on WordPress Slack](http://wordpress.slack.com) ([sign up here](http://chat.wordpress.org)).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
|
23 |
+
Here is how to get started:
|
24 |
|
25 |
+
$ git clone https://github.com/georgestephanis/two-factor.git
|
26 |
+
$ npm install
|
27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
Then open [a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) with the suggested changes.
|
29 |
|
30 |
## Changelog ##
|
31 |
|
32 |
+
See the [release history](https://github.com/georgestephanis/two-factor/releases).
|
33 |
|
readme.txt
CHANGED
@@ -1,51 +1,27 @@
|
|
1 |
-
=== Two-Factor ===
|
2 |
-
Contributors: georgestephanis, valendesigns, stevenkword, extendwings, sgrant, aaroncampbell, johnbillion, stevegrunwell, netweb, kasparsd
|
3 |
-
Tags: two factor, two step, authentication, login, totp, fido u2f, u2f, email, backup codes, 2fa, yubikey
|
4 |
-
Requires at least: 4.3
|
5 |
-
Tested up to: 5.
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
- `two_factor_enabled_providers_for_user` filter overrides the list of two-factor providers enabled for a user. First argument is an array of enabled provider classnames as values, the second argument is the user ID.
|
29 |
-
- `two_factor_user_authenticated` action which receives the logged in `WP_User` object as the first argument for determining the logged in user right after the authentication workflow.
|
30 |
-
- `two_factor_token_ttl` filter overrides the time interval in seconds that an email token is considered after generation. Accepts the time in seconds as the first argument and the ID of the `WP_User` object being authenticated.
|
31 |
-
|
32 |
-
== Screenshots ==
|
33 |
-
|
34 |
-
1. Two-factor options under User Profile.
|
35 |
-
2. U2F Security Keys section under User Profile.
|
36 |
-
3. Email Code Authentication during WordPress Login.
|
37 |
-
|
38 |
-
== Get Involved ==
|
39 |
-
|
40 |
-
Development happens [on GitHub](https://github.com/wordpress/two-factor/). Join the `#core-passwords` channel [on WordPress Slack](http://wordpress.slack.com) ([sign up here](http://chat.wordpress.org)).
|
41 |
-
|
42 |
-
Here is how to get started:
|
43 |
-
|
44 |
-
$ git clone https://github.com/wordpress/two-factor.git
|
45 |
-
$ npm install
|
46 |
-
|
47 |
-
Then open [a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) with the suggested changes.
|
48 |
-
|
49 |
-
== Changelog ==
|
50 |
-
|
51 |
-
See the [release history](https://github.com/wordpress/two-factor/releases).
|
1 |
+
=== Two-Factor ===
|
2 |
+
Contributors: georgestephanis, valendesigns, stevenkword, extendwings, sgrant, aaroncampbell, johnbillion, stevegrunwell, netweb, kasparsd
|
3 |
+
Tags: two factor, two step, authentication, login, totp, fido u2f, u2f, email, backup codes, 2fa, yubikey
|
4 |
+
Requires at least: 4.3
|
5 |
+
Tested up to: 5.1
|
6 |
+
Stable tag: trunk
|
7 |
+
|
8 |
+
Enable Two-Factor Authentication using time-based one-time passwords (OTP, Google Authenticator), Universal 2nd Factor (FIDO U2F, YubiKey), email and backup verification codes.
|
9 |
+
|
10 |
+
== Description ==
|
11 |
+
|
12 |
+
For more history, see [this post](https://stephanis.info/2013/08/14/two-cents-on-two-factor/).
|
13 |
+
|
14 |
+
== Get Involved ==
|
15 |
+
|
16 |
+
Development happens [on GitHub](https://github.com/georgestephanis/two-factor/). Join the `#core-passwords` channel [on WordPress Slack](http://wordpress.slack.com) ([sign up here](http://chat.wordpress.org)).
|
17 |
+
|
18 |
+
Here is how to get started:
|
19 |
+
|
20 |
+
$ git clone https://github.com/georgestephanis/two-factor.git
|
21 |
+
$ npm install
|
22 |
+
|
23 |
+
Then open [a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) with the suggested changes.
|
24 |
+
|
25 |
+
== Changelog ==
|
26 |
+
|
27 |
+
See the [release history](https://github.com/georgestephanis/two-factor/releases).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
two-factor.php
CHANGED
@@ -1,19 +1,11 @@
|
|
1 |
<?php
|
2 |
/**
|
3 |
-
* Two Factor
|
4 |
-
*
|
5 |
-
* @package Two_Factor
|
6 |
-
* @author Plugin Contributors
|
7 |
-
* @copyright 2020 Plugin Contributors
|
8 |
-
* @license GPL-2.0-or-later
|
9 |
-
*
|
10 |
-
* @wordpress-plugin
|
11 |
* Plugin Name: Two Factor
|
12 |
* Plugin URI: https://wordpress.org/plugins/two-factor/
|
13 |
-
* Description:
|
14 |
-
* Author:
|
15 |
-
* Version: 0.
|
16 |
-
* Author URI: https://
|
17 |
* Network: True
|
18 |
* Text Domain: two-factor
|
19 |
*/
|
@@ -23,26 +15,14 @@
|
|
23 |
*/
|
24 |
define( 'TWO_FACTOR_DIR', plugin_dir_path( __FILE__ ) );
|
25 |
|
26 |
-
/**
|
27 |
-
* Version of the plugin.
|
28 |
-
*/
|
29 |
-
define( 'TWO_FACTOR_VERSION', '0.7.0' );
|
30 |
-
|
31 |
/**
|
32 |
* Include the base class here, so that other plugins can also extend it.
|
33 |
*/
|
34 |
-
require_once TWO_FACTOR_DIR . 'providers/class
|
35 |
|
36 |
/**
|
37 |
* Include the core that handles the common bits.
|
38 |
*/
|
39 |
-
require_once TWO_FACTOR_DIR . 'class
|
40 |
-
|
41 |
-
/**
|
42 |
-
* A compatability layer for some of the most-used plugins out there.
|
43 |
-
*/
|
44 |
-
require_once TWO_FACTOR_DIR . 'class-two-factor-compat.php';
|
45 |
-
|
46 |
-
$two_factor_compat = new Two_Factor_Compat();
|
47 |
|
48 |
-
Two_Factor_Core::add_hooks(
|
1 |
<?php
|
2 |
/**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
* Plugin Name: Two Factor
|
4 |
* Plugin URI: https://wordpress.org/plugins/two-factor/
|
5 |
+
* Description: A prototype extensible core to enable Two-Factor Authentication.
|
6 |
+
* Author: George Stephanis
|
7 |
+
* Version: 0.4.0
|
8 |
+
* Author URI: https://stephanis.info
|
9 |
* Network: True
|
10 |
* Text Domain: two-factor
|
11 |
*/
|
15 |
*/
|
16 |
define( 'TWO_FACTOR_DIR', plugin_dir_path( __FILE__ ) );
|
17 |
|
|
|
|
|
|
|
|
|
|
|
18 |
/**
|
19 |
* Include the base class here, so that other plugins can also extend it.
|
20 |
*/
|
21 |
+
require_once( TWO_FACTOR_DIR . 'providers/class.two-factor-provider.php' );
|
22 |
|
23 |
/**
|
24 |
* Include the core that handles the common bits.
|
25 |
*/
|
26 |
+
require_once( TWO_FACTOR_DIR . 'class.two-factor-core.php' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
|
28 |
+
Two_Factor_Core::add_hooks();
|