summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/OATHAuth
diff options
context:
space:
mode:
authorYaco <franco@reevo.org>2020-06-04 11:01:00 -0300
committerYaco <franco@reevo.org>2020-06-04 11:01:00 -0300
commitfc7369835258467bf97eb64f184b93691f9a9fd5 (patch)
treedaabd60089d2dd76d9f5fb416b005fbe159c799d /www/wiki/extensions/OATHAuth
first commit
Diffstat (limited to 'www/wiki/extensions/OATHAuth')
-rw-r--r--www/wiki/extensions/OATHAuth/CODE_OF_CONDUCT.md1
-rw-r--r--www/wiki/extensions/OATHAuth/COPYING339
-rw-r--r--www/wiki/extensions/OATHAuth/Gruntfile.js41
-rw-r--r--www/wiki/extensions/OATHAuth/OATHAuth.alias.php49
-rw-r--r--www/wiki/extensions/OATHAuth/OATHAuth.php29
-rw-r--r--www/wiki/extensions/OATHAuth/composer.json25
-rw-r--r--www/wiki/extensions/OATHAuth/extension.json120
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/af.json8
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/ar.json53
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/ast.json50
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/az.json10
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/ba.json15
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/be-tarask.json8
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/be.json19
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/bg.json26
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/bn.json37
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/br.json8
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/bs.json32
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/ce.json33
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/cs.json41
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/de-formal.json8
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/de.json46
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/diq.json10
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/dsb.json20
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/el.json13
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/en.json52
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/es.json45
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/et.json25
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/fa.json51
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/fi.json26
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/fr.json62
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/frp.json16
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/gl.json52
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/he.json52
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/hi.json13
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/hr.json21
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/hsb.json19
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/hu.json44
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/hy.json10
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/ia.json26
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/id.json14
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/is.json11
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/it.json44
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/ja.json23
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/jv.json40
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/ka.json24
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/ko.json50
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/ksh.json18
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/lb.json22
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/lki.json8
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/lt.json9
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/lv.json14
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/mk.json50
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/ml.json21
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/mr.json8
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/ms.json19
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/mwl.json12
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/nb.json51
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/ne.json8
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/nl.json27
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/oc.json15
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/pl.json30
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/pms.json19
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/ps.json8
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/pt-br.json55
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/pt.json52
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/qqq.json59
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/ro.json16
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/roa-tara.json16
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/ru.json57
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/sa.json8
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/sah.json8
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/sd.json8
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/si.json14
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/sk.json38
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/sq.json9
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/sr-ec.json10
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/sr-el.json8
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/sv.json32
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/ta.json8
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/te.json8
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/th.json21
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/tl.json20
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/tr.json26
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/uk.json52
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/ur.json14
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/vi.json40
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/yi.json8
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/zh-hans.json53
-rw-r--r--www/wiki/extensions/OATHAuth/i18n/zh-hant.json43
-rw-r--r--www/wiki/extensions/OATHAuth/includes/OATHAuthHooks.php221
-rw-r--r--www/wiki/extensions/OATHAuth/includes/OATHAuthKey.php187
-rw-r--r--www/wiki/extensions/OATHAuth/includes/OATHAuthUtils.php141
-rw-r--r--www/wiki/extensions/OATHAuth/includes/OATHUser.php83
-rw-r--r--www/wiki/extensions/OATHAuth/includes/OATHUserRepository.php103
-rw-r--r--www/wiki/extensions/OATHAuth/includes/api/ApiOATHValidate.php101
-rw-r--r--www/wiki/extensions/OATHAuth/includes/api/ApiQueryOATH.php90
-rw-r--r--www/wiki/extensions/OATHAuth/includes/auth/TOTPAuthenticationRequest.php43
-rw-r--r--www/wiki/extensions/OATHAuth/includes/auth/TOTPSecondaryAuthenticationProvider.php121
-rw-r--r--www/wiki/extensions/OATHAuth/includes/lib/base32.php105
-rw-r--r--www/wiki/extensions/OATHAuth/includes/lib/hotp.php179
-rw-r--r--www/wiki/extensions/OATHAuth/includes/special/ProxySpecialPage.php227
-rw-r--r--www/wiki/extensions/OATHAuth/includes/special/SpecialOATH.php44
-rw-r--r--www/wiki/extensions/OATHAuth/includes/special/SpecialOATHDisable.php136
-rw-r--r--www/wiki/extensions/OATHAuth/includes/special/SpecialOATHEnable.php241
-rw-r--r--www/wiki/extensions/OATHAuth/maintenance/disableOATHAuthForUser.php40
-rw-r--r--www/wiki/extensions/OATHAuth/maintenance/updateScratchTokenFormat.php51
-rw-r--r--www/wiki/extensions/OATHAuth/modules/ext.oath.showqrcode.js6
-rw-r--r--www/wiki/extensions/OATHAuth/modules/ext.oath.showqrcode.styles.css13
-rw-r--r--www/wiki/extensions/OATHAuth/modules/jquery.qrcode.js88
-rw-r--r--www/wiki/extensions/OATHAuth/modules/qrcode.js1237
-rw-r--r--www/wiki/extensions/OATHAuth/sql/mssql/tables.sql10
-rw-r--r--www/wiki/extensions/OATHAuth/sql/mysql/patch-remove_reset.sql8
-rw-r--r--www/wiki/extensions/OATHAuth/sql/mysql/tables.sql11
-rw-r--r--www/wiki/extensions/OATHAuth/sql/oracle/tables.sql15
-rw-r--r--www/wiki/extensions/OATHAuth/sql/postgres/tables.sql19
-rw-r--r--www/wiki/extensions/OATHAuth/tests/phan/config.php3
-rw-r--r--www/wiki/extensions/OATHAuth/tests/phpunit/TOTPAuthenticationRequestTest.php17
118 files changed, 6323 insertions, 0 deletions
diff --git a/www/wiki/extensions/OATHAuth/CODE_OF_CONDUCT.md b/www/wiki/extensions/OATHAuth/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..d8e5d087
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/CODE_OF_CONDUCT.md
@@ -0,0 +1 @@
+The development of this software is covered by a [Code of Conduct](https://www.mediawiki.org/wiki/Code_of_Conduct).
diff --git a/www/wiki/extensions/OATHAuth/COPYING b/www/wiki/extensions/OATHAuth/COPYING
new file mode 100644
index 00000000..d159169d
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/www/wiki/extensions/OATHAuth/Gruntfile.js b/www/wiki/extensions/OATHAuth/Gruntfile.js
new file mode 100644
index 00000000..4ce8b94a
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/Gruntfile.js
@@ -0,0 +1,41 @@
+/*jshint node:true */
+module.exports = function ( grunt ) {
+ grunt.loadNpmTasks( 'grunt-contrib-jshint' );
+ grunt.loadNpmTasks( 'grunt-banana-checker' );
+ grunt.loadNpmTasks( 'grunt-jsonlint' );
+ grunt.loadNpmTasks( 'grunt-stylelint' );
+
+ grunt.initConfig( {
+ jshint: {
+ options: {
+ jshintrc: true
+ },
+ all: [
+ '**/*.js',
+ '!modules/qrcode.js',
+ '!node_modules/**',
+ '!vendor/**'
+ ]
+ },
+ banana: {
+ all: 'i18n/'
+ },
+ jsonlint: {
+ all: [
+ '**/*.json',
+ '!node_modules/**',
+ '!vendor/**'
+ ]
+ },
+ stylelint: {
+ all: [
+ '**/*.css',
+ '!node_modules/**',
+ '!vendor/**'
+ ]
+ }
+ } );
+
+ grunt.registerTask( 'test', [ 'jshint', 'jsonlint', 'banana', 'stylelint' ] );
+ grunt.registerTask( 'default', 'test' );
+};
diff --git a/www/wiki/extensions/OATHAuth/OATHAuth.alias.php b/www/wiki/extensions/OATHAuth/OATHAuth.alias.php
new file mode 100644
index 00000000..6d59f9a6
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/OATHAuth.alias.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * Aliases for OATHAuth's special pages
+ *
+ * @file
+ * @ingroup Extensions
+ */
+
+$specialPageAliases = [];
+
+/** English (English) */
+$specialPageAliases['en'] = [
+ 'OATH' => [ 'Two-factor authentication', 'OATH', 'OATHAuth' ],
+];
+
+/** Arabic (العربية) */
+$specialPageAliases['ar'] = [
+ 'OATH' => [ 'أواث', 'أواث_أوث' ],
+];
+
+/** Egyptian Arabic (مصرى) */
+$specialPageAliases['arz'] = [
+ 'OATH' => [ 'اواث', 'اواث_اوث' ],
+];
+
+/** Czech (čeština) */
+$specialPageAliases['cs'] = [
+ 'OATH' => [ 'Dvoufaktorové_ověření' ],
+];
+
+/** Northern Luri (لۊری شومالی) */
+$specialPageAliases['lrc'] = [
+ 'OATH' => [ 'قأسأم' ],
+];
+
+/** Urdu (اردو) */
+$specialPageAliases['ur'] = [
+ 'OATH' => [ 'حلف_نامہ' ],
+];
+
+/** Simplified Chinese (中文(简体)‎) */
+$specialPageAliases['zh-hans'] = [
+ 'OATH' => [ 'OATH验证' ],
+];
+
+/** Traditional Chinese (中文(繁體)‎) */
+$specialPageAliases['zh-hant'] = [
+ 'OATH' => [ 'OATH_認證' ],
+];
diff --git a/www/wiki/extensions/OATHAuth/OATHAuth.php b/www/wiki/extensions/OATHAuth/OATHAuth.php
new file mode 100644
index 00000000..6cf00b0a
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/OATHAuth.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * OATHAuth extension - Support for HMAC based one time passwords
+ *
+ *
+ * For more info see http://mediawiki.org/wiki/Extension:OATHAuth
+ *
+ * @file
+ * @ingroup Extensions
+ * @author Ryan Lane <rlane@wikimedia.org>
+ * @copyright © 2012 Ryan Lane
+ * @license GPL-2.0-or-later
+ */
+
+if ( function_exists( 'wfLoadExtension' ) ) {
+ wfLoadExtension( 'OATHAuth' );
+
+ $wgMessagesDirs['OATHAuth'] = __DIR__ . '/i18n';
+ $wgExtensionMessagesFiles['OATHAuthAlias'] = __DIR__ . '/OATHAuth.alias.php';
+
+ /* wfWarn(
+ 'Deprecated PHP entry point used for OATHAuth extension. Please use wfLoadExtension instead, ' .
+ 'see https://www.mediawiki.org/wiki/Extension_registration for more details.'
+ ); */
+
+ return true;
+} else {
+ die( 'This version of the OATHAuth extension requires MediaWiki 1.25+' );
+}
diff --git a/www/wiki/extensions/OATHAuth/composer.json b/www/wiki/extensions/OATHAuth/composer.json
new file mode 100644
index 00000000..c48241db
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/composer.json
@@ -0,0 +1,25 @@
+{
+ "require-dev": {
+ "jakub-onderka/php-parallel-lint": "1.0.0",
+ "mediawiki/mediawiki-codesniffer": "18.0.0",
+ "jakub-onderka/php-console-highlighter": "0.3.2",
+ "mediawiki/minus-x": "0.3.1",
+ "mediawiki/mediawiki-phan-config": "0.2.0"
+ },
+ "scripts": {
+ "lint": "parallel-lint . --exclude vendor --exclude node_modules",
+ "phpcs": "phpcs -p -s",
+ "fix": [
+ "phpcbf",
+ "minus-x fix ."
+ ],
+ "test": [
+ "composer lint",
+ "composer phpcs",
+ "minus-x check ."
+ ]
+ },
+ "extra": {
+ "phan-taint-check-plugin": "1.2.0"
+ }
+}
diff --git a/www/wiki/extensions/OATHAuth/extension.json b/www/wiki/extensions/OATHAuth/extension.json
new file mode 100644
index 00000000..c6b58388
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/extension.json
@@ -0,0 +1,120 @@
+{
+ "name": "OATHAuth",
+ "version": "0.2.2",
+ "author": "Ryan Lane",
+ "url": "https://www.mediawiki.org/wiki/Extension:OATHAuth",
+ "descriptionmsg": "oathauth-desc",
+ "type": "other",
+ "requires": {
+ "MediaWiki": ">= 1.31.0"
+ },
+ "license-name": "GPL-2.0-or-later AND GPL-3.0-or-later",
+ "AutoloadClasses": {
+ "ApiOATHValidate": "includes/api/ApiOATHValidate.php",
+ "ApiQueryOATH": "includes/api/ApiQueryOATH.php",
+ "OATHAuthHooks": "includes/OATHAuthHooks.php",
+ "OATHAuthKey": "includes/OATHAuthKey.php",
+ "OATHAuthUtils": "includes/OATHAuthUtils.php",
+ "OATHUserRepository": "includes/OATHUserRepository.php",
+ "HOTP": "includes/lib/hotp.php",
+ "HOTPResult": "includes/lib/hotp.php",
+ "Base32": "includes/lib/base32.php",
+ "OATHUser": "includes/OATHUser.php",
+ "SpecialOATH": "includes/special/SpecialOATH.php",
+ "SpecialOATHEnable": "includes/special/SpecialOATHEnable.php",
+ "SpecialOATHDisable": "includes/special/SpecialOATHDisable.php",
+ "ProxySpecialPage": "includes/special/ProxySpecialPage.php",
+ "TOTPAuthenticationRequest": "includes/auth/TOTPAuthenticationRequest.php",
+ "TOTPSecondaryAuthenticationProvider": "includes/auth/TOTPSecondaryAuthenticationProvider.php"
+ },
+ "AuthManagerAutoConfig": {
+ "secondaryauth": {
+ "TOTPSecondaryAuthenticationProvider":{
+ "class": "TOTPSecondaryAuthenticationProvider",
+ "sort": 50
+ }
+ }
+ },
+ "ExtensionMessagesFiles": {
+ "OATHAuthAlias": "OATHAuth.alias.php"
+ },
+ "Hooks": {
+ "AuthChangeFormFields": [
+ "OATHAuthHooks::onAuthChangeFormFields"
+ ],
+ "TwoFactorIsEnabled": [
+ "OATHAuthHooks::onTwoFactorIsEnabled"
+ ],
+ "LoadExtensionSchemaUpdates": [
+ "OATHAuthHooks::onLoadExtensionSchemaUpdates"
+ ],
+ "GetPreferences": [
+ "OATHAuthHooks::onGetPreferences"
+ ]
+ },
+ "MessagesDirs": {
+ "OATHAuth": [
+ "i18n"
+ ]
+ },
+ "config": {
+ "OATHAuthWindowRadius": 4,
+ "OATHAuthDatabase": false,
+ "OATHAuthSecret": false,
+ "OATHAuthAccountPrefix": false
+ },
+ "ResourceModules": {
+ "ext.oath.showqrcode": {
+ "scripts": [
+ "jquery.qrcode.js",
+ "qrcode.js",
+ "ext.oath.showqrcode.js"
+ ]
+ },
+ "ext.oath.showqrcode.styles": {
+ "styles": [
+ "ext.oath.showqrcode.styles.css"
+ ]
+ }
+ },
+ "ResourceFileModulePaths": {
+ "localBasePath": "modules",
+ "remoteExtPath": "OATHAuth"
+ },
+ "SpecialPages": {
+ "OATH": "SpecialOATH"
+ },
+ "AvailableRights": [
+ "oathauth-enable",
+ "oathauth-api-all"
+ ],
+ "GroupPermissions": {
+ "*": {
+ "oathauth-enable": true
+ }
+ },
+ "GrantPermissions": {
+ "oath": {
+ "oathauth-api-all": true
+ }
+ },
+ "GrantPermissionGroups": {
+ "oath": "authentication"
+ },
+ "APIModules": {
+ "oathvalidate": "ApiOATHValidate"
+ },
+ "APIMetaModules": {
+ "oath": "ApiQueryOATH"
+ },
+ "RateLimits": {
+ "badoath": {
+ "&can-bypass": false,
+ "user": [
+ 10,
+ 60
+ ]
+ }
+ },
+ "manifest_version": 1
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/af.json b/www/wiki/extensions/OATHAuth/i18n/af.json
new file mode 100644
index 00000000..3b7c2234
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/af.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Naudefj"
+ ]
+ },
+ "oathauth-notloggedin": "Aanmelding is verpligtend"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/ar.json b/www/wiki/extensions/OATHAuth/i18n/ar.json
new file mode 100644
index 00000000..5c2022c9
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/ar.json
@@ -0,0 +1,53 @@
+{
+ "@metadata": {
+ "authors": [
+ "محمد أحمد عبد الفتاح",
+ "علاء",
+ "Meno25",
+ "ديفيد"
+ ]
+ },
+ "oathauth-desc": "يوفر دعم المصادقة باستخدام كلمات مرور لمرة واحدة تستند إلى HMAC",
+ "specialpages-group-oath": "توثيق ذو عاملين",
+ "oathauth-account": "اسم المستخدم:",
+ "oathauth-secret": "رمز أمان التوثيق ذي العاملين:",
+ "oathauth-enable": "تفعيل التوثيق ذي العاملين",
+ "oathauth-scratchtokens": "القائمة التالية عبارة عن قائمة برموز سكراتش للاستخدام لمرة واحدة، يمكن استخدام هذه الرموز المميزة مرة واحدة فقط، وهي للاستخدام في حالات الطوارئ، يُرجَى كتابتها والاحتفاظ بها في مكان آمن، إذا فقدت هاتفك، فهذه الرموز المميزة هي الطريقة الوحيدة لإنقاذ حسابك، '''لن يتم عرض هذه الرموز مرة أخرى أبدا'''.",
+ "oathauth-token": "الرمز",
+ "oathauth-disable": "تعطيل التوثيق ذي العاملين",
+ "oathauth-validatedoath": "شهادات التوثيق ذو العاملين مُعتمدة. سينفذ التوثيق ذو العاملين منذ الآن.",
+ "oathauth-noscratchforvalidation": "لا يمكنك استخدام رمز سكراتش لتأكيد المصادقة الثنائية; تكون رموز الخدوش للنسخ الاحتياطي والاستخدام العرضي فقط، يُرجَى استخدام رمز التحقق من مولد الكود الخاص بك.",
+ "oathauth-failedtovalidateoath": "فشل التحقق من صحة بيانات الاعتماد الثنائية",
+ "oathauth-disabledoath": "تم تعطيل التوثيق ذو العاملين.",
+ "oathauth-prefs-label": "توثيق ذو عاملين:",
+ "oathauth-step1": "الخطوة 1: نزل برنامج عاملي التوثيق",
+ "oathauth-step1-test": "حمل تطبيقًا يساعد في التوثيق ذي العاملين. من الممكن أن يكون تطبيق هاتف (مثل Google Authenticator) أو تطبيقًا لسطح المكتب.",
+ "oathauth-step2": "الخطوة 2: امسح الرمز المربع",
+ "oathauth-step2alt": "أو أدخل رمز الأمان بشكل يدوي:",
+ "oathauth-step3": "الخطوة 3: اكتب الرُموز الواردة في الأسفل",
+ "oathauth-step4": "الخطوة 4: التحقق",
+ "oathauth-entertoken": "أدخل رمز التحقق من جهازك للتأكيد:",
+ "right-oathauth-enable": "تفعيل ميزة التحقق بخطوتين",
+ "action-oathauth-enable": "تفعيل ميزة التحقق بخطوتين",
+ "oathauth-auth-token-label": "الرمز",
+ "oathauth-auth-token-help": "تُستخدَم كلمة المرور لمرة واحدة كعامل ثاني للمصادقة الثنائية.",
+ "oathauth-auth-ui": "رجاءً أدخل رمز التحقق من جهاز المصادقة الخاص بك.",
+ "oathauth-throttled": "هناك الكثير من محاولات التحقق! الرجاء انتظار $1.",
+ "oathauth-login-failed": "فشل التحقق.",
+ "oathauth-describe-provider": "توثيق ذو عاملين (OATH)",
+ "grant-group-authentication": "أداء إجراءات التوثيق لنفسك والآخرين",
+ "grant-oath": "الوصول إلى معلومات المصادقة الثنائية (OATH) للذات وغيرها",
+ "right-oathauth-api-all": "الاستعلام والتحقق من صحة معلومات OATH للنفس والآخرين",
+ "action-oathauth-api-all": "تحقق من حالة OATH",
+ "apihelp-query+oath-description": "تحقق لمعرفة ما إذا تم تمكين المصادقة الثنائية (OATH) للمستخدم.",
+ "apihelp-query+oath-summary": "تحقق لمعرفة ما إذا تم تمكين المصادقة الثنائية (OATH) للمستخدم.",
+ "apihelp-query+oath-param-user": "المستخدم للحصول على معلومات عنه، افتراضي للمستخدم الحالي.",
+ "apihelp-query+oath-example-1": "الحصول على معلومات حول المستخدم الحالي",
+ "apihelp-query+oath-example-2": "الحصول على معلومات حول المستخدم <kbd>Example</kbd>",
+ "apihelp-oathvalidate-description": "تحقق من صحة رمز المصادقة الثنائية (OATH).",
+ "apihelp-oathvalidate-summary": "تحقق من صحة رمز المصادقة الثنائية (OATH).",
+ "apihelp-oathvalidate-param-user": "مستخدم للتحقق من صحة الرمز المميز له، افتراضي للمستخدم الحالي.",
+ "apihelp-oathvalidate-param-totp": "رمز المصادقة الثنائية (OATH).",
+ "apihelp-oathvalidate-example-1": "تحقق من رمز مميز للمستخدم الحالي",
+ "apihelp-oathvalidate-example-2": "قق من رمز مستخدم <kbd>Example</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/ast.json b/www/wiki/extensions/OATHAuth/i18n/ast.json
new file mode 100644
index 00000000..ff6b7cd8
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/ast.json
@@ -0,0 +1,50 @@
+{
+ "@metadata": {
+ "authors": [
+ "Xuacu"
+ ]
+ },
+ "oathauth-desc": "Ufre soporte d'identificación utilizando contraseñes pa una sola vez basaes en HMAC",
+ "specialpages-group-oath": "Identificación de dos factores",
+ "oathauth-account": "Nome de la cuenta:",
+ "oathauth-secret": "Clave secreta d'identificación en dos pasos:",
+ "oathauth-enable": "Activar la identificación de dos factores",
+ "oathauth-scratchtokens": "La siguiente ye una llista de pases d'un solu usu. Estos pases sólo pueen utilizase una vez, y son pa usu d'emerxencia. Por favor, apúntales y guárdales nun llugar seguru. Si pierdes el teléfonu, estos pases son la única manera de rescatar la cuenta. '''Estos pases nunca volverán a apaecer'''.",
+ "oathauth-token": "Pase",
+ "oathauth-disable": "Desactivar la identificación de dos factores",
+ "oathauth-validatedoath": "Validáronse les credenciales de dos factores. Agora sedrá obligatoria la identificación de dos factores.",
+ "oathauth-noscratchforvalidation": "Nun puedes utilizar un códigu predefiníu pa confirmar la identificación en dos pasos. Esos códigos solo sirven como respaldu y pa usu ocasional. Utiliza un códigu de comprobación del to xenerador de códigos.",
+ "oathauth-failedtovalidateoath": "Nun pudieron validase les credenciales de dos factores",
+ "oathauth-disabledoath": "Desactivada la identificación de dos factores.",
+ "oathauth-prefs-label": "Identificación de dos factores:",
+ "oathauth-step1": "Pasu 1: Descargar un programa d'identificación en dos pasos",
+ "oathauth-step1-test": "Descarga un programa pa identificación en dos pasos. Pué ser una aplicación pa móvil (como Google Authenticator) o d'escritoriu.",
+ "oathauth-step2": "Pasu 2: escanea'l códigu QR",
+ "oathauth-step2alt": "O escribe'l códigu secretu manualmente:",
+ "oathauth-step3": "Pasu 3: Apunta los códigos",
+ "oathauth-step4": "Pasu 4: Comprobación",
+ "oathauth-entertoken": "Escribe'l códigu dende'l preséu d'identificación a comprobar:",
+ "right-oathauth-enable": "Activar la identificación de dos factores",
+ "action-oathauth-enable": "activar la identificación de dos factores",
+ "oathauth-auth-token-label": "Pase",
+ "oathauth-auth-token-help": "La contraseña d'un usu emplegada como segundu factor na autenticación de dos pasos.",
+ "oathauth-auth-ui": "Escribe un códigu de comprobación dende'l preséu d'identificación",
+ "oathauth-throttled": "Demasiaos intentos de comprobación. Espera $1.",
+ "oathauth-login-failed": "Falló la comprobación.",
+ "oathauth-describe-provider": "Identificación de dos factores (OATH).",
+ "grant-group-authentication": "Facer les aiciones d'autenticación propies y pa otros",
+ "grant-oath": "Acceder a la información d'autenticación en dos factores (OAuth) propies y pa otros",
+ "right-oathauth-api-all": "Consultar y validar la información OATH propia y d'otros",
+ "action-oathauth-api-all": "comprobar l'estatus OATH",
+ "apihelp-query+oath-description": "Comprobar si la identificación de dos factores (OATH) ta activada pa un usuariu.",
+ "apihelp-query+oath-summary": "Comprobar si la identificación de dos factores (OATH) tá activada pa un usuariu.",
+ "apihelp-query+oath-param-user": "Usuariu del qu'algamar la información. De mou predetermináu, ye l'usuariu actual.",
+ "apihelp-query+oath-example-1": "Recibir información sobro l'usuariu actual.",
+ "apihelp-query+oath-example-2": "Recibir información sobro l'usuariu <kbd>Example</kbd>",
+ "apihelp-oathvalidate-description": "Validar un pase d'identificación de dos factores (OATH)",
+ "apihelp-oathvalidate-summary": "Validar un pase d'identificación de dos factores (OATH)",
+ "apihelp-oathvalidate-param-user": "Usuariu pal que validar el pase. De mou predetermináu, l'usuariu actual.",
+ "apihelp-oathvalidate-param-totp": "Pase d'identificación de dos factores (OATH).",
+ "apihelp-oathvalidate-example-1": "Validar un pase pal usuariu actual",
+ "apihelp-oathvalidate-example-2": "Validar un pase pal usuariu <kbd>Example</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/az.json b/www/wiki/extensions/OATHAuth/i18n/az.json
new file mode 100644
index 00000000..03e76ef2
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/az.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Zpizza",
+ "Wertuose"
+ ]
+ },
+ "oathauth-account": "Hesab adı:",
+ "right-oathauth-enable": "ikifaktorlu autentifikasiyanı tətbiq et"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/ba.json b/www/wiki/extensions/OATHAuth/i18n/ba.json
new file mode 100644
index 00000000..f26d589b
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/ba.json
@@ -0,0 +1,15 @@
+{
+ "@metadata": {
+ "authors": [
+ "Айсар",
+ "Вильданова Гюзель",
+ "Ләйсән"
+ ]
+ },
+ "oathauth-step1": "1-се аҙым: ҡушымтаны ҡуйығыҙ",
+ "oathauth-step2alt": "Йә серһүҙҙе ҡулдан яҙығыҙ:",
+ "oathauth-step3": "3-сө аҙым: Кодтарҙы яҙығыҙ",
+ "oathauth-step4": "4-се аҙым: Тикшереү",
+ "oathauth-entertoken": "Тикшереү өсөн кодты мобиль ҡушымта ярҙамында яҙығыҙ:",
+ "oathauth-auth-token-label": "Тамға"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/be-tarask.json b/www/wiki/extensions/OATHAuth/i18n/be-tarask.json
new file mode 100644
index 00000000..88ca9f0e
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/be-tarask.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Red Winged Duck"
+ ]
+ },
+ "specialpages-group-oath": "Двухфактарная аўтэнтыфікацыя"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/be.json b/www/wiki/extensions/OATHAuth/i18n/be.json
new file mode 100644
index 00000000..1b7a519b
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/be.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Artsiom91"
+ ]
+ },
+ "oathauth-account": "Імя ўліковага запісу:",
+ "oathauth-secret": "Двухфактарны сакрэтны ключ аўтэнтыфікацыі:",
+ "oathauth-scratchtokens": "Гэта — спіс аднаразовых токенаў. Гэтыя токены могуць быць выкарыстаны толькі адзін раз і прызначаны для выкарыстання ў надзвычайных сітуацыях. Калі ласка, запішыце іх і захоўвайце ў бяспечным месцы. Калі Вы страціце свой тэлефон, яны будуць адзіным спосабам выратаваць Ваш акаўнт. '''Гэтыя токены болей ніколі не будуць паказаныя'''.",
+ "oathauth-step1": "Этап 1: Спампуйце праграму двухфактарнай аўтэнтыфікацыі",
+ "oathauth-step2": "Этап 2: Адсканіруйце QR-код",
+ "oathauth-step2alt": "Або ўвядзіце сакрэтны код уручную:",
+ "oathauth-step3": "Этап 3: Запішыце скрэтч-коды",
+ "oathauth-step4": "Этап 4: Праверка",
+ "oathauth-entertoken": "Увядзіце код з Вашай прылады праверкі сапраўднасці, каб праверыць:",
+ "right-oathauth-enable": "уключэнне двухфактарнай аўтэнтыфікацыі",
+ "action-oathauth-enable": "уключыць двухфактарную аўтэнтыфікацыю",
+ "oathauth-auth-ui": "Калі ласка, увядзіце код пацвярджэння з прылады праверкі сапраўднасці"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/bg.json b/www/wiki/extensions/OATHAuth/i18n/bg.json
new file mode 100644
index 00000000..917052e1
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/bg.json
@@ -0,0 +1,26 @@
+{
+ "@metadata": {
+ "authors": [
+ "StanProg",
+ "ShockD"
+ ]
+ },
+ "specialpages-group-oath": "Двукомпонентно удостоверяване",
+ "oathauth-account": "Име на сметка:",
+ "oathauth-secret": "Таен ключ на двукомпонентното удостоверяване:",
+ "oathauth-enable": "Включване на двукомпонентно удостоверяване",
+ "oathauth-scratchtokens": "Следният списък съдържа еднократни маркери. Тези маркери могат да бъдат използвани само веднъж и са за спешни случаи. Моля, запишете ги и ги пазете на сигурно място. Ако загубите телефона си, тези маркери са единственият начин да спасите сметката си. Тези маркери никога повече няма да бъдат показани.",
+ "oathauth-disable": "Изключване на двукомпонентно удостоверяване",
+ "oathauth-disabledoath": "Двукомпонентното удостоверяване е изключено.",
+ "oathauth-prefs-label": "Двукомпонентно удостоверяване:",
+ "oathauth-step1": "Стъпка 1: Изтегляне на софтуера за двукомпонентно удостоверяване",
+ "oathauth-step1-test": "Изтеглете програма за двукомпонентно удостоверяване. Това може да е мобилно приложение (като Google Authenticator) или настолно приложение",
+ "oathauth-step2": "Стъпка 2: Сканиране на QR кода",
+ "oathauth-step2alt": "Или напишете тайната ръчно:",
+ "oathauth-step3": "Стъпка 3: Запишете кодовете",
+ "oathauth-step4": "Стъпка 4: Потвърждение",
+ "oathauth-entertoken": "Въведете код от устройството Ви за удостоверяване, за проверка:",
+ "right-oathauth-enable": "Включване на двукомпонентно удостоверяване",
+ "action-oathauth-enable": "включване на двукомпонентно удостоверяване",
+ "oathauth-describe-provider": "Двукомпонентно удостоверяване (OATH)."
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/bn.json b/www/wiki/extensions/OATHAuth/i18n/bn.json
new file mode 100644
index 00000000..61edf119
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/bn.json
@@ -0,0 +1,37 @@
+{
+ "@metadata": {
+ "authors": [
+ "Aftabuzzaman",
+ "Elias Ahmmad"
+ ]
+ },
+ "oathauth-desc": "HMAC ভিত্তিক এক-কালীন পাসওয়ার্ড ব্যবহার করে প্রমাণীকরণ প্রদান করে",
+ "specialpages-group-oath": "দুই-গুণনীয়ক প্রমাণীকরণ",
+ "oathauth-account": "অ্যাকাউন্টের নাম:",
+ "oathauth-secret": "দুই-গুণনীয়ক প্রমাণীকরণের গোপন চাবি:",
+ "oathauth-enable": "দুই-গুণনীয়ক প্রমাণীকরণ সক্রিয় করুন",
+ "oathauth-scratchtokens": "নিম্নলিখিত তালিকাটি হচ্ছে এককালীন ব্যবহারের স্ক্র্যাচ টোকেনের একটি তালিকা। এই টোকেন শুধুমাত্র একবার ব্যবহার করা যাবে, এবং এগুলি জরুরি ব্যবহারের জন্য। দয়া করে এইগুলি লিখে রাখুন এবং তাদের একটি নিরাপদ স্থানে রাখুন। আপনি যদি আপনার ফোন হারিয়ে ফেলেন, তাহলে এই টোকেনগুলি হচ্ছে আপনার অ্যাকাউন্ট উদ্ধার করার একমাত্র উপায়। '''এই টোকেনগুলি আর কখনো দেখানো হবে না'''।",
+ "oathauth-token": "টোকেন",
+ "oathauth-disable": "দুই-গুণনীয়ক প্রমাণীকরণ নিষ্ক্রিয় করুন",
+ "oathauth-validatedoath": "দুই-গুণনীয়ক পরিচয়পত্র যাচাই করা হয়েছে। এখন থেকে দুই-গুণনীয়ক প্রমাণীকরণ প্রয়োগ করা হবে।",
+ "oathauth-failedtovalidateoath": "দুই-গুণনীয়ক পরিচয়পত্র বৈধকরণ ব্যর্থ হয়েছে",
+ "oathauth-disabledoath": "দুই-গুণনীয়ক প্রমাণীকরণ নিষ্ক্রিয় করুন।",
+ "oathauth-prefs-label": "দুই-গুণনীয়ক প্রমাণীকরণ:",
+ "oathauth-step1": "ধাপ ১: অ্যাপটি ডাউনলোড করুন",
+ "oathauth-step1-test": "আপনার ফোনে দুই-গুণনীয়ক প্রমাণীকরণের জন্য একটি মোবাইল অ্যাপ্লিকেশন (যেমন Google Authenticator) ডাউনলোড করুন।",
+ "oathauth-step2": "ধাপ ২: কিউআর কোডটি স্ক্যান করুন",
+ "oathauth-step2alt": "বা গোপন কোড নিজ হাতে লিখুন:",
+ "oathauth-step3": "ধাপ ৩: স্ক্র্যাচ কোড লিখুন",
+ "oathauth-step4": "ধাপ ৪: নিশ্চিতকরণ",
+ "oathauth-entertoken": "যাচাই করতে আপনার মোবাইল অ্যাপ থেকে একটি কোড লিখুন:",
+ "right-oathauth-enable": "দুই-গুণনীয়ক প্রমাণীকরণ সক্রিয় করে",
+ "action-oathauth-enable": "দুই-গুণনীয়ক প্রমাণীকরণ সক্রিয় করার",
+ "oathauth-auth-token-label": "টোকেন",
+ "oathauth-auth-token-help": "এককালীন পাসওয়ার্ড দুই-গুণনীয়ক প্রমাণীকরণের দ্বিতীয় গুণক হিসেবে ব্যবহৃত হয়।",
+ "oathauth-auth-ui": "আপনার প্রমাণীকরণ ডিভাইস থেকে যাচাইকরণ কোড প্রবেশ করান",
+ "oathauth-throttled": "অনেকগুলি যাচাইয়ের প্রচেষ্টা! অনুগ্রহ করে $1 অপেক্ষা করুন।",
+ "oathauth-login-failed": "যাচাইকরণ ব্যর্থ হয়েছে।",
+ "oathauth-describe-provider": "দুই-গুণনীয়ক প্রমাণীকরণ (OATH)।",
+ "action-oathauth-api-all": "OATH অবস্থা পরীক্ষা করার",
+ "apihelp-oathvalidate-example-1": "বর্তমান ব্যবহারকারীর জন্য একটি টোকেন যাচাই করুন"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/br.json b/www/wiki/extensions/OATHAuth/i18n/br.json
new file mode 100644
index 00000000..d2ed48ce
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/br.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Fohanno"
+ ]
+ },
+ "oathauth-token": "Jedouer"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/bs.json b/www/wiki/extensions/OATHAuth/i18n/bs.json
new file mode 100644
index 00000000..028ecf95
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/bs.json
@@ -0,0 +1,32 @@
+{
+ "@metadata": {
+ "authors": [
+ "Srdjan m"
+ ]
+ },
+ "specialpages-group-oath": "Potvrda u 2 koraka",
+ "oathauth-account": "Ime računa:",
+ "oathauth-secret": "Tajni ključ za potvrdu u 2 koraka:",
+ "oathauth-enable": "Uključi potvrdu u 2 koraka",
+ "oathauth-scratchtokens": "Sljedeći spisak sadrži žetone za jednokratnu upotrebu. Mogu se koristiti samo jednom i to u hitnim slučajevima. Zapišite i čuvajte ih na sigurnom mjestu. Ako izgubite telefon, jedino će oni moći spasiti Vaš račun. Ovi žetoni više se nikad neće prikazati.",
+ "oathauth-token": "Žeton",
+ "oathauth-disable": "Isključi potvrdu u 2 koraka",
+ "oathauth-validatedoath": "Podaci potvrđeni. Odsad će se provoditi potvrda u 2 koraka.",
+ "oathauth-failedtovalidateoath": "Ne mogu potvrditi dvofaktorske podatke",
+ "oathauth-disabledoath": "Potvrda u 2 koraka isključena.",
+ "oathauth-prefs-label": "Potvrda u 2 koraka:",
+ "oathauth-step1": "1. korak: Preuzmite program za potvrdu u 2 koraka",
+ "oathauth-step1-test": "Preuzmite program za potvrdu u 2 koraka. Može biti mobilna (naprimjer, Google Authenticator) ili računarska aplikacija",
+ "oathauth-step2": "2. korak: Skenirajte QR kôd",
+ "oathauth-step2alt": "Ili ručno upišite tajnu:",
+ "oathauth-step3": "3. korak: Zapišite kodove",
+ "oathauth-step4": "4. korak: Potvrda",
+ "oathauth-entertoken": "Upišite kôd za potvrdu s Vašeg uređaja:",
+ "right-oathauth-enable": "Uključivanje potvrde u 2 koraka",
+ "action-oathauth-enable": "uključite potvrdu u 2 koraka",
+ "oathauth-auth-token-label": "Žeton",
+ "oathauth-auth-ui": "Upišite kôd za potvrdu s Vašeg uređaja",
+ "oathauth-throttled": "Previše pokušaja potvrde! Sačekajte $1.",
+ "oathauth-login-failed": "Potvrda nije uspjela.",
+ "oathauth-describe-provider": "Potvrda u 2 koraka (OATH)."
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/ce.json b/www/wiki/extensions/OATHAuth/i18n/ce.json
new file mode 100644
index 00000000..d67b8af4
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/ce.json
@@ -0,0 +1,33 @@
+{
+ "@metadata": {
+ "authors": [
+ "Умар"
+ ]
+ },
+ "oathauth-desc": "НМАСан гӀоьнца бакъхиларна таллам барехь гӀо латтадо, цкъа лелочу паролашца",
+ "specialpages-group-oath": "шина-факторан аутентификаци",
+ "oathauth-account": "Декъашхочун цӀе:",
+ "oathauth-secret": "Шина-факторан аутентификацин къайла догӀа:",
+ "oathauth-enable": "Шина-факторан аутентификаци латае",
+ "oathauth-scratchtokens": "ХӀара бу — цхьауз лелош йоцу токенийн могӀам. ХӀара токенаш цхьауз бен лелош яц, уьш леррина ю чӀогӀа ситуацеш хилча лело. Дехар до, уьш дӀаязъяр а. Ӏалашъяр а. Ахьа хьайн телефон яйахь, цера гӀоьнца хьан аккаунт меттахӀотто йиш хир ю. И токенаш кхий цкъа а гойтур яц.",
+ "oathauth-token": "Токен",
+ "oathauth-disable": "шина-факторан аутентификаци дӀаяйа",
+ "oathauth-failedtovalidateoath": "Шина-факторан аутентификацин хаамаш хьажа цаделий",
+ "oathauth-disabledoath": "шина-факторан аутентификаци дӀаяйина",
+ "oathauth-prefs-label": "Шина-факторан аутентификаци:",
+ "oathauth-step1": "Ког 1: Шина-факторан аутентификацин программа чуяккха",
+ "oathauth-step1-test": "Шина-факторан аутентификацин программа чуяккха, иза хила мега телефонан программа (масала, Google Authenticator) я кхиин",
+ "oathauth-step2": "Ког 2: QR-кодан сканер яккха",
+ "oathauth-step2alt": "Я куьга дӀаязъе къайла код:",
+ "oathauth-step3": "Ког 3: ДӀаязъе скретч кодаш",
+ "oathauth-step4": "Ког 4: Таллар",
+ "oathauth-entertoken": "Хьайн телефон тӀера код дӀаязъе, хьажархьама:",
+ "right-oathauth-enable": "шина-факторан аутентификаци латор",
+ "action-oathauth-enable": "шина-факторан аутентификаци латае",
+ "oathauth-auth-token-label": "Токен",
+ "oathauth-auth-ui": "Дехар до, хьайн телефон тӀера код дӀаязъе, хьажархьама:",
+ "oathauth-login-failed": "Хьажа цаделий",
+ "oathauth-describe-provider": "Шина-факторан аутентификаци (OATH).",
+ "grant-oath": "Шина-факторан аутентификаци (OAUTH) тӀекхачар",
+ "action-oathauth-api-all": "Хьажа OATH статус"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/cs.json b/www/wiki/extensions/OATHAuth/i18n/cs.json
new file mode 100644
index 00000000..1783d100
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/cs.json
@@ -0,0 +1,41 @@
+{
+ "@metadata": {
+ "authors": [
+ "Mormegil",
+ "Urbanecm",
+ "Dvorapa",
+ "Martin Urbanec",
+ "Matěj Suchánek"
+ ]
+ },
+ "oathauth-desc": "Poskytuje podporu pro autentizaci pomocí jednorázových hesel založených na HMAC",
+ "specialpages-group-oath": "Dvoufaktorová autentizace",
+ "oathauth-account": "Název účtu:",
+ "oathauth-secret": "Tajný klíč pro dvoufaktorovou autentizaci:",
+ "oathauth-enable": "Zapnutí dvoufaktorové autentizace",
+ "oathauth-scratchtokens": "Následující seznam obsahuje jednorázové provizorní kódy. Tyto kódy lze použít pouze jednou a slouží pro případ nouze. Opište si je a uchovávejte je na bezpečném místě. Pokud ztratíte svůj telefon, budou tyto kódy jediným způsobem, jak zachránit váš účet. '''Tyto kódy se již nikdy znovu nezobrazí'''.",
+ "oathauth-token": "Kód",
+ "oathauth-disable": "Vypnutí dvoufaktorové autentizace",
+ "oathauth-validatedoath": "Dvoufaktorové přihlášení ověřeno. Odteď bude vynucována dvoufaktorová autentizace.",
+ "oathauth-failedtovalidateoath": "Nepodařilo se ověřit dvoufaktorové přihlášení.",
+ "oathauth-disabledoath": "Dvoufaktorová autentizace vypnuta.",
+ "oathauth-prefs-label": "Dvoufaktorová autentizace:",
+ "oathauth-step1": "Krok 1: Stáhněte si program pro dvoufaktorovou autentizaci",
+ "oathauth-step1-test": "Stáhněte si program nebo mobilní aplikaci (jako je třeba Google Authenticator) pro dvoufaktorovou autentizaci",
+ "oathauth-step2": "Krok 2: Naskenujte QR kód",
+ "oathauth-step2alt": "Nebo tajemství zadejte ručně:",
+ "oathauth-step3": "Krok 3: Zapište si jednorázové kódy",
+ "oathauth-step4": "Krok 4: Ověření",
+ "oathauth-entertoken": "Pro ověření zadejte kód z vašeho autentizačního zařízení:",
+ "right-oathauth-enable": "Zapnutí dvoufaktorové autentizace",
+ "action-oathauth-enable": "zapnout dvoufaktorovou autentizaci",
+ "oathauth-auth-token-label": "Kód",
+ "oathauth-auth-token-help": "Jednorázové heslo používané jako druhý faktor dvoufaktorové autentizace.",
+ "oathauth-auth-ui": "Zadejte prosím ověřovací kód ze svého autentizačního zařízení",
+ "oathauth-throttled": "Příliš mnoho pokusů o ověření! Počkejte prosím $1.",
+ "oathauth-login-failed": "Ověření se nezdařilo.",
+ "oathauth-describe-provider": "Dvoufaktorová autentizace (OATH).",
+ "grant-group-authentication": "Provádění autentizačních akcí pro sebe či ostatní",
+ "grant-oath": "Přístup k informacím o dvoufaktorové autentizaci (OATH) u sebe a ostatních",
+ "right-oathauth-api-all": "Zjišťování a ověřování informací týkajících se OATH u sebe a ostatních"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/de-formal.json b/www/wiki/extensions/OATHAuth/i18n/de-formal.json
new file mode 100644
index 00000000..a42ea9eb
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/de-formal.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Kghbln"
+ ]
+ },
+ "oathauth-scratchtokens": "Die folgende Liste ist eine Liste einmalig verwendbarer Sondertoken. Diese Token können jeweils nur einmal verwendet werden und sind für Notfälle vorgesehen. Bitte schreiben Sie sie auf und verwahren Sie sie an einem sicheren Ort. Sofern Ihnen Ihr Mobiltelefon abhanden kommt, werden diese Token die einzige Möglichkeit sein, Ihr Konto zu retten. Diese Token werden Ihnen niemals wieder angezeigt werden."
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/de.json b/www/wiki/extensions/OATHAuth/i18n/de.json
new file mode 100644
index 00000000..0b37d951
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/de.json
@@ -0,0 +1,46 @@
+{
+ "@metadata": {
+ "authors": [
+ "Kghbln",
+ "Metalhead64"
+ ]
+ },
+ "oathauth-desc": "Ermöglicht die Authentifizierung mit HMAC-gestützten Einmalpasswörtern",
+ "specialpages-group-oath": "Zwei-Faktor-Authentifizierung",
+ "oathauth-account": "Benutzerkonto:",
+ "oathauth-secret": "Geheimer Schlüssel für die Zwei-Faktor-Authentifizierung:",
+ "oathauth-enable": "Die Zwei-Faktor-Authentifizierung aktivieren",
+ "oathauth-scratchtokens": "Die folgende Liste ist eine Liste einmalig verwendbarer Sondertoken. Diese Token können jeweils nur einmal verwendet werden und sind für Notfälle vorgesehen. Bitte schreibe sie auf und verwahre sie an einem sicheren Ort. Sofern dir dein Mobiltelefon abhanden kommt, werden diese Token die einzige Möglichkeit sein, dein Konto zu retten. '''Diese Token werden dir niemals wieder angezeigt werden'''.",
+ "oathauth-token": "Token",
+ "oathauth-disable": "Die Zwei-Faktor-Authentifizierung deaktivieren",
+ "oathauth-validatedoath": "Die Zwei-Faktor-Anmeldeinformationen wurden bestätigt. Die Zwei-Faktor-Authentifizierung wird jetzt durchgesetzt.",
+ "oathauth-noscratchforvalidation": "Du kannst keinen Scratchcode verwenden, um die Zwei-Faktor-Authentifikation zu bestätigen. Scratchcodes sind für Sicherungszwecke und die gelegentliche Nutzung vorgesehen. Bitte verwende einen Verifizierungscode von deinem Codegenerator.",
+ "oathauth-failedtovalidateoath": "Die Zwei-Faktor-Anmeldeinformationen konnten nicht bestätigt werden.",
+ "oathauth-disabledoath": "Die Zwei-Faktor-Authentifizierung wurde deaktiviert.",
+ "oathauth-prefs-label": "Zwei-Faktor-Authentifizierung:",
+ "oathauth-step1": "Schritt 1: Lade ein Zwei-Faktor-Authentifizierungsprogramm herunter",
+ "oathauth-step1-test": "Lade ein Programm zur Zwei-Faktor-Authentifizierung herunter. Das kann eine mobile Anwendung (wie Google Authenticator) oder eine Desktop-Anwendung sein.",
+ "oathauth-step2": "Schritt 2: Scanne den QR-Code",
+ "oathauth-step2alt": "Oder gib das Geheimnis manuell ein:",
+ "oathauth-step3": "Schritt 3: Schreibe die Sondercodes auf",
+ "oathauth-step4": "Schritt 4: Verifizierung",
+ "oathauth-entertoken": "Gib zur Verifizierung einen Code von deinem Authentifizierungsgerät ein:",
+ "right-oathauth-enable": "Die Zwei-Faktor-Authentifizierung aktivieren",
+ "action-oathauth-enable": "die Zwei-Faktor-Authentifizierung zu aktivieren",
+ "oathauth-auth-token-label": "Token",
+ "oathauth-auth-token-help": "Das Einmalpasswort, verwendet als zweiter Faktor der Zwei-Faktor-Authentifizierung.",
+ "oathauth-auth-ui": "Bitte einen Verifizierungscode vom Authentifizierungsgerät eingeben",
+ "oathauth-throttled": "Zu viele Verifizierungsversuche! Bitte warte $1.",
+ "oathauth-login-failed": "Verifizierung fehlgeschlagen.",
+ "oathauth-describe-provider": "Zwei-Faktor-Authentifizierung (OATH).",
+ "grant-group-authentication": "Authentifizierungs-Aktionen für sich selbst und andere ausführen",
+ "grant-oath": "Auf Zwei-Faktor-Authentifizierungs-Informationen für sich selbst und andere zugreifen",
+ "right-oathauth-api-all": "OATH-Informationen für sich selbst und andere abfragen und validieren",
+ "action-oathauth-api-all": "den OATH-Status zu überprüfen",
+ "apihelp-query+oath-example-1": "Ruft Informationen über den aktuellen Benutzer ab",
+ "apihelp-oathvalidate-description": "Validiert einen Token der Zwei-Faktor-Authentifizierung (OATH).",
+ "apihelp-oathvalidate-param-user": "Benutzer, für den der Token validiert werden soll. Standard ist der aktuelle Benutzer.",
+ "apihelp-oathvalidate-param-totp": "Token der Zwei-Faktor-Authentifizierung (OATH).",
+ "apihelp-oathvalidate-example-1": "Validiert einen Token für den aktuellen Benutzer",
+ "apihelp-oathvalidate-example-2": "Validiert einen Token für den Benutzer <kbd>Beispiel</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/diq.json b/www/wiki/extensions/OATHAuth/i18n/diq.json
new file mode 100644
index 00000000..11543606
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/diq.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Marmase",
+ "Mirzali",
+ "Kumkumuk"
+ ]
+ },
+ "oathauth-token": "Mare"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/dsb.json b/www/wiki/extensions/OATHAuth/i18n/dsb.json
new file mode 100644
index 00000000..fdd6f11d
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/dsb.json
@@ -0,0 +1,20 @@
+{
+ "@metadata": {
+ "authors": [
+ "Derbeth",
+ "Michawiki"
+ ]
+ },
+ "oathauth-desc": "Zmóžnja awtentifkaciju z pomocu raz wužytych gronidłow na zakłaźe HMAC",
+ "specialpages-group-oath": "Dwójofaktorowa awtentifikacija",
+ "oathauth-account": "Kontowe mě dwójofaktoroweje awtentifikacije:",
+ "oathauth-secret": "Dwójofaktorowy pótajmny kluc:",
+ "oathauth-enable": "Dwójofaktorowu awtentifikaciju zmóžniś",
+ "oathauth-scratchtokens": "Slědujuca lisćina jo lisćina raz wužywajobnych specialnych tokenow. Toś te tokeny daju se jano jaden raz wužywaś a su za nuzne pady. Pšosym napiš je a zachowaj je na wěstem městnje. Jolic zgubijoš swój mobilny telefon, budu toś te tokeny jadnučka móžnosć, aby swójo konto wuchował. Toś te tokeny njepokažu śi žednje zas.",
+ "oathauth-token": "Token",
+ "oathauth-disable": "Dwójofaktorowu awtentifikaciju znjemóžniś",
+ "oathauth-validatedoath": "Dwójofaktorowe pśizjawjeńske informacije su se wobkšuśili. Dwójofaktorowa awtentifikacija buźo se něnto pśesajźiś.",
+ "oathauth-failedtovalidateoath": "Dwójofaktorowe pśizjawjeńske informacije njejsu dali se wobkšuśiś",
+ "oathauth-disabledoath": "Dwójofaktorowa awtentifikacija jo se znjemóžniła.",
+ "oathauth-prefs-label": "Dwójofaktorowa awtentifikacija:"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/el.json b/www/wiki/extensions/OATHAuth/i18n/el.json
new file mode 100644
index 00000000..a48af8a0
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/el.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "Geraki",
+ "Protnet",
+ "Nikosgranturismogt"
+ ]
+ },
+ "oathauth-account": "Όνομα λογαριασμού:",
+ "oathauth-secret": "Μυστικό κλειδί ταυτοποίησης δύο παραγόντων:",
+ "oathauth-disable": "Απενεργοποίηση ελέγχου ταυτότητας δύο παραγόντων",
+ "grant-oath": "Πρόσβαση σε πληροφορίες ταυτοποίησης δύο παραγόντων (OATH) για εαυτόν και άλλους"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/en.json b/www/wiki/extensions/OATHAuth/i18n/en.json
new file mode 100644
index 00000000..09a1e701
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/en.json
@@ -0,0 +1,52 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ryan Lane <rlane@wikimedia.org>",
+ "Tyler Romeo <tylerromeo@gmail.com>"
+ ]
+ },
+ "oathauth-desc": "Provides authentication support using HMAC based one-time passwords",
+ "oath": "OATHAuth",
+ "specialpages-group-oath": "Two-factor authentication",
+ "oathauth-account": "Account name:",
+ "oathauth-secret": "Two-factor authentication secret key:",
+ "oathauth-enable": "Enable two-factor authentication",
+ "oathauth-scratchtokens": "The following list is a list of one-time use scratch tokens. These tokens can only be used once, and are for emergency use. Please write these down and keep them in a secure location. If you lose your phone, these tokens are the only way to rescue your account. '''These tokens will never be shown again'''.",
+ "oathauth-token": "Token",
+ "oathauth-disable": "Disable two-factor authentication",
+ "oathauth-validatedoath": "Validated two-factor credentials. Two-factor authentication will now be enforced.",
+ "oathauth-noscratchforvalidation": "You cannot use a scratch code to confirm two-factor authentication. Scratch codes are for backup and incidental use only. Please use a verification code from your code generator.",
+ "oathauth-failedtovalidateoath": "Failed to validate two-factor credentials",
+ "oathauth-disabledoath": "Disabled two-factor authentication.",
+ "oathauth-prefs-label": "Two-factor authentication:",
+ "oathauth-step1": "Step 1: Download a two-factor authentication program",
+ "oathauth-step1-test": "Download a program for two-factor authentication. That can be a mobile application (such as Google Authenticator) or a desktop application",
+ "oathauth-step2": "Step 2: Scan the QR code",
+ "oathauth-step2alt": "Or enter the secret manually:",
+ "oathauth-step3": "Step 3: Write down the scratch codes",
+ "oathauth-step4": "Step 4: Verification",
+ "oathauth-entertoken": "Enter a code from your authentication device to verify:",
+ "right-oathauth-enable": "Enable two-factor authentication",
+ "action-oathauth-enable": "enable two-factor authentication",
+ "oathauth-auth-token-label": "Token",
+ "oathauth-auth-token-help": "The one-time password used as the second factor of two-factor authentication.",
+ "oathauth-auth-ui": "Please enter a verification code from your authentication device",
+ "oathauth-throttled": "Too many verification attempts! Please wait $1.",
+ "oathauth-login-failed": "Verification failed.",
+ "oathauth-describe-provider": "Two-factor authentication (OATH).",
+ "grant-group-authentication": "Perform authentication actions for self and others",
+ "grant-oath": "Access two-factor authentication (OATH) information for self and others",
+ "right-oathauth-api-all": "Query and validate OATH information for self and others",
+ "action-oathauth-api-all": "check OATH status",
+ "apihelp-query+oath-description": "Check to see if two-factor authentication (OATH) is enabled for a user.",
+ "apihelp-query+oath-summary": "Check to see if two-factor authentication (OATH) is enabled for a user.",
+ "apihelp-query+oath-param-user": "User to get information about. Defaults to the current user.",
+ "apihelp-query+oath-example-1": "Get information about the current user",
+ "apihelp-query+oath-example-2": "Get information about user <kbd>Example</kbd>",
+ "apihelp-oathvalidate-description": "Validate a two-factor authentication (OATH) token.",
+ "apihelp-oathvalidate-summary": "Validate a two-factor authentication (OATH) token.",
+ "apihelp-oathvalidate-param-user": "User to validate token for. Defaults to the current user.",
+ "apihelp-oathvalidate-param-totp": "Two-factor authentication (OATH) token.",
+ "apihelp-oathvalidate-example-1": "Validate a token for the current user",
+ "apihelp-oathvalidate-example-2": "Validate a token for the user <kbd>Example</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/es.json b/www/wiki/extensions/OATHAuth/i18n/es.json
new file mode 100644
index 00000000..073c5356
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/es.json
@@ -0,0 +1,45 @@
+{
+ "@metadata": {
+ "authors": [
+ "Armando-Martin",
+ "Fitoschido",
+ "McDutchie",
+ "Matiia",
+ "Macofe",
+ "Dgstranz"
+ ]
+ },
+ "oathauth-desc": "Proporciona compatibilidad con la autenticación mediante contraseñas en HMAC de un solo uso",
+ "specialpages-group-oath": "Autenticación de dos factores",
+ "oathauth-account": "Nombre de la cuenta:",
+ "oathauth-secret": "Clave secreta de autenticación en dos pasos:",
+ "oathauth-enable": "Activar la autenticación en dos pasos",
+ "oathauth-scratchtokens": "La lista siguiente es una lista de claves de un solo uso. Estas claves solo pueden utilizarse una vez y son para uso de emergencia. Por favor, anótalas y mantenlas en un lugar seguro. Si pierdes el teléfono, estas claves son la única manera de recuperar tu cuenta. Estas claves nunca se mostrarán una segunda vez.",
+ "oathauth-token": "Clave",
+ "oathauth-disable": "Deshabilitar la autenticación de dos factores",
+ "oathauth-validatedoath": "Se han validado las credenciales de dos factores. Ahora se aplicará la autenticación de dos factores.",
+ "oathauth-noscratchforvalidation": "No puedes utilizar códigos predefinidos para confirmar la autenticación de dos pasos. Tales códigos solo sirven como respaldo y para uso ocasional accesorio. Utiliza un código de verificación proveniente de un generador de códigos.",
+ "oathauth-failedtovalidateoath": "Error al validar las credenciales de dos factores",
+ "oathauth-disabledoath": "Se ha deshabilitado la autenticación de dos factores.",
+ "oathauth-prefs-label": "Autenticación en dos etapas:",
+ "oathauth-step1": "Paso 1: descarga un programa de autenticación en dos pasos",
+ "oathauth-step1-test": "Descarga un programa para la autentificación de dos pasos. Puede tratarse de una aplicación móvil (como Google Authenticator) o bien de una aplicación de escritorio",
+ "oathauth-step2": "Paso 2: escanea el código QR",
+ "oathauth-step2alt": "O escribe el secreto manualmente:",
+ "oathauth-step3": "Paso 3: escribe los códigos",
+ "oathauth-step4": "Paso 4: verificación",
+ "oathauth-entertoken": "Escribe un código de tu dispositivo de autentificación para verificar:",
+ "right-oathauth-enable": "Activar la autenticación en dos pasos",
+ "action-oathauth-enable": "activar la autenticación de dos pasos",
+ "oathauth-auth-ui": "Escribe el código de verificación de tu dispositivo de autenticación",
+ "oathauth-login-failed": "Falló la verificación.",
+ "oathauth-describe-provider": "Autenticación de dos factores (OATH).",
+ "grant-group-authentication": "Realizar acciones de autentificación para uno mismo y para otros",
+ "grant-oath": "Acceder a la autentificación de dos factores (OATH) para uno mismo y para otros",
+ "right-oathauth-api-all": "Consultar y validar la inofrmación OATH para uno mismo y para otros",
+ "action-oathauth-api-all": "verificar el estado de OATH",
+ "apihelp-query+oath-description": "Comprobar si la autentificación de dos factores (OATH) está habilitada para un usuario.",
+ "apihelp-query+oath-param-user": "Usuario del que obtener información. Por defecto, se trata del usuario actual.",
+ "apihelp-query+oath-example-1": "Obtener información sobre el usuario actual",
+ "apihelp-query+oath-example-2": "Obtener información sobre el usuario <kbd>Example</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/et.json b/www/wiki/extensions/OATHAuth/i18n/et.json
new file mode 100644
index 00000000..cacea021
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/et.json
@@ -0,0 +1,25 @@
+{
+ "@metadata": {
+ "authors": [
+ "Avjoska",
+ "Cumbril",
+ "Pikne"
+ ]
+ },
+ "oathauth-account": "Konto nimi:",
+ "oathauth-secret": "Kaheastmelise autentimise salavõti:",
+ "oathauth-enable": "Luba kaheastmeline autentimine",
+ "oathauth-scratchtokens": "All on ühekordsete koodide nimekiri. Igat koodi saab kasutada vaid ühel korral, koodid on mõeldud hädaolukorra jaoks. Kirjuta koodid üles ja pane turvalisse kohta hoiule. Kui peaksid telefoni kaotama, on need koodid ainus võimalus oma kontot päästa. '''Allolevaid koode rohkem ei kuvata'''.",
+ "oathauth-prefs-label": "Kaheastmeline autentimine:",
+ "oathauth-step1": "Samm 1: laadi alla kaheastmelise autentimise rakendus",
+ "oathauth-step1-test": "Laadi alla kaheastmelise autentimise programm. See võib olla mobiilirakendus (näiteks Google Authenticator) või lauaarvuti rakendus.",
+ "oathauth-step2": "Samm 2: skaneeri QR-kood",
+ "oathauth-step2alt": "Või sisesta salavõti käsitsi.",
+ "oathauth-step3": "Samm 3: kirjuta üles ühekordsed koodid",
+ "oathauth-step4": "Samm 4: kinnitamine",
+ "oathauth-entertoken": "Kinnitamiseks sisesta autentimisseadmest pärit kood:",
+ "right-oathauth-enable": "Lubada kaheastmelist autentimist",
+ "action-oathauth-enable": "kaheastmelist autentimist lubada",
+ "grant-oath": "Juurdepääs enda ja teiste kaheastmelise autentimise (OATH) andmetele",
+ "right-oathauth-api-all": "Pärida ja valideerida enda ja teiste OATH-i teavet"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/fa.json b/www/wiki/extensions/OATHAuth/i18n/fa.json
new file mode 100644
index 00000000..e52d8986
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/fa.json
@@ -0,0 +1,51 @@
+{
+ "@metadata": {
+ "authors": [
+ "Armin1392",
+ "Ebraminio",
+ "Alirezaaa",
+ "Huji",
+ "Reza1615",
+ "Ladsgroup"
+ ]
+ },
+ "oathauth-desc": "پشتیبانی از اعتبارسنجی با استفاده از گذرواژه‌های یکبار مصرف مبتنی بر اچ‌ام‌ای‌سی",
+ "specialpages-group-oath": "اعتبارسنجی دومرحله‌ای",
+ "oathauth-account": "نام حساب:",
+ "oathauth-secret": "کلید مخفی ورود دومرحله‌ای:",
+ "oathauth-enable": "فعال کردن اعتبارسنجی دومرحله‌ای",
+ "oathauth-scratchtokens": "فهرست زیر، فهرست یک‌بار مصرف نشانه‌های مخفی است. این نشانه‌ها می‌تواند فقط یک بار مورد استفاده قرار گیرند، و برای ضرورت استفاده می‌شوند. لطفاً این‌ها را برروی کاغذ یا جایی بنویسید و آن‌ها را در محل امنی نگهدارید. اگر گوشی خود رو گم کردید، این نشانه‌ها تنها راه نجات حساب شما هستند. این نشانه‌ها هرگز دوباره نشان داده نخواهند شد.",
+ "oathauth-token": "بلیط",
+ "oathauth-disable": "غیرفعال کردن اعتبارسنجی دومرحله‌ای",
+ "oathauth-validatedoath": "اعتبارنامه‌های دو مرحله‌ای تأیید شدند. اعتبارسنجی دو مرحله‌ای از این پس اعمال خواهد شد.",
+ "oathauth-failedtovalidateoath": "تأیید اعتبارنامه‌های ورود دومرحله‌ای شکست خورد",
+ "oathauth-disabledoath": "غیرفعال کردن اعتبارسنجی دومرحله‌ای.",
+ "oathauth-prefs-label": "اعتبارسنجی دومرحله‌ای:",
+ "oathauth-step1": "قدم ۱: یک برنامه ورود دومرحله‌ای را بارگیری کنید",
+ "oathauth-step1-test": "برنامه‌ای (نظیر Google Authenticator) برای اعتبارسنجی دومرحله‌ای روی دستگاه تلفن همراه خود بارگیری کنید",
+ "oathauth-step2": "قدم ۲: رمزینهٔ پاسخ سریع را پویش کنید",
+ "oathauth-step2alt": "یا رمز را دستی وارد کنید:",
+ "oathauth-step3": "قدم ۳: کدهای دورانداختنی را یادداشت کنید",
+ "oathauth-step4": "قدم ۴: تأیید",
+ "oathauth-entertoken": "کدی که در برنامهٔ روی دستگاه‌تان نمایش داده شده وارد کنید تا تأیید شوید:",
+ "right-oathauth-enable": "فعال‌سازی اعتبارسنجی دومرحله‌ای",
+ "action-oathauth-enable": "فعال‌سازی اعتبارسنجی دومرحله‌ای",
+ "oathauth-auth-token-label": "بلیط",
+ "oathauth-auth-token-help": "گذرواژهٔ یکبار مصرف قابل استفاده به عنوان عامل دوم در اعتبارسنجی دومرحله‌ای",
+ "oathauth-auth-ui": "لطفاً کد تأیید را از برنامهٔ روی دستگاه همراه خود وارد کنید",
+ "oathauth-throttled": "تعداد تلاش‌ها برای تأیید بیش از حد مجاز بود! لطفاً $1 صبر کنید.",
+ "oathauth-login-failed": "تأیید شکست خورد.",
+ "oathauth-describe-provider": "اعتبارسنجی دومرحله‌ای (OATH).",
+ "grant-group-authentication": "انجام اعمال اعتبارسنجی برای خودتان و دیگران",
+ "grant-oath": "دسترسی به اطلاعات اعتبارسنجی دومرحله‌ای (OATH) برای خودتان و دیگران",
+ "right-oathauth-api-all": "پرسمان و تأیید اطلاعات OATH برای خودتان و دیگران",
+ "apihelp-query+oath-description": "بررسی این که آیا اعتبارسنجی دومرحله‌ای (OATH) برای یک کاربر فعال شده‌است.",
+ "apihelp-query+oath-param-user": "کاربری که در موردش اطلاعات می‌گیرید. پیش‌فرض، حساب کاربری جاری است.",
+ "apihelp-query+oath-example-1": "گرفتن اطلاعات در مورد کاربر جاری",
+ "apihelp-query+oath-example-2": "گرفتن اطلاعات در مورد کاربر <kbd>Example</kbd>",
+ "apihelp-oathvalidate-description": "تأیید یک بلیط اعتبارسنجی دومرحله‌ای (OATH)",
+ "apihelp-oathvalidate-param-user": "کاربری که بلیط برایش تأیید می‌شود. پیش‌فرض، حساب کاربری جاری است.",
+ "apihelp-oathvalidate-param-totp": "بلیط اعتبارسنجی دومرحله‌ای (OATH).",
+ "apihelp-oathvalidate-example-1": "تأیید یک بلیط برای کاربر جاری",
+ "apihelp-oathvalidate-example-2": "تأیید یک بلیط برای کاربر <kbd>Example</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/fi.json b/www/wiki/extensions/OATHAuth/i18n/fi.json
new file mode 100644
index 00000000..925e5d18
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/fi.json
@@ -0,0 +1,26 @@
+{
+ "@metadata": {
+ "authors": [
+ "Mikahama",
+ "Stryn",
+ "Pxos",
+ "Nike"
+ ]
+ },
+ "specialpages-group-oath": "Kaksivaiheinen tunnistautuminen",
+ "oathauth-account": "Tilin nimi:",
+ "oathauth-secret": "Kaksivaiheisen tunnistautumisen salausavain:",
+ "oathauth-enable": "Ota käyttöön kaksivaiheinen tunnistautuminen",
+ "oathauth-token": "Avain",
+ "oathauth-disable": "Poista käytöstä kaksivaiheinen tunnistautuminen",
+ "oathauth-prefs-label": "Kaksivaiheinen tunnistautuminen:",
+ "oathauth-step1": "Vaihe 1: Lataa kaksivaiheisen tunnistautumisen sovellus",
+ "oathauth-step2": "Vaihe 2: Skannaa QR-koodi",
+ "oathauth-step2alt": "Tai syötä salainen tunnus käsin:",
+ "oathauth-step4": "Vaihe 4: Vahvistus",
+ "right-oathauth-enable": "Ottaa käyttöön kaksivaiheinen tunnistautuminen",
+ "action-oathauth-enable": "ottaa käyttöön kaksivaiheista tunnistautumista",
+ "oathauth-auth-token-label": "Avain",
+ "oathauth-auth-ui": "Syötä vahvistuskoodi tunnistautumislaitteestasi",
+ "action-oathauth-api-all": "tarkistaa OATH-tilaa"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/fr.json b/www/wiki/extensions/OATHAuth/i18n/fr.json
new file mode 100644
index 00000000..90c8ceb4
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/fr.json
@@ -0,0 +1,62 @@
+{
+ "@metadata": {
+ "authors": [
+ "Crochet.david",
+ "Gomoko",
+ "Peter17",
+ "Sherbrooke",
+ "Manaviko",
+ "Orlodrim",
+ "Verdy p",
+ "Wladek92",
+ "Yasten",
+ "McDutchie",
+ "Trial",
+ "Urhixidur",
+ "Thibaut120094"
+ ]
+ },
+ "oathauth-desc": "Prend en charge l’authentification utilisant des mots de passe HMAC à utilisation unique.",
+ "specialpages-group-oath": "Authentification à deux facteurs",
+ "oathauth-account": "Nom du compte :",
+ "oathauth-secret": "Clé secrète d’authentification à deux facteurs :",
+ "oathauth-enable": "Activer l'authentification à deux facteurs",
+ "oathauth-scratchtokens": "La liste suivante est une liste de jetons à gratter à utilisation unique. Ces jetons ne peuvent être utilisés qu'une seule fois, et servent en cas d'urgence. Veuillez les écrire et les conserver dans un endroit sûr. Si vous perdez votre téléphone, ces jetons sont le seul moyen de récupérer votre compte. '''Ces jetons ne seront jamais affichés de nouveau'''.",
+ "oathauth-token": "Jeton",
+ "oathauth-disable": "Désactiver l'authentification à deux facteurs",
+ "oathauth-validatedoath": "Identifications à deux facteurs validées. L'authentification à deux facteurs sera désormais appliquée.",
+ "oathauth-noscratchforvalidation": "Vous ne pouvez pas utiliser un code prédéfini pour confirmer une authentification à deux facteurs. Les codes prédéfinis sont utilisés uniquement pour la sauvegarde et les incidents. Veuillez utiliser un code de vérification de votre générateur de code.",
+ "oathauth-failedtovalidateoath": "Échec de validation des identifications à deux facteurs",
+ "oathauth-disabledoath": "Authentification à deux facteurs désactivée.",
+ "oathauth-prefs-label": "Authentification à deux facteurs :",
+ "oathauth-step1": "Étape 1 : téléchargez un programme d'authentification à deux facteurs",
+ "oathauth-step1-test": "Télécharger un programme pour l’authentification à deux facteurs. Il peut s'agir d'une application mobile (comme Google Authenticator) ou une application de bureau",
+ "oathauth-step2": "Étape 2 : scannez le code QR",
+ "oathauth-step2alt": "Ou entrez le code secret manuellement",
+ "oathauth-step3": "Étape 3 : inscrivez les codes à gratter",
+ "oathauth-step4": "Étape 4 : vérification",
+ "oathauth-entertoken": "Entrez un code à partir de votre équipement d'identification pour vérifier:",
+ "right-oathauth-enable": "Activer l’authentification à deux facteurs",
+ "action-oathauth-enable": "activer l’authentification à deux facteurs",
+ "oathauth-auth-token-label": "Jeton",
+ "oathauth-auth-token-help": "Le mot de passe à usage unique utilisé comme le deuxième facteur d'authentification à deux facteurs.",
+ "oathauth-auth-ui": "Veuillez entrer le code de vérification à partir de votre appareil d’authentification",
+ "oathauth-throttled": "Trop de tentatives de vérification ! Veuillez attendre $1.",
+ "oathauth-login-failed": "Échec de la vérification",
+ "oathauth-describe-provider": "Authentification à deux facteurs (OATH)",
+ "grant-group-authentication": "Effectuer les actions d’authentification pour soi-même et les autres",
+ "grant-oath": "Accéder aux informations (OAuth) d’authentification à deux facteurs pour soi-même et les autres",
+ "right-oathauth-api-all": "Demander et valider les informations OATH pour soi-même et les autres",
+ "action-oathauth-api-all": "vérifier l'état OATH",
+ "apihelp-query+oath-description": "Vérifier si l’authentification à deux facteurs (OATH) est activée pour un utilisateur.",
+ "apihelp-query+oath-summary": "Vérifie si l'authentification à deux facteurs (OATH) est activée chez un utilisateur",
+ "apihelp-query+oath-param-user": "Utilisateur au sujet duquel obtenir des informations. Par défaut, l’utilisateur actuel.",
+ "apihelp-query+oath-example-1": "Obtenir des informations sur l’utilisateur actuel",
+ "apihelp-query+oath-example-2": "Obtenir des informations sur l’utilisateur <kbd>Example</kbd>",
+ "apihelp-oathvalidate-description": "Valider un jeton d’authentification à deux facteurs (OATH).",
+ "apihelp-oathvalidate-summary": "Valider un jeton d’authentification à deux facteurs (OATH).",
+ "apihelp-oathvalidate-param-user": "Utilisateur pour lequel valider le jeton. Par défaut, l’utilisateur actuel.",
+ "apihelp-oathvalidate-param-totp": "Jeton d’authentification à deux facteurs (OATH).",
+ "apihelp-oathvalidate-example-1": "Valider un jeton pour l’utilisateur actuel",
+ "apihelp-oathvalidate-example-2": "Valider un jeton pour l’utilisateur <kbd>Example</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/frp.json b/www/wiki/extensions/OATHAuth/i18n/frp.json
new file mode 100644
index 00000000..9ee826f2
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/frp.json
@@ -0,0 +1,16 @@
+{
+ "@metadata": {
+ "authors": [
+ "ChrisPtDe"
+ ]
+ },
+ "oathauth-desc": "Balye n’assistance d’ôtentificacion empleyent HMAC basâ sur des contresegnos a usâjo solèt",
+ "specialpages-group-oath": "Ôtentificacion a doux factors",
+ "oathauth-account": "Nom du compto a doux factors :",
+ "oathauth-secret": "Cllâf secrèta a doux factors :",
+ "oathauth-enable": "Activar l’ôtentificacion a doux factors",
+ "oathauth-token": "Jeton",
+ "oathauth-disable": "Dèsactivar l’ôtentificacion a doux factors",
+ "oathauth-failedtovalidateoath": "Falyita de la validacion de les refèrences a doux factors",
+ "oathauth-disabledoath": "Ôtentificacion a doux factors dèsactivâye."
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/gl.json b/www/wiki/extensions/OATHAuth/i18n/gl.json
new file mode 100644
index 00000000..8c5d11cc
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/gl.json
@@ -0,0 +1,52 @@
+{
+ "@metadata": {
+ "authors": [
+ "Toliño",
+ "Elisardojm",
+ "Banjo"
+ ]
+ },
+ "oathauth-desc": "Proporciona un soporte de autenticación mediante HMAC baseado en contrasinais dunha soa vez",
+ "specialpages-group-oath": "Autenticación de dous factores",
+ "oathauth-account": "Nome da conta:",
+ "oathauth-secret": "Clave secreta de autenticación de dous factoresː",
+ "oathauth-enable": "Activar a autenticación de dous factores",
+ "oathauth-scratchtokens": "A seguinte é unha lista de pases dun só uso. Estes pases unicamente se poden empregar unha vez, e son para casos de emerxencia. Escríbaos nun papel e gárdeos nun lugar seguro. Se perde o seu teléfono, estes pases son o único xeito de recuperar a súa conta. Esta é a única vez que poderá ver os pases.",
+ "oathauth-token": "Pase",
+ "oathauth-disable": "Desactivar a autenticación de dous factores",
+ "oathauth-validatedoath": "Validáronse as credenciais de dous factores. Agora hase aplicar a autenticación de dous factores.",
+ "oathauth-noscratchforvalidation": "Non pode utilizar códigos predefinidos para confirmar a autenticación de dous pasos. Tales códigos só sirven como respaldo e para uso ocasional accesorio. Utilice un código de verificación provinte do seu xerador de códigos.",
+ "oathauth-failedtovalidateoath": "Erro ao validar as credenciais de dous factores",
+ "oathauth-disabledoath": "Desactivouse a autenticación de dous factores.",
+ "oathauth-prefs-label": "Autenticación de dous factores:",
+ "oathauth-step1": "Paso 1: descargue a un programa de autenticación en dous pasos",
+ "oathauth-step1-test": "Descargue un programa para a autentificación en dous pasos. Pode tratarse dunha aplicación móbil (como Google Authenticator) ou ben dunha aplicación de escritorio",
+ "oathauth-step2": "Paso 2: escanee o código QR",
+ "oathauth-step2alt": "Ou escriba o código secreto manualmente:",
+ "oathauth-step3": "Paso 3: escriba os códigos",
+ "oathauth-step4": "Paso 4: verificación",
+ "oathauth-entertoken": "Escriba un código a partir do seu dispositivo de autenticación para verificar:",
+ "right-oathauth-enable": "Activar a autenticación en dous pasos",
+ "action-oathauth-enable": "activar a autenticación de dous pasos",
+ "oathauth-auth-token-label": "Identificador",
+ "oathauth-auth-token-help": "O contrasinal único empregado como segundo factor da autenticación de dous pasos.",
+ "oathauth-auth-ui": "Por favor, introduzca o código de verificación do seu dispositivo de autenticación",
+ "oathauth-throttled": "Demasiados intentos de verificaciónǃ Por favor, espere $1.",
+ "oathauth-login-failed": "Fallou a verificación",
+ "oathauth-describe-provider": "Autenticación de dous factores (OATH).",
+ "grant-group-authentication": "Relizar as accións de autenticación para vostede e para outros",
+ "grant-oath": "Acceder á información (OAuth) de autenticación en dous factores para vostede e para outros",
+ "right-oathauth-api-all": "Consultar e validar a información OATH para vostede e para outros",
+ "action-oathauth-api-all": "verificar o estado de OATH",
+ "apihelp-query+oath-description": "Comprobar se a autenticación en dous factores (OAuth) está activa para un usuario.",
+ "apihelp-query+oath-summary": "Verificar se a autenticación en dous factores (OATH) está activa para un usuario.",
+ "apihelp-query+oath-param-user": "Usuario do que obter a información. Por defecto é o usuario actual.",
+ "apihelp-query+oath-example-1": "Obter información sobre o usuario actual",
+ "apihelp-query+oath-example-2": "Obter información sobre o usuario <kbd>Exemplo</kbd>",
+ "apihelp-oathvalidate-description": "Validar un pase de autenticación en dous factores (OATH)",
+ "apihelp-oathvalidate-summary": "Validar un pase de autenticación en dous factores (OATH)",
+ "apihelp-oathvalidate-param-user": "Usuario ó que validar o pase. Por defecto, o usuario actual.",
+ "apihelp-oathvalidate-param-totp": "Pase de autenticación en dos factores (OATH)",
+ "apihelp-oathvalidate-example-1": "Validar un pase para o usuario actual",
+ "apihelp-oathvalidate-example-2": "Validar un pase para o usuario <kbd>Exemplo</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/he.json b/www/wiki/extensions/OATHAuth/i18n/he.json
new file mode 100644
index 00000000..7892362d
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/he.json
@@ -0,0 +1,52 @@
+{
+ "@metadata": {
+ "authors": [
+ "Amire80",
+ "Guycn2",
+ "Matanya"
+ ]
+ },
+ "oathauth-desc": "תמיכה באימות באמצעות סיסמאות חד־פעמיות מבוססות HMAC",
+ "specialpages-group-oath": "אימות דו־שלבי",
+ "oathauth-account": "שם החשבון:",
+ "oathauth-secret": "המפתח הסודי של האימות הדו־שלבי:",
+ "oathauth-enable": "הפעלת אימות דו־שלבי",
+ "oathauth-scratchtokens": "להלן רשימה של אסימוני גירוד זמניים. אפשר להשתמש באסימונים האלה רק פעם אחת במצב חירום. נא לרשום אותם ולשמור אותם במקום בטוח. אם הטלפון שלך יאבד, האסימונים האלה יהיו דרכך היחידה להציל את החשבון שלך. '''האסימונים האלה לא יוצגו שוב לעולם.'''",
+ "oathauth-token": "אסימון",
+ "oathauth-disable": "כיבוי אימות דו־שלבי",
+ "oathauth-validatedoath": "תקינותם של נתוני האימות הדו־שלבי נבדקה. מעכשיו ייכפה אימות דו־שלבי.",
+ "oathauth-noscratchforvalidation": "לא ניתן להשתמש בקוד גירוד כדי לאשר אימות דו־שלבי. קודי גירוד מיועדים רק לגיבוי ולשימוש אקראי. נא להשתמש בקוד אימות ממחולל הקודים שלך.",
+ "oathauth-failedtovalidateoath": "בדיקת התקינות של נתוני האימות הדו־שלבי נכשלה.",
+ "oathauth-disabledoath": "האימות הדו־שלבי כובה.",
+ "oathauth-prefs-label": "אימות דו־שלבי:",
+ "oathauth-step1": "צעד 1: הורדת תכנית לאימות דו־שלבי",
+ "oathauth-step1-test": "יש להוריד תכנית לאימות דו־שלבי. זה יכול להיות יישום נייד (כגון המאמת של גוגל) או יישום שולחני",
+ "oathauth-step2": "צעד 2: סריקת קוד ה־QR",
+ "oathauth-step2alt": "אפשר גם להזין את המפתח הסודי באופן ידני:",
+ "oathauth-step3": "צעד 3: רישום קודי גירוד",
+ "oathauth-step4": "צעד 4: אישור",
+ "oathauth-entertoken": "נא להזין את הקוד מהמכשיר המאמת שלך לאישור:",
+ "right-oathauth-enable": "הפעלת אימות דו־שלבי",
+ "action-oathauth-enable": "להפעיל אימות דו־שלבי",
+ "oathauth-auth-token-label": "אסימון",
+ "oathauth-auth-token-help": "הסיסמה החד־פעמית שמשמשת בתור הגורם השני של האימות הדו־שלבי.",
+ "oathauth-auth-ui": "נא להזין את קוד האישור ממכשיר האימות שלך",
+ "oathauth-throttled": "יותר מדי ניסיונות אימות! נא לנסות $1.",
+ "oathauth-login-failed": "האימות נכשל.",
+ "oathauth-describe-provider": "אימות דו־שלבי (OATH).",
+ "grant-group-authentication": "ביצוע פעולות זיהוי עבור עצמך ועבור אחרים",
+ "grant-oath": "גישה למידע של אימות דו־שלבי (OATH) על עצמך ועל אחרים",
+ "right-oathauth-api-all": "שליחת שאילתה לקבלת מידע על OATH ובדיקת תקינותו עבור עצמך ועבור אחרים",
+ "action-oathauth-api-all": "בדיקת מצב OATH",
+ "apihelp-query+oath-description": "בדיקה האם האימות הדו־שלבי (OATH) מופעל עבור המשתמש הזה.",
+ "apihelp-query+oath-summary": "בדיקה האם אימות דו־שלבי (OATH) מופעל עבור משתמש.",
+ "apihelp-query+oath-param-user": "על איזה משתמש לקבל מידע. בררת המחדל היא המשתמש הנוכחי.",
+ "apihelp-query+oath-example-1": "קבלת מידע על המשתמש הנוכחי",
+ "apihelp-query+oath-example-2": "קבלת מידע על משתמש <kbd>Example</kbd>",
+ "apihelp-oathvalidate-description": "בדיקת תקינות של אסימון אימות דו־שלבי (OATH).",
+ "apihelp-oathvalidate-summary": "בדיקת תקינות של אסימון אימות דו־שלבי (OATH).",
+ "apihelp-oathvalidate-param-user": "לאיזה משתמש לאמת את האסימון. בררת המחדל היא המשתמש הנוכחי.",
+ "apihelp-oathvalidate-param-totp": "אסימון אימות דו־שלבי (OATH).",
+ "apihelp-oathvalidate-example-1": "בדיקת תקינות של אסימון עבור המשתמש הנוכחי.",
+ "apihelp-oathvalidate-example-2": "בדיקת אסימון עבור המשתמש <kbd>Example</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/hi.json b/www/wiki/extensions/OATHAuth/i18n/hi.json
new file mode 100644
index 00000000..d63c9c7f
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/hi.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ziyaurr",
+ "Smtchahal"
+ ]
+ },
+ "oathauth-scratchtokens": "निम्नलिखित एक-बार उपयोग किये जाने वाले स्क्रैच टोकनो की सूची है। ये टोकन केवल एक बार उपयोग किये जा सकते हैं, तथा ये आपातकालीन उपयोग के लिये हैं। कृपया इन्हें लिखकर किसी सुरक्षित जगह रखें। अपना फ़ोन खो देने पर ये टोकन आपके खाते को बचाने का एकमात्र ज़रिया हैं। '''ये टोकन दोबारा कभी नहीं दिखाए जाएंगें।'''",
+ "apihelp-oathvalidate-description": "दो कारक प्रमाणीकरण (शपथ) की निशानी पोषण करें।",
+ "apihelp-oathvalidate-param-totp": "दो कारक प्रमाणीकरण (शपथ) की निशानी।",
+ "apihelp-oathvalidate-example-1": "वर्तमान उपयोगकर्ता के लिए एक टोकन पोषण करें।",
+ "apihelp-oathvalidate-example-2": "उपयोगकर्ता के लिए एक टोकन मंजूर करें<kbd>उदाहरण</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/hr.json b/www/wiki/extensions/OATHAuth/i18n/hr.json
new file mode 100644
index 00000000..21eacf16
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/hr.json
@@ -0,0 +1,21 @@
+{
+ "@metadata": {
+ "authors": [
+ "MaGa"
+ ]
+ },
+ "oathauth-account": "Suradnički račun:",
+ "oathauth-secret": "Tajni ključ za dvostruku provjeru autentičnosti:",
+ "oathauth-enable": "Omogući dvostruku provjeru autentičnosti",
+ "oathauth-scratchtokens": "Sljedeći popis je popis jednokratnih tokena. Mogu se uporabiti jednom, i to u hitnim slučajevima. Zapišite ih i čuvajte ih na sigurnom mjestu. Ako izgubite telefon, ovi tokeni su jedini način da spasite suradnički račun. Ovi tokeni više nikada neće biti prikazani.",
+ "oathauth-prefs-label": "Dvostruka provjera autentičnosti:",
+ "oathauth-step1": "Prvi korak: preuzmite program za dvostruku provjeru autentičnosti",
+ "oathauth-step1-test": "Preuzmite program za dvostruku provjeru autentičnosti. Može biti aplikacija za mobilni uređaj (kao Google Authenticator) ili aplikacija za stolno računalo",
+ "oathauth-step2": "Drugi korak: skenirajte QR kôd",
+ "oathauth-step2alt": "Možete unijeti i ručno:",
+ "oathauth-step3": "Treći korak: zapišite kôdove",
+ "oathauth-step4": "Četvrti korak: provjera",
+ "oathauth-entertoken": "Za provjeru unesite kôd s Vašeg uređaja:",
+ "right-oathauth-enable": "Omogući dvostruku provjeru autentičnosti",
+ "action-oathauth-enable": "omogući dvostruku provjeru autentičnosti"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/hsb.json b/www/wiki/extensions/OATHAuth/i18n/hsb.json
new file mode 100644
index 00000000..bbeb1822
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/hsb.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Michawiki"
+ ]
+ },
+ "oathauth-desc": "Zmóžnja awtentifkaciju z pomocu jónkróć wužiwanych hesłow na zakładźe HMAC",
+ "specialpages-group-oath": "Dwufaktorowa awtentifikacija",
+ "oathauth-account": "Kontowe mjeno dwufaktoroweje awtentifikacije:",
+ "oathauth-secret": "Tajny kluč dwufaktoroweje awtentfikacije:",
+ "oathauth-enable": "Dwufaktorowu awtentifikaciju zmóžnić",
+ "oathauth-scratchtokens": "Slědowaca lisćina je lisćina jónkróć wužiwajomnych specialnych tokenow. Tute tokeny dadźa so jenož jadyn raz wužiwać a su za nuzowe pady. Prošu napisaj je a wobchowaj je na wěstym městnje. Jeli zhubiš swój mobilny telefon, budu tute tokeny jenička móžnosć, zo by swoje konto zachował. Tute tokeny so ći ženje znowa njepokazaja.",
+ "oathauth-token": "Token",
+ "oathauth-disable": "Dwufaktorowu awtentifikaciju znjemóžnić",
+ "oathauth-validatedoath": "Dwufaktorowe přizjewjenske informacije su so wobkrućili. Dwufaktorowa awtentifikacija budźe so nětko wukonjeć.",
+ "oathauth-failedtovalidateoath": "Dwufaktorowe přizjewjenske informacije njedachu so wobkrućić",
+ "oathauth-disabledoath": "Dwufaktorowu awtentifikaciju znjemóžnjena.",
+ "oathauth-prefs-label": "Dwufaktorowa awtentifikacija:"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/hu.json b/www/wiki/extensions/OATHAuth/i18n/hu.json
new file mode 100644
index 00000000..d489134e
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/hu.json
@@ -0,0 +1,44 @@
+{
+ "@metadata": {
+ "authors": [
+ "Bencoke",
+ "Tacsipacsi"
+ ]
+ },
+ "specialpages-group-oath": "Kétlépcsős azonosítás",
+ "oathauth-account": "Fióknév:",
+ "oathauth-secret": "Kétlépcsős azonosítás titkos kulcsa:",
+ "oathauth-enable": "Kétlépcsős azonosítás engedélyezése",
+ "oathauth-token": "Token",
+ "oathauth-disable": "Kétlépcsős azonosítás letiltása",
+ "oathauth-validatedoath": "Kétlépcsős azonosítás engedélyezve. Mostantól csak két lépésben tudod azonosítani magad.",
+ "oathauth-failedtovalidateoath": "A kétlépcsős azonosítási adatok ellenőrzése sikertelen",
+ "oathauth-disabledoath": "Kétlépcsős azonosítás letiltva.",
+ "oathauth-prefs-label": "Kétlépcsős azonosítás:",
+ "oathauth-step1": "1. lépés: Alkalmazás letöltése",
+ "oathauth-step1-test": "Tölts le egy kétlépcsős azonosításra szolgáló alkalmazást (pl. Google Hitelesítő) a telefonodra.",
+ "oathauth-step2": "2. lépés: QR-kód beolvasása",
+ "oathauth-step2alt": "Vagy írd be a titkos kódot kézzel:",
+ "oathauth-step4": "4. lépés: Ellenőrzés",
+ "oathauth-entertoken": "Írj be egy kódot a mobilalkalmazásodból az ellenőrzéshez:",
+ "right-oathauth-enable": "kétlépcsős azonosítás engedélyezése",
+ "action-oathauth-enable": "kétlépcsős azonosítás engedélyezése",
+ "oathauth-auth-token-label": "Token",
+ "oathauth-auth-token-help": "A kétlépcsős bejelentkezés második lépcsőjeként használt egyszeri jelszó.",
+ "oathauth-auth-ui": "Írd be a megerősítő kódot a hitelesítési eszközödről",
+ "oathauth-throttled": "Túl sok hitelesítési kísérlet! Várj $1 ideig.",
+ "oathauth-login-failed": "Ellenőrzés sikertelen",
+ "oathauth-describe-provider": "Kétlépcsős azonosítás (OATH).",
+ "grant-group-authentication": "hitelesítési műveletek végrehajtása magadnak és másoknak",
+ "grant-oath": "kétlépcsős hitelesítés (OATH) információinak elérése magadról és másokról",
+ "right-oathauth-api-all": "OATH-információk lekérdezése és érvényesítése magadról és másokról",
+ "apihelp-query+oath-description": "Annak ellenőrzése, hogy a kétlépcsős azonosítás (OATH) engedélyezve van-e egy felhasználónak.",
+ "apihelp-query+oath-param-user": "A lekérdezendő felhasználó, alapértelmezetten az aktuális.",
+ "apihelp-query+oath-example-1": "Információk az aktuális felhasználóról",
+ "apihelp-query+oath-example-2": "Információk az <kbd>Example</kbd> felhasználóról.",
+ "apihelp-oathvalidate-description": "Kétlépcsős hitelesítés (OATH) tokenjének érvényesítése.",
+ "apihelp-oathvalidate-param-user": "A felhasználó, akinek a tokenjét érvényesíteni szeretnéd, alapértelmezetten az aktuális felhasználó.",
+ "apihelp-oathvalidate-param-totp": "Kétlépcsős azonosítás (OATH) tokenje.",
+ "apihelp-oathvalidate-example-1": "Egy token érvényesítése az aktuális felhasználónak",
+ "apihelp-oathvalidate-example-2": "Egy token érvényesítése az <kbd>Example</kbd> felhasználónak"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/hy.json b/www/wiki/extensions/OATHAuth/i18n/hy.json
new file mode 100644
index 00000000..ec98e86a
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/hy.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Kareyac",
+ "Xelgen"
+ ]
+ },
+ "oathauth-account": "Հաշվի անունը",
+ "oathauth-auth-ui": "Խնդրում ենք մուտքագրել հաստատման կոդը ձեր նույնականացման սարքից"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/ia.json b/www/wiki/extensions/OATHAuth/i18n/ia.json
new file mode 100644
index 00000000..2b5a460e
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/ia.json
@@ -0,0 +1,26 @@
+{
+ "@metadata": {
+ "authors": [
+ "McDutchie"
+ ]
+ },
+ "oathauth-desc": "Forni supporto de authentication usante contrasignos a uso unic a base de HMAC",
+ "specialpages-group-oath": "Authentication bifactorial",
+ "oathauth-account": "Nomine de conto:",
+ "oathauth-secret": "Clave secrete de authentication bifactorial:",
+ "oathauth-enable": "Activar authentication bifactorial",
+ "oathauth-scratchtokens": "Le sequente lista contine indicios a uso unic. Iste indicios pote esser usate un sol vice e es pro casos de emergentia. Per favor nota los e guarda los in un loco secur. Si tu perde tu telephono, iste indicios es le sol maniera de salvar tu conto. Iste indicios nunquam essera monstrate un altere vice.",
+ "oathauth-token": "Indicio",
+ "oathauth-disable": "Disactivar authentication bifactorial",
+ "oathauth-validatedoath": "Le credentiales bifactorial ha essite validate. Le authentication bifactorial essera applicate desde ora.",
+ "oathauth-failedtovalidateoath": "Impossibile validar credentiales bifactorial",
+ "oathauth-disabledoath": "Le authentication bifactorial ha essite disactivate.",
+ "right-oathauth-enable": "Activar le authentication bifactorial",
+ "action-oathauth-enable": "activar le authentication bifactorial",
+ "oathauth-auth-token-label": "Indicio",
+ "oathauth-auth-token-help": "Le contrasigno a uso singule usate como secunde factor pro le authentication bifactorial.",
+ "oathauth-auth-ui": "Scribe le codice de verification ab tu dispositivo de authentication",
+ "oathauth-throttled": "Troppo de tentativas de verification! Per favor attende $1.",
+ "oathauth-login-failed": "Verification fallite.",
+ "oathauth-describe-provider": "Authentication bifactorial (OATH)."
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/id.json b/www/wiki/extensions/OATHAuth/i18n/id.json
new file mode 100644
index 00000000..b697a071
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/id.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "Rachmat.Wahidi",
+ "Rachmat04"
+ ]
+ },
+ "oathauth-auth-token-label": "Token",
+ "oathauth-auth-token-help": "Kata sandi satu kali digunakan sebagai faktor kedua dari otentikasi dua-faktor.",
+ "oathauth-auth-ui": "Silakan masukkan kode verifikasi dari aplikasi telepon genggam Anda",
+ "oathauth-throttled": "Terlalu banyak percobaan verifikasi dilakukan! Mohon menunggu $1.",
+ "oathauth-login-failed": "Verifikasi gagal.",
+ "oathauth-describe-provider": "Otentikasi dua-faktor (OATH)."
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/is.json b/www/wiki/extensions/OATHAuth/i18n/is.json
new file mode 100644
index 00000000..8efd686b
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/is.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Sveinn í Felli"
+ ]
+ },
+ "oathauth-account": "Heiti aðgangs:",
+ "oathauth-token": "Teikn",
+ "oathauth-auth-token-label": "Teikn",
+ "oathauth-login-failed": "Staðfesting mistókst."
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/it.json b/www/wiki/extensions/OATHAuth/i18n/it.json
new file mode 100644
index 00000000..add891de
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/it.json
@@ -0,0 +1,44 @@
+{
+ "@metadata": {
+ "authors": [
+ "Beta16",
+ "Darth Kule",
+ "Gianfranco",
+ "McDutchie"
+ ]
+ },
+ "oathauth-desc": "Fornisce supporto per l'autenticazione utilizzando password a uso singolo basate su HMAC",
+ "specialpages-group-oath": "Autenticazione a due fattori",
+ "oathauth-account": "Nome utenza:",
+ "oathauth-secret": "Chiave segreta per l'autenticazione a due fattori:",
+ "oathauth-enable": "Abilita autenticazione a due fattori",
+ "oathauth-scratchtokens": "Il seguente è un elenco di token monouso. Questi token possono essere utilizzati solo una volta e sono per casi di emergenza. Sei pregato di annotarteli e tenerli in un luogo sicuro. Se perdi il telefono, questi token sono l'unico modo per recuperare l'accesso al tuo account. '''Questi token non saranno mostrati mai più.'''",
+ "oathauth-token": "Token",
+ "oathauth-disable": "Disabilita autenticazione a due fattori",
+ "oathauth-validatedoath": "Convalidate le credenziali a due fattori. D'ora in poi sarà applicata l'autenticazione a due fattori.",
+ "oathauth-failedtovalidateoath": "Impossibile convalidare le credenziali a due fattori",
+ "oathauth-disabledoath": "Disabilita autenticazione a due fattori.",
+ "oathauth-prefs-label": "Autenticazione a due fattori:",
+ "oathauth-step1": "Passo 1: scarica un programma per l'autenticazione a due fattori",
+ "oathauth-step1-test": "Scarica un programma per l'autenticazione a due fattori. Può essere un'applicazione mobile (come Google Authenticator) o per desktop.",
+ "oathauth-step2": "Passo 2: scansiona il codice QR",
+ "oathauth-step2alt": "O inserisci il codice segreto manualmente:",
+ "oathauth-step3": "Passo 3: scrivi il codice 'graffiato'",
+ "oathauth-step4": "Passo 4: verifica",
+ "oathauth-entertoken": "Inserisci un codice dal tuo dispositivo di autenticazione per verificare:",
+ "right-oathauth-enable": "Abilita autenticazione a due fattori",
+ "action-oathauth-enable": "abilitare l'autenticazione a due fattori",
+ "oathauth-auth-token-label": "Token",
+ "oathauth-auth-token-help": "La password a uso singolo utilizzata come secondo fattore per l'autenticazione a due fattori.",
+ "oathauth-auth-ui": "Inserisci il codice di verifica dal tuo dispositivo di autenticazione",
+ "oathauth-throttled": "Troppi tentativi di verifica! Attendi $1.",
+ "oathauth-login-failed": "Verifica non riuscita.",
+ "oathauth-describe-provider": "Autenticazione a due fattori (OATH).",
+ "apihelp-query+oath-example-1": "Ottieni informazioni sull'utente attuale",
+ "apihelp-query+oath-example-2": "Ottieni informazioni sull'utente <kbd>Example</kbd>",
+ "apihelp-oathvalidate-description": "Convalida un token di autenticazione a due fattori (OATH).",
+ "apihelp-oathvalidate-param-user": "Utente per cui convalidare il token. Valore predefinito è l'utente attuale.",
+ "apihelp-oathvalidate-param-totp": "Token di autenticazione a due fattori (OATH).",
+ "apihelp-oathvalidate-example-1": "Convalida un token per l'utente attuale",
+ "apihelp-oathvalidate-example-2": "Convalida un token per l'utente <kbd>Example</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/ja.json b/www/wiki/extensions/OATHAuth/i18n/ja.json
new file mode 100644
index 00000000..cc675bcd
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/ja.json
@@ -0,0 +1,23 @@
+{
+ "@metadata": {
+ "authors": [
+ "Shirayuki",
+ "ネイ",
+ "Yusuke1109"
+ ]
+ },
+ "oathauth-desc": "ワンタイムパスワードに基づいた HMAC を使用する認証機能を提供する",
+ "specialpages-group-oath": "二要素認証",
+ "oathauth-account": "アカウント名:",
+ "oathauth-secret": "二要素認証の秘密鍵:",
+ "oathauth-enable": "二要素認証の有効化",
+ "oathauth-scratchtokens": "以下は、一度しか使用できないトークンの一覧です。これらのトークンは一度しか使用できず、緊急用です。これらを書き留めて、安全な場所に保管してください。携帯電話を紛失した際に、これらのトークンがあなたのアカウントを救済する唯一の手段になります。'''これらのトークンは二度と表示されません'''。",
+ "oathauth-token": "トークン",
+ "oathauth-disable": "二要素認証の無効化",
+ "oathauth-validatedoath": "二要素信用情報を検証しました。二要素認証を実行します。",
+ "oathauth-failedtovalidateoath": "二要素信用情報の検証に失敗しました。",
+ "oathauth-disabledoath": "二要素認証を無効にしました。",
+ "oathauth-prefs-label": "二要素認証:",
+ "right-oathauth-enable": "二要素認証を有効にする",
+ "action-oathauth-enable": "二要素認証の有効化"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/jv.json b/www/wiki/extensions/OATHAuth/i18n/jv.json
new file mode 100644
index 00000000..bc9b27b8
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/jv.json
@@ -0,0 +1,40 @@
+{
+ "@metadata": {
+ "authors": [
+ "NoiX180"
+ ]
+ },
+ "specialpages-group-oath": "Otèntifikasi rong faktor",
+ "oathauth-account": "Jeneng akun:",
+ "oathauth-secret": "Kunci wani otèntifikasi rong faktor:",
+ "oathauth-enable": "Urubaké otèntifikasi rong faktor",
+ "oathauth-token": "Token",
+ "oathauth-disable": "Patèni otèntifikasi rong faktor",
+ "oathauth-failedtovalidateoath": "Wurung validhasi krédhènsial rong faktor",
+ "oathauth-disabledoath": "Patèni otèntifikasi rong faktor.",
+ "oathauth-prefs-label": "Otèntifikasi rong faktor:",
+ "oathauth-step1": "Lampah 1: Undhuh program otèntifikasi rong faktor",
+ "oathauth-step2alt": "Utawa isinen wadiné kanthi manual:",
+ "oathauth-step3": "Lampah 3: Tulisen kodhe rèngrèngan",
+ "oathauth-step4": "Lampah 5: Vèrifikasi",
+ "oathauth-entertoken": "Isinen kodhe saka piranti otèntifikasi kanggo vèrifikasi:",
+ "right-oathauth-enable": "Urubaké otèntifikasi rong faktor",
+ "action-oathauth-enable": "urubaké otèntifikasi rong faktor",
+ "oathauth-auth-token-label": "Token",
+ "oathauth-auth-ui": "Mangga isinen kodhe vèrifikasi saka piranti otèntifikasiné panjenengan",
+ "oathauth-throttled": "Kakèhan tumindak vèrifikasi! Mangga entènana $1.",
+ "oathauth-login-failed": "Wurung vèrifikasi.",
+ "oathauth-describe-provider": "Otèntifikasi rong faktor (OATH).",
+ "grant-group-authentication": "Ayahi tumindak otèntifikasi kanggo panjenengan dhéwé lan liyan",
+ "grant-oath": "Aksès informasi otèntifikasi rong faktor (OATH) kanggo panjenengan dhéwé lan liyan",
+ "action-oathauth-api-all": "priksa status OATH",
+ "apihelp-query+oath-description": "Priksa yèn otèntifikasi rong faktor (OATH) wis murub tumrap panganggo.",
+ "apihelp-query+oath-param-user": "Panganggo sing informasiné arep digolèki. Modhe gawan ya iku marang panganggo saiki.",
+ "apihelp-query+oath-example-1": "Golèk informasi ngenani panganggo saiki",
+ "apihelp-query+oath-example-2": "Golèk informasi ngenani panganggo <kbd>Conto</kbd>",
+ "apihelp-oathvalidate-description": "Validhasi token otèntifikasi rong faktor (OATH).",
+ "apihelp-oathvalidate-param-user": "Panganggo sing tokené arep divalidhasi. Modhe gawan ya iku marang panganggo saiki.",
+ "apihelp-oathvalidate-param-totp": "Token otèntifikasi rong faktor (OATH).",
+ "apihelp-oathvalidate-example-1": "Validhasi token kanggo panganggo saiki",
+ "apihelp-oathvalidate-example-2": "Validhasi token kanggo panganggo <kbd>Conto</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/ka.json b/www/wiki/extensions/OATHAuth/i18n/ka.json
new file mode 100644
index 00000000..a5f3504f
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/ka.json
@@ -0,0 +1,24 @@
+{
+ "@metadata": {
+ "authors": [
+ "David1010",
+ "Otogi"
+ ]
+ },
+ "specialpages-group-oath": "ორფაქტორიანი იდენტიფიკაცია",
+ "oathauth-account": "ორფაქტორიანი ანგარიშის სახელი:",
+ "oathauth-secret": "ორფაქტორიანი საიდუმლო გასაღები:",
+ "oathauth-enable": "ორფაქტორიანი იდენტიფიკაციის ჩართვა",
+ "oathauth-scratchtokens": "შემდეგი სია წარმოადგენს დაკაწრული ტოკენების გამოყენების ერთჯერად სიას. ეს ტოკენები მხოლოდ ერთხელ შეიძლება იყოს გამოყენებული და განკუთვნილია საგანგებო სიტუაციების სამართავად. გთხოვთ, ჩაიწერეთ ისინი და შეინახეთ უსაფრთხო ადგილას. ტელეფონის დაკარგვის შემთხევაში თქვენი ანგარიშის გადარჩენის ერთადერთი საშუალება ტოკენები იქნება. ეს ტოკენები აღარასოდეს იქნება ნაჩვენები.",
+ "oathauth-token": "ჟეტონი",
+ "oathauth-disable": "ორფაქტორიანი იდენტიფიკაციის გამორთვა",
+ "oathauth-disabledoath": "ორფაქტორიანი იდენტიფიკაცია გამორთულია.",
+ "oathauth-prefs-label": "ორფაქტორიანი ავთენტიკაცია",
+ "oathauth-step1": "ნაბიჯი 1: გადმოწერეთ აპლიკაცია",
+ "oathauth-step1-test": "გადმოწერეთ მიბილური აპლიკაცია ორფაქტორიანი ავთენტიკაციისთვის (მსგავსად Google ავთენტიკატორისა) თქვენ ტელეფონზე",
+ "oathauth-step2": "ნაბიჯი 2: დაასკანირეთ QR კოდი",
+ "oathauth-step2alt": "ან ხელით შეიყვანეთ გასაღები",
+ "oathauth-step3": "ნაბიჯი 3: ჩაიწერეთ დაკაწრული კოდები",
+ "oathauth-step4": "ნაბიჯი 4: ვერიფიკაცია",
+ "oathauth-entertoken": "დასადასტურებლად შეიყვანეთ კოდი თქვენი მობილური აპლიკაციიდან"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/ko.json b/www/wiki/extensions/OATHAuth/i18n/ko.json
new file mode 100644
index 00000000..0c5ccfea
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/ko.json
@@ -0,0 +1,50 @@
+{
+ "@metadata": {
+ "authors": [
+ "Priviet",
+ "아라",
+ "Ykhwong",
+ "Jonghaya",
+ "Revi",
+ "Tursetic",
+ "IRTC1015"
+ ]
+ },
+ "oathauth-desc": "일회용 비밀번호에 기초한 HMAC를 사용하여 인증 기능을 제공",
+ "specialpages-group-oath": "2요소 인증",
+ "oathauth-account": "계정 이름:",
+ "oathauth-secret": "2요소 인증 비밀 키:",
+ "oathauth-enable": "2요소 인증 활성화하기",
+ "oathauth-scratchtokens": "다음 목록은 일회용 스크래치 토큰 목록입니다. 이 토큰들은 한 번만 사용할 수 있으며 비상용입니다. 이것들을 적어놓고 안전한 위치에 보관하세요. 전화를 분실할 경우 이 토큰들이 당신의 계정을 찾을 유일한 수단입니다. '''이 토큰들은 다시 볼 수 없습니다'''.",
+ "oathauth-token": "토큰",
+ "oathauth-disable": "2요소 인증 비활성화하기",
+ "oathauth-validatedoath": "2요소 자격 정보를 검증했습니다. 2요소 인증을 실행합니다.",
+ "oathauth-noscratchforvalidation": "2요소 인증을 확인하기 위해 스크래치 코드를 사용할 수 없습니다. 스크래치 코드는 백업용 및 부수적인 용도 전용입니다. 코드 생성기의 인증 코드를 사용해 주십시오.",
+ "oathauth-failedtovalidateoath": "2요소 자격정보를 검증하는 데 실패했습니다",
+ "oathauth-disabledoath": "2요소 인증이 비활성화되었습니다.",
+ "oathauth-prefs-label": "2요소 인증:",
+ "oathauth-step1": "1단계: 2요소 인증 프로그램을 다운로드하세요",
+ "oathauth-step1-test": "2요소 인증을 위한 프로그램을 다운로드하세요. 모바일 애플리케이션(Google Authenticator 등)이나 데스크톱 애플리케이션일 수 있습니다.",
+ "oathauth-step2": "2단계: QR 코드를 스캔하세요",
+ "oathauth-step2alt": "아니면 수동으로 비밀 키를 입력하세요:",
+ "oathauth-step3": "3단계: 스크래치 코드를 기록하세요",
+ "oathauth-step4": "4단계: 확인",
+ "oathauth-entertoken": "확인을 위해 인증 장치로부터 받은 코드를 입력하세요:",
+ "right-oathauth-enable": "2요소 인증 활성화",
+ "action-oathauth-enable": "2요소 인증 활성화",
+ "oathauth-auth-token-label": "토큰",
+ "oathauth-auth-token-help": "2단계 인증의 두 번째 단계로 사용되는 일회용 암호입니다.",
+ "oathauth-auth-ui": "인증 장치의 인증 코드를 넣어주세요",
+ "oathauth-throttled": "너무 많은 인증을 요청했습니다! $1 기다리세요.",
+ "oathauth-login-failed": "인증 검토 실패.",
+ "oathauth-describe-provider": "2단계 인증(OATH)",
+ "grant-group-authentication": "인증을 위해 OAuth 사용",
+ "grant-oath": "자기 자신과 다른 사람에 대한 2요소 인증(OATH) 정보에 접근합니다",
+ "action-oathauth-api-all": "OATH 상태 학인",
+ "apihelp-oathvalidate-description": "2요소 인증 (OATH) 토큰을 검사합니다.",
+ "apihelp-oathvalidate-summary": "2요소 인증 (OATH) 토큰을 검사합니다.",
+ "apihelp-oathvalidate-param-user": "토큰을 확인할 사용자입니다. 기본값은 현재 사용자입니다.",
+ "apihelp-oathvalidate-param-totp": "2요소 인증(OATH) 토큰입니다.",
+ "apihelp-oathvalidate-example-1": "현재 사용자의 토큰 인증",
+ "apihelp-oathvalidate-example-2": "사용자 <kbd>Example</kbd>의 토큰을 검사합니다"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/ksh.json b/www/wiki/extensions/OATHAuth/i18n/ksh.json
new file mode 100644
index 00000000..3f3a4663
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/ksh.json
@@ -0,0 +1,18 @@
+{
+ "@metadata": {
+ "authors": [
+ "Purodha"
+ ]
+ },
+ "oathauth-desc": "Määd et Enlogge met enem <code lang=\"en\">HMAC</code>-eijmohl-Paßwoot müjjelesch.",
+ "oath": "OATHAuth — de Login-Pröhvong övver zwai Wähje",
+ "specialpages-group-oath": "Two-factor authentication !FUZZY!! <!--\n\thttps://translatewiki.net/wiki/Thread:Support/About_MediaWiki:Specialpages-group-oath/en\n-->",
+ "oathauth-account": "Der Nahme zom Enlogge för de Login-Pröhvong övver zwai Wähje:",
+ "oathauth-secret": "Der jeheime Schößel för de Login-Pröhvong övver zwai Wähje:",
+ "oathauth-enable": "Donn de Login-Pröhvong övver zwai Wähje enschallde",
+ "oathauth-disable": "Donn de Login-Pröhvong övver zwai Wähje ußschallde",
+ "oathauth-validatedoath": "De Aanjahbe för de Login-Pröhvong övver zwai Wähje sin joht, de Login-Pröhvong övver zwai Wähje wehd jäz ",
+ "oathauth-failedtovalidateoath": "De Aanjahbe för de Login-Pröhvong övver zwai Wähje wohre nit joht.",
+ "oathauth-disabledoath": "Mer han de Login-Pröhvong övver zwai Wähje ußjeschalldt.",
+ "oathauth-prefs-label": "Login-Pröhvong övver zwai Wähje:"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/lb.json b/www/wiki/extensions/OATHAuth/i18n/lb.json
new file mode 100644
index 00000000..1dadae17
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/lb.json
@@ -0,0 +1,22 @@
+{
+ "@metadata": {
+ "authors": [
+ "Robby"
+ ]
+ },
+ "oathauth-account": "Numm vum Kont:",
+ "oathauth-enable": "Authentifikatioun mat zwee Elementer aschalten",
+ "oathauth-token": "Token",
+ "oathauth-disable": "Authentifikatioun mat zwee Elementer ausschalten",
+ "oathauth-disabledoath": "Authentifikatioun mat zwee Elementer ausgeschalt",
+ "oathauth-prefs-label": "Authentifikatioun mat zwee Elementer:",
+ "oathauth-step1": "Schrëtt 1: Den Zwee-Facteur-Authentifikatiounsprogramm eroflueden",
+ "oathauth-step2": "Schrëtt 2: De QR-Code scannen",
+ "oathauth-step4": "Schrëtt 4: Verificatioun",
+ "grant-group-authentication": "Authentifikatiouns-Aktioune fir sech selwer a fir Anerer maachen",
+ "action-oathauth-api-all": "OATH-Status nokucken",
+ "apihelp-query+oath-param-user": "Benotzer vun deem d'Informatioun gefrot gëtt. Standard ass et den aktuelle Benotzer.",
+ "apihelp-query+oath-example-1": "Informatiounen iwwer den aktuelle Benotzer kréien",
+ "apihelp-query+oath-example-2": "Informatiounen iwwer de Benotzer <kbd>Beispill</kbd> kréien",
+ "apihelp-oathvalidate-example-1": "En Token fir den aktuelle Benotzer validéieren"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/lki.json b/www/wiki/extensions/OATHAuth/i18n/lki.json
new file mode 100644
index 00000000..2a2e08d2
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/lki.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Hosseinblue"
+ ]
+ },
+ "oathauth-backtopreferences": "بازگشت به اولویت‌ها"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/lt.json b/www/wiki/extensions/OATHAuth/i18n/lt.json
new file mode 100644
index 00000000..968eb05d
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/lt.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Eitvys200"
+ ]
+ },
+ "oathauth-account": "Paskyros vardas:",
+ "action-oathauth-api-all": "patikrinti OATH būseną"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/lv.json b/www/wiki/extensions/OATHAuth/i18n/lv.json
new file mode 100644
index 00000000..776bd676
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/lv.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "Papuass",
+ "Silraks"
+ ]
+ },
+ "oathauth-account": "Konta nosaukums:",
+ "oathauth-enable": "Iespējot divpakāpju autentifikāciju",
+ "oathauth-token": "Marķieris",
+ "right-oathauth-enable": "Iespējot divpakāpju autentifikāciju",
+ "action-oathauth-enable": "iespējot divpakāpju autentifikāciju",
+ "oathauth-auth-token-label": "Marķieris"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/mk.json b/www/wiki/extensions/OATHAuth/i18n/mk.json
new file mode 100644
index 00000000..4610562a
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/mk.json
@@ -0,0 +1,50 @@
+{
+ "@metadata": {
+ "authors": [
+ "Bjankuloski06"
+ ]
+ },
+ "oathauth-desc": "Овозможува заверка на корисничката веродостојност со HMAC врз основа на еднократни лозинки",
+ "specialpages-group-oath": "Двочинителска заверка",
+ "oathauth-account": "Име на сметката:",
+ "oathauth-secret": "Таен клуч за двочинителска заверка:",
+ "oathauth-enable": "Овозможување на двочинителска заверка на веродостојноста",
+ "oathauth-scratchtokens": "Ова е список на еднократни шифри. Можат да се користат само еднаш и служат за непредвидени случаи. Запишете ги и чувајте ги на безбедно место. Ако го загубите телефонот, шифрите се единствен начин да си ја повратите сметката. Овие шифри никогаш повеќе нема да се прикажат.",
+ "oathauth-token": "Шифра",
+ "oathauth-disable": "Оневозможување на двочинителска заверка на веродостојноста",
+ "oathauth-validatedoath": "Двочинителското полномоштво е заверено. Сега стапува на сила.",
+ "oathauth-noscratchforvalidation": "Не можете да користите код од грепка за да ја потврдите двочинителската заверка. Овие кодови се резервни, и имаат исклучиво наменска употреба. Послужете се со код од вашиот создавач на кодови.",
+ "oathauth-failedtovalidateoath": "Не успеав да го заверам двочинителското полномоштво",
+ "oathauth-disabledoath": "Двочинителската заверка е оневозможена.",
+ "oathauth-prefs-label": "Двочинителска заверка:",
+ "oathauth-step1": "Чекор 1: Преземете го програмот за двочинителска заверка",
+ "oathauth-step1-test": "Преземете го програмот за двочинителска заверка. Ова може да биде мобилен прилог (како Google Authenticator) или прилог за столен сметач",
+ "oathauth-step2": "Чекор 2: Отчитајте го QR-кодот",
+ "oathauth-step2alt": "Или пак внесете ја тајната рачно:",
+ "oathauth-step3": "Чекор 3: Запишете го кодовите",
+ "oathauth-step4": "Чекор 4: Заверка",
+ "oathauth-entertoken": "Внесете код од вашиот уред на заверка за да потврдите:",
+ "right-oathauth-enable": "Овозможување на двочинителска заверка",
+ "action-oathauth-enable": "овозможување на двочинителска заверката",
+ "oathauth-auth-token-label": "Жетон",
+ "oathauth-auth-token-help": "Еднократната лозинка што служи како втор чинител во двочинителската заверка.",
+ "oathauth-auth-ui": "Внесете потврден код од вашиот уред на заверка",
+ "oathauth-throttled": "Направивте премногу обиди за заверка! Почекајте $1.",
+ "oathauth-login-failed": "Заверката не успеа.",
+ "oathauth-describe-provider": "Двочинителска заверка (OATH).",
+ "grant-group-authentication": "Вршење заверка за себе и други",
+ "grant-oath": "Пристап до податоците за двочинителска заверка (OATH) за себе и други",
+ "right-oathauth-api-all": "Барање и проверка на податоци за OATH за себе и други",
+ "action-oathauth-api-all": "проверување на OATH-статус",
+ "apihelp-query+oath-description": "Проверка дали двочинителската заверка (OATH) е овозможена за даден корисник.",
+ "apihelp-query+oath-summary": "Провери дали е овозможена двочинителската заверка (OATH) за даден корисник.",
+ "apihelp-query+oath-param-user": "За кој корисник да се дадат информации. По основно: тековниот корисник.",
+ "apihelp-query+oath-example-1": "Дај информации за тековниот корисник.",
+ "apihelp-query+oath-example-2": "Дај информации за корисникот <kbd>Пример</kbd>",
+ "apihelp-oathvalidate-description": "Провери шифра за двочинителска заверка (OATH).",
+ "apihelp-oathvalidate-summary": "Провери шифра за двочинителска заверка (OATH).",
+ "apihelp-oathvalidate-param-user": "Корисник чија шифра треба да се завери. По основно: тековниот корисник.",
+ "apihelp-oathvalidate-param-totp": "Шифра за двочинителска заверка (OATH).",
+ "apihelp-oathvalidate-example-1": "Провери шифра за тековниот корисник",
+ "apihelp-oathvalidate-example-2": "Провери шифра за корисникот <kbd>Пример</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/ml.json b/www/wiki/extensions/OATHAuth/i18n/ml.json
new file mode 100644
index 00000000..28767c06
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/ml.json
@@ -0,0 +1,21 @@
+{
+ "@metadata": {
+ "authors": [
+ "Praveenp"
+ ]
+ },
+ "oathauth-desc": "എച്ച്.എം.എ.സി. അധിഷ്ഠിത ഒറ്റത്തവണ രഹസ്യവാക്കുക്കൾ വഴി സാധൂകരണം ചെയ്യുന്നു",
+ "specialpages-group-oath": "ദ്വി-ഘടക സാധൂകരണം",
+ "oathauth-account": "അംഗത്വ നാമം:",
+ "oathauth-secret": "ദ്വി-ഘടക സാധൂകരണത്തിനുള്ള രഹസ്യചാവി:",
+ "oathauth-enable": "ദ്വി-ഘടക സാധൂകരണം സജ്ജമാക്കുക",
+ "oathauth-token": "ചീട്ട്",
+ "oathauth-disable": "ദ്വി-ഘടക സാധൂകരണം ഒഴിവാക്കുക",
+ "oathauth-disabledoath": "ദ്വി-ഘടക സാധൂകരണം പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു.",
+ "oathauth-prefs-label": "ദ്വി-ഘടക സാധൂകരണം:",
+ "oathauth-step1": "ഘട്ടം 1: ദ്വി-ഘടക സാധൂകരണ പ്രോഗ്രാം ഡൗൺലോഡ് ചെയ്യുക",
+ "oathauth-step2": "ഘട്ടം 2: ക്യൂ.ആർ. കോഡ് സ്കാൻ ചെയുക",
+ "oathauth-step3": "ഘട്ടം 3: സ്ക്രാച്ച് കോഡുകൾ എഴുതിവെക്കുക",
+ "oathauth-step4": "ഘട്ടം 4: പരിശോധിക്കൽ",
+ "oathauth-auth-token-label": "ചീട്ട്"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/mr.json b/www/wiki/extensions/OATHAuth/i18n/mr.json
new file mode 100644
index 00000000..6135375c
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/mr.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "V.narsikar"
+ ]
+ },
+ "oathauth-auth-token-label": "बिल्ला"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/ms.json b/www/wiki/extensions/OATHAuth/i18n/ms.json
new file mode 100644
index 00000000..c5f19c7a
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/ms.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Anakmalaysia"
+ ]
+ },
+ "oathauth-desc": "Menyediakan sokongan penentusahan dengan menggunakan kata laluan kegunaan sekali berasaskan HMAC",
+ "specialpages-group-oath": "Penentusahan Dwifaktor",
+ "oathauth-account": "Nama Akaun Dwifaktor:",
+ "oathauth-secret": "Kunci Rahsia Dwifaktor:",
+ "oathauth-enable": "Hidupkan Penentusahan Dwifaktor",
+ "oathauth-scratchtokens": "Yang berikut ialah senarai token kegunaan sekali. Token-token ini hanya boleh digunakan sekali, malah adalah untuk kegunaan kecemasan. Sila catatkan dalam kertas dan simpan dalam tempat yang selamat. Sekiranya anda kehilangan telefon anda, token-token ini sahajalah caranya untuk menyelamatkan akaun anda. Token-token ini tidak akan dipaparkan lagi.",
+ "oathauth-token": "Token",
+ "oathauth-disable": "Matikan Penentusahan Dwifaktor",
+ "oathauth-validatedoath": "Watikah dwifaktor disahkan. Penentusahan dwifaktor kini akan berkuatkuasa.",
+ "oathauth-failedtovalidateoath": "Watikah dwifaktor gagal disahkan",
+ "oathauth-disabledoath": "Penentusahan dwifaktor dimatikan.",
+ "oathauth-prefs-label": "Penentusahan dwifaktor:"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/mwl.json b/www/wiki/extensions/OATHAuth/i18n/mwl.json
new file mode 100644
index 00000000..4c44fefc
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/mwl.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "MokaAkashiyaPT",
+ "Athena in Wonderland"
+ ]
+ },
+ "specialpages-group-oath": "Outenticaçon de dous fatores",
+ "oathauth-prefs-label": "Outenticaçon de dous fatores:",
+ "right-oathauth-enable": "Atibar outenticaçon de dous fatores",
+ "action-oathauth-enable": "atibar outenticaçon de dous fatores"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/nb.json b/www/wiki/extensions/OATHAuth/i18n/nb.json
new file mode 100644
index 00000000..457eaca1
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/nb.json
@@ -0,0 +1,51 @@
+{
+ "@metadata": {
+ "authors": [
+ "Jon Harald Søby",
+ "Jeblad"
+ ]
+ },
+ "oathauth-desc": "Muliggjør autentiseringsstøtte med bruk av HMAC-baserte engangspassord",
+ "specialpages-group-oath": "Tofaktor-autentisering",
+ "oathauth-account": "Kontonavn:",
+ "oathauth-secret": "Hemmelig nøkkel for tofaktor-autentisering:",
+ "oathauth-enable": "Slå på tofaktor-autentisering",
+ "oathauth-scratchtokens": "Følgende liste er en liste over engangspassord. Disse kan kun brukes én gang, og bør kun brukes i nødstilfelle. Skriv dem ned og legg dem på et lurt sted. Om du mister telefonen din vil disse passordene være eneste måte å få tilbake kontoen din. Disse passordene vil aldri vises igjen.",
+ "oathauth-token": "Tegn",
+ "oathauth-disable": "Slå av tofaktor-autentisering",
+ "oathauth-validatedoath": "Validerte tofaktor-attestering. Tofaktorautentisering vil nå bli iverksatt.",
+ "oathauth-noscratchforvalidation": "Du kan ikke bruke en engangskode for å bekrefte tofaktorautentisering. Engangskoder er kun til backup og nødsbruk. Bruk en bekreftelseskode fra kodegeneratoren din.",
+ "oathauth-failedtovalidateoath": "Kunne ikke validere tofaktorattestering",
+ "oathauth-disabledoath": "Tofaktorautentisering er nå slått av.",
+ "oathauth-prefs-label": "Tofaktorautentisering:",
+ "oathauth-step1": "Steg 1: Last ned et program for tofaktor-autentisering",
+ "oathauth-step1-test": "Last ned et program for tofaktor-autentisering. Det kan være en mobilapp (som Google Authenticator) eller et dataprogram.",
+ "oathauth-step2": "Steg 2: Skann QR-koden",
+ "oathauth-step2alt": "Eller skriv inn hemmeligheten manuelt:",
+ "oathauth-step3": "Steg 3: Skriv ned engangskodene",
+ "oathauth-step4": "Steg 4: Verifisering",
+ "oathauth-entertoken": "Skriv inn en kode fra autentiseringsprogrammet ditt for å verifisere:",
+ "right-oathauth-enable": "Slå på tofaktor-autentisering",
+ "action-oathauth-enable": "slå på tofaktor-autentisering",
+ "oathauth-auth-token-label": "Token",
+ "oathauth-auth-token-help": "Engangspassordet som brukes som den andre faktoren av tofaktor-autentiseringen.",
+ "oathauth-auth-ui": "Skriv inn verifiseringskoden fra autentiseringsenheten din",
+ "oathauth-throttled": "For mange verifiseringsforsøk! Vent i $1.",
+ "oathauth-login-failed": "Verifikasjonen feilet.",
+ "oathauth-describe-provider": "Tofaktor-autentisering (OATH).",
+ "grant-group-authentication": "Utføre autentiseringshandlinger for seg selv og andre",
+ "grant-oath": "Få tilgang til tofaktorautentiseringsinformasjon (OATH) for seg selv og andre",
+ "right-oathauth-api-all": "Spørre mot og validere OATH-informasjon for seg selv og andre",
+ "action-oathauth-api-all": "sjekk OATH-status",
+ "apihelp-query+oath-description": "Sjekk om tofaktorautentisering (OATH) er slått på for en bruker.",
+ "apihelp-query+oath-summary": "Sjekk om tofaktorautentisering (OATH) er slått på for en bruker.",
+ "apihelp-query+oath-param-user": "Brukeren det skal hentes informasjon om. Standard er den gjeldende brukeren.",
+ "apihelp-query+oath-example-1": "Få informasjon om den aktuelle brukeren",
+ "apihelp-query+oath-example-2": "Få informasjon om brukeren <kbd>Example</kbd>",
+ "apihelp-oathvalidate-description": "Valider et tofaktor-autentiseringstegn (OATH).",
+ "apihelp-oathvalidate-summary": "Valider et tofaktorautentiseringstegn (OATH).",
+ "apihelp-oathvalidate-param-user": "Brukeren tegnet skal valideres for. Standard er den aktuelle brukeren.",
+ "apihelp-oathvalidate-param-totp": "Tofaktor-autentiseringstegn (OATH).",
+ "apihelp-oathvalidate-example-1": "Valider et tegn for den aktuelle brukeren",
+ "apihelp-oathvalidate-example-2": "Valider et tegn for brukeren <kbd>Example</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/ne.json b/www/wiki/extensions/OATHAuth/i18n/ne.json
new file mode 100644
index 00000000..06bca26a
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/ne.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "NehalDaveND"
+ ]
+ },
+ "oathauth-token": "टोकन"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/nl.json b/www/wiki/extensions/OATHAuth/i18n/nl.json
new file mode 100644
index 00000000..c552980d
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/nl.json
@@ -0,0 +1,27 @@
+{
+ "@metadata": {
+ "authors": [
+ "SPQRobin",
+ "Siebrand",
+ "Romaine",
+ "Sjoerddebruin",
+ "Southparkfan"
+ ]
+ },
+ "oathauth-desc": "Biedt ondersteuning voor authenticatie via op HMAC-gebaseerde eenmalige wachtwoorden",
+ "specialpages-group-oath": "Tweetrapsauthenticatie",
+ "oathauth-account": "Accountnaam:",
+ "oathauth-secret": "Geheime sleutel voor tweetrapsauthenticatie:",
+ "oathauth-enable": "Tweetrapsauthenticatie inschakelen",
+ "oathauth-scratchtokens": "De onderstaande lijst bevat tokens voor eenmalig gebruik. Deze tokens kunnen slechts één keer gebruikt worden en zijn bedoeld voor noodgevallen. Noteer deze tokens en bewaar ze op een veilige plaats. Als u uw telefoon bent verloren, zijn deze tokens de enige manier om uw gebruikersaccount te redden. '''Deze tokens worden nooit meer weergegeven'''.",
+ "oathauth-token": "Token",
+ "oathauth-disable": "Tweetrapsauthenticatie uitschakelen",
+ "oathauth-validatedoath": "De gebruikersgegevens voor tweetrapsauthenticatie zijn gevalideerd. Tweetrapsauthenticatie is nu verplicht.",
+ "oathauth-failedtovalidateoath": "Het valideren van de gebruikersgegevens voor tweetrapsauthenticatie is mislukt.",
+ "oathauth-disabledoath": "Tweetrapsauthenticatie is uitgeschakeld.",
+ "oathauth-prefs-label": "Tweetrapsauthenticatie:",
+ "oathauth-entertoken": "Vul een code van uw authenticatie-apparaat in om te bevestigen:",
+ "oathauth-auth-token-label": "Verificatiecode",
+ "oathauth-auth-ui": "Gelieve een verificatiecode van uw authenticatie-apparaat invullen",
+ "grant-group-authentication": "OAuth gebruiken voor authenticatie"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/oc.json b/www/wiki/extensions/OATHAuth/i18n/oc.json
new file mode 100644
index 00000000..eca8a90f
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/oc.json
@@ -0,0 +1,15 @@
+{
+ "@metadata": {
+ "authors": [
+ "Cedric31"
+ ]
+ },
+ "specialpages-group-oath": "Autentificacion de dos factors",
+ "oathauth-enable": "Activar l'autentificacion de dos factors",
+ "oathauth-disable": "Desactivar l'autentificacion de dos factors",
+ "oathauth-disabledoath": "Autentificacion de dos factors desactivada.",
+ "oathauth-prefs-label": "Autentificacion de dos factors :",
+ "right-oathauth-enable": "Activar l’autentificacion de dos factors",
+ "action-oathauth-enable": "activar l’autentificacion de dos factors",
+ "oathauth-describe-provider": "Autentificacion de dos factors (OATH)"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/pl.json b/www/wiki/extensions/OATHAuth/i18n/pl.json
new file mode 100644
index 00000000..415dcf62
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/pl.json
@@ -0,0 +1,30 @@
+{
+ "@metadata": {
+ "authors": [
+ "Chrumps",
+ "Alan ffm"
+ ]
+ },
+ "specialpages-group-oath": "Uwierzytelnianie dwuskładnikowe",
+ "oathauth-account": "Nazwa konta:",
+ "oathauth-secret": "Tajny klucz uwierzytelniania dwuskładnikowego:",
+ "oathauth-enable": "Włączenie uwierzytelniania dwuskładnikowego",
+ "oathauth-token": "Token",
+ "oathauth-disable": "Wyłączenie uwierzytelniania dwuskładnikowego",
+ "oathauth-prefs-label": "Uwierzytelnianie dwuskładnikowe:",
+ "oathauth-step1": "Krok 1: Pobierz program do uwierzytelniania dwuskładnikowego",
+ "oathauth-step1-test": "Pobierz program do uwierzytelniania dwuskładnikowego. Może to być aplikacja mobilna (np. Google Authenticator) lub aplikacja komputerowa",
+ "oathauth-step2": "Krok 2: Zeskanuj kod QR",
+ "oathauth-step2alt": "Lub ręcznie wprowadź tajny klucz:",
+ "oathauth-step4": "Krok 4: Weryfikacja",
+ "oathauth-entertoken": "Wpisz kod z urządzenia uwierzytelniającego w celu weryfikacji:",
+ "oathauth-auth-token-label": "Token",
+ "oathauth-auth-ui": "Wpisz kod weryfikacyjny z urządzenia uwierzytelniającego",
+ "oathauth-throttled": "Zbyt wiele prób weryfikacji! Poczekaj $1.",
+ "oathauth-login-failed": "Weryfikacja nie powiodła się.",
+ "oathauth-describe-provider": "Uwierzytelnianie dwuetapowe (OATH).",
+ "action-oathauth-api-all": "sprawdzania statusu OATH",
+ "apihelp-query+oath-example-1": "Pobierz informacje o aktualnym użytkowniku",
+ "apihelp-query+oath-example-2": "Pobierz informacje na temat użytkownika <kbd>Example</kbd>",
+ "apihelp-oathvalidate-param-totp": "Token uwierzytelniania dwuetapowego (OATH)."
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/pms.json b/www/wiki/extensions/OATHAuth/i18n/pms.json
new file mode 100644
index 00000000..dd2bfc36
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/pms.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Borichèt",
+ "Dragonòt"
+ ]
+ },
+ "oathauth-desc": "A dà un supòrt d'autenticassion dovrand HMAC basà su dle ciav a usagi ùnich",
+ "specialpages-group-oath": "Autenticassion a Doi Fator",
+ "oathauth-account": "Nòm dël Cont dij Doi Fator:",
+ "oathauth-secret": "Ciav Segreta dij Doi Fator:",
+ "oathauth-enable": "Abilité l'Autenticassion a Doi Fator",
+ "oathauth-scratchtokens": "La lista sì-dapress a l'é na lista ëd geton da dovré mach na vira. Sti geton a peulo mach esse dovrà na vira, e a son da dovré an cas d'emergensa. Për piasì, ch'a jë scriva e ch'a-j goerna ant un pòst sigur. S'a perd sò teléfon, costi geton a son l'ùnica manera ëd salvé sò cont. Costi geton a saran mai pi mostrà torna.",
+ "oathauth-token": "Marca-pòst",
+ "oathauth-disable": "Disabilité l'Autenticassion a Doi Fator",
+ "oathauth-validatedoath": "Credensiaj a doi fator validà. L'autenticassion a doi fator a sarà dorenavan aplicà.",
+ "oathauth-failedtovalidateoath": "Falì a validé le credensiaj a doi fator",
+ "oathauth-disabledoath": "Autenticassion a doi fator disabilità."
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/ps.json b/www/wiki/extensions/OATHAuth/i18n/ps.json
new file mode 100644
index 00000000..74b31ae3
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/ps.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Baloch Khan"
+ ]
+ },
+ "grant-oath": "دوه فکتور تصدیق ('''واته''') د ځان او نورو لپاره معلومات"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/pt-br.json b/www/wiki/extensions/OATHAuth/i18n/pt-br.json
new file mode 100644
index 00000000..e43736a4
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/pt-br.json
@@ -0,0 +1,55 @@
+{
+ "@metadata": {
+ "authors": [
+ "Araceletorres",
+ "Luk3",
+ "Tark",
+ "Felipe L. Ewald",
+ "Eduardo Addad de Oliveira"
+ ]
+ },
+ "oathauth-desc": "Fornece suporte a autenticação usando HMAC com base em senhas de uso único",
+ "oath": "OATHAuth",
+ "specialpages-group-oath": "Autenticação de dois fatores",
+ "oathauth-account": "Nome da conta:",
+ "oathauth-secret": "Chave secreta de autenticação de dois fatores:",
+ "oathauth-enable": "Ativar autenticação de dois fatores",
+ "oathauth-scratchtokens": "A lista a seguir é uma lista de chaves de uso único. Essas chaves só podem ser usadas uma vez, e são para uso de emergência. Por favor, anote-as e mantenha-as em um local seguro. Se você perder seu telefone, '''essas chaves são a única maneira de recuperar sua conta. Essas chaves nunca serão mostradas novamente'''.",
+ "oathauth-token": "Token",
+ "oathauth-disable": "Desativar autenticação de dois fatores",
+ "oathauth-validatedoath": "Credenciais de dois fatores validadas. A autenticação de dois fatores agora será aplicada.",
+ "oathauth-noscratchforvalidation": "Você não pode usar um código de rascunho para confirmar a autenticação de dois fatores. Os códigos Scratch são para backup e uso incidental apenas. Use um código de verificação do seu gerador de código.",
+ "oathauth-failedtovalidateoath": "Falha ao validar credenciais de dois fatores",
+ "oathauth-disabledoath": "Desativar autenticação de dois fatores.",
+ "oathauth-prefs-label": "Autenticação de dois fatores:",
+ "oathauth-step1": "Passo 1: Baixe um programa de autenticação de dois fatores",
+ "oathauth-step1-test": "Baixe um programa para a autenticação de dois fatores. Pode ser um aplicativo para celular (como o Google Authenticator) ou para desktop",
+ "oathauth-step2": "Passo 2: Escaneie o código QR",
+ "oathauth-step2alt": "Ou introduza o segredo manualmente:",
+ "oathauth-step3": "Passo 3: Anote os códigos scratch",
+ "oathauth-step4": "Passo 4: Verificação",
+ "oathauth-entertoken": "Digite um código do seu dispositivo de autenticação para verificação:",
+ "right-oathauth-enable": "Ativar autenticação de dois fatores",
+ "action-oathauth-enable": "ativar autenticação em duas etapas",
+ "oathauth-auth-token-label": "Token",
+ "oathauth-auth-token-help": "A senha única utilizada como o segundo fator de autenticação de dois fatores.",
+ "oathauth-auth-ui": "Por favor, digite um código de verificação do seu dispositivo de autenticação",
+ "oathauth-throttled": "Muitas tentativas de verificação! Por favor, aguarde $1.",
+ "oathauth-login-failed": "Verificação falhou.",
+ "oathauth-describe-provider": "Autenticação de dois fatores (OATH).",
+ "grant-group-authentication": "Execute ações de autenticação para si e para os outros",
+ "grant-oath": "Acesse informações de autenticação de dois fatos (OATH) para si e para os outros",
+ "right-oathauth-api-all": "Consultar e validar a informação OATH para si e para os outros",
+ "action-oathauth-api-all": "verificar estado do OATH",
+ "apihelp-query+oath-description": "Verifique se a autenticação de dois fatores (OATH) está habilitada para um usuário.",
+ "apihelp-query+oath-summary": "Verifique se a autenticação de dois fatores (OATH) está habilitada para um usuário.",
+ "apihelp-query+oath-param-user": "Usuário para o qual obter informações. Padrão para o usuário atual.",
+ "apihelp-query+oath-example-1": "Obtenha informações sobre o usuário atual",
+ "apihelp-query+oath-example-2": "Obter informações sobre o usuário <kbd>Exemplo</kbd>",
+ "apihelp-oathvalidate-description": "Valide um token de autenticação de dois fatos (OAUTH).",
+ "apihelp-oathvalidate-summary": "Valide um token de autenticação de dois fatos (OAUTH).",
+ "apihelp-oathvalidate-param-user": "Usuário para o qual validar token. Padrão para o usuário atual.",
+ "apihelp-oathvalidate-param-totp": "Token de autenticação de dois fatos (OATH).",
+ "apihelp-oathvalidate-example-1": "Validar um token para o usuário atual",
+ "apihelp-oathvalidate-example-2": "Valide um token para o usuário <kbd>Exemplo</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/pt.json b/www/wiki/extensions/OATHAuth/i18n/pt.json
new file mode 100644
index 00000000..ef8a37d5
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/pt.json
@@ -0,0 +1,52 @@
+{
+ "@metadata": {
+ "authors": [
+ "Vitorvicentevalente",
+ "Hamilton Abreu",
+ "Athena in Wonderland"
+ ]
+ },
+ "oathauth-desc": "Fornece suporte à autenticação usando palavras-passe de uso único baseadas em códigos HMAC",
+ "specialpages-group-oath": "Autenticação de dois fatores",
+ "oathauth-account": "Nome da conta:",
+ "oathauth-secret": "Chave secreta da autenticação de dois fatores:",
+ "oathauth-enable": "Ativar a autenticação de dois fatores",
+ "oathauth-scratchtokens": "A seguinte é uma lista de chaves de uso único. Estas chaves só podem ser usadas uma vez, e são para casos de emergência. Anote-as, por favor, e guarde-as num local seguro. Se perder o seu telefone, estas chaves são a única forma de recuperar a sua conta. '''Esta é a única vez em que estas chaves serão mostradas'''.",
+ "oathauth-token": "Chave",
+ "oathauth-disable": "Desativar a autenticação de dois fatores",
+ "oathauth-validatedoath": "As credenciais de dois fatores foram validadas. A autenticação de dois fatores será agora aplicada.",
+ "oathauth-noscratchforvalidation": "Não pode usar um código de uso único para confirmar a autenticação de dois fatores. Os códigos de uso único só servem como reserva para uso ocasional. Use um código de verificação do seu gerador de códigos.",
+ "oathauth-failedtovalidateoath": "A validação das credenciais de dois fatores falhou",
+ "oathauth-disabledoath": "A autenticação de dois fatores foi desativada.",
+ "oathauth-prefs-label": "Autenticação de dois fatores:",
+ "oathauth-step1": "Passo 1: Descarregue um programa de autenticação de dois fatores",
+ "oathauth-step1-test": "Descarregue um programa para autenticação de dois fatores. Tanto pode ser uma aplicação de dispositivo móvel (como o Autenticador da Google, Google Authenticator) como de ambiente de trabalho.",
+ "oathauth-step2": "Passo 2: Faça o ''scan'' do código QR",
+ "oathauth-step2alt": "Ou, introduza o segredo manualmente:",
+ "oathauth-step3": "Passo 3: Anote os códigos de uso único",
+ "oathauth-step4": "Passo 4: Verificação",
+ "oathauth-entertoken": "Introduza um código do seu dispositivo de autenticação para verificação:",
+ "right-oathauth-enable": "Ativar autenticação de dois fatores",
+ "action-oathauth-enable": "ativar autenticação de dois fatores",
+ "oathauth-auth-token-label": "Chave",
+ "oathauth-auth-token-help": "A palavra-passe de uso único usada como segundo fator da autenticação de dois fatores.",
+ "oathauth-auth-ui": "Insira o código de verificação do seu dispositivo de autenticação",
+ "oathauth-throttled": "Ocorreram demasiadas tentativas de verificação! Por favor, aguarde $1.",
+ "oathauth-login-failed": "A verificação falhou.",
+ "oathauth-describe-provider": "Autenticação de dois fatores (OATH).",
+ "grant-group-authentication": "Executar as ações de autenticação para si e para outros",
+ "grant-oath": "Aceder a informação da autenticação de dois fatores (OATH) do próprio e de outros",
+ "right-oathauth-api-all": "Consultar e validar informação OATH do próprio e de outros",
+ "action-oathauth-api-all": "verificar o estado OATH",
+ "apihelp-query+oath-description": "Verificar se a autenticação de dois fatores (OATH) está ativa para um utilizador.",
+ "apihelp-query+oath-summary": "Verificar se a autenticação de dois fatores (OATH) está ativa para um utilizador.",
+ "apihelp-query+oath-param-user": "Utilizador acerca do qual será obtida informação. Por omissão, o utilizador atual.",
+ "apihelp-query+oath-example-1": "Obter informações sobre o utilizador atual",
+ "apihelp-query+oath-example-2": "Obter informação sobre o utilizador <kbd>Example</kbd>",
+ "apihelp-oathvalidate-description": "Validar uma chave de autenticação de dois fatores (OATH).",
+ "apihelp-oathvalidate-summary": "Validar uma chave de autenticação de dois fatores (OATH).",
+ "apihelp-oathvalidate-param-user": "O utilizador para o qual a chave será validada. Por omissão, o utilizador atual.",
+ "apihelp-oathvalidate-param-totp": "Chave para autenticação de dois fatores (OATH).",
+ "apihelp-oathvalidate-example-1": "Validar uma chave para o utilizador atual",
+ "apihelp-oathvalidate-example-2": "Validar uma chave para o utilizador <kbd>Example</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/qqq.json b/www/wiki/extensions/OATHAuth/i18n/qqq.json
new file mode 100644
index 00000000..445ea228
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/qqq.json
@@ -0,0 +1,59 @@
+{
+ "@metadata": {
+ "authors": [
+ "Raymond",
+ "Ryan Lane <rlane@wikimedia.org>",
+ "Shirayuki",
+ "Purodha",
+ "Umherirrender",
+ "Tyler Romeo <tylerromeo@gmail.com>",
+ "Liuxinyu970226",
+ "Amire80",
+ "Siebrand"
+ ]
+ },
+ "oathauth-desc": "{{desc|name=OATH Auth|url=https://www.mediawiki.org/wiki/Extension:OATHAuth}}",
+ "oath": "{{optional}}\n{{doc-special|OATH}}",
+ "specialpages-group-oath": "{{doc-special-group|like=[[Special:OATH]]}}\n\nSee [[w:en:Two_factor_authentication|Wikipedia article on two factor authentication]].\n\n{{Identical|Two factor authentication}}",
+ "oathauth-account": "Plain text associated with [https://en.wikipedia.org/wiki/Two_factor_authentication two factor authentication] on this wiki (username@<wiki name>) found on Special:OATH.\n{{Identical|Account name}}",
+ "oathauth-secret": "Plain text found on Special:OATH while enabling OATH\n\nSee [https://en.wikipedia.org/wiki/Two_factor_authentication two factor authentication]",
+ "oathauth-enable": "Page title on Special:OATH, when enabling OATH.\n\nSee [https://en.wikipedia.org/wiki/Two_factor_authentication two factor authentication]",
+ "oathauth-scratchtokens": "Plain text, found on Special:OATH while enabling OATH.",
+ "oathauth-token": "HTMLForm label, found on [[Special:OATH]], when verifying OATH.\n{{Identical|Token}}",
+ "oathauth-disable": "Page title on Special:OATH while disabling OATH.\n\nSee [https://en.wikipedia.org/wiki/Two_factor_authentication two factor authentication]",
+ "oathauth-validatedoath": "Plain text found on Special:OATH after a token has been validated.\n\nSee [https://en.wikipedia.org/wiki/Two_factor_authentication two factor authentication]",
+ "oathauth-noscratchforvalidation": "Plain text found on Special:OATH if the user used the incorrect type of token while enabling OATH.\n\nSee [https://en.wikipedia.org/wiki/Two_factor_authentication two factor authentication]",
+ "oathauth-failedtovalidateoath": "Plain text found on Special:OATH when validation of a token has failed.\n\nSee [https://en.wikipedia.org/wiki/Two_factor_authentication two factor authentication]",
+ "oathauth-disabledoath": "Plain text found on Special:OATH when disabling OATH has been successful.\n\nSee [https://en.wikipedia.org/wiki/Two_factor_authentication two factor authentication]",
+ "oathauth-prefs-label": "Plain text label seen on Special:Preferences\n\nSee [https://en.wikipedia.org/wiki/Two_factor_authentication two factor authentication]\n{{Identical|Two factor authentication}}",
+ "oathauth-step1": "Label for step 1 on Special:OATH form",
+ "oathauth-step1-test": "Text for step 1 on Special:OATH for. Check the name of \"Google Authenticator\" in your language in the Play Market—it's quite likely that it's translated.",
+ "oathauth-step2": "Label for step 2, the QR code, on Special:OATH",
+ "oathauth-step2alt": "Label for information on how to manually do step 2 on Special:OATH",
+ "oathauth-step3": "Label for step 3 information on Special:OATH",
+ "oathauth-step4": "Label for step 4 information on Special:OATH",
+ "oathauth-entertoken": "Label on input field on Special:OATH asking user to enter token",
+ "right-oathauth-enable": "{{doc-right|oathauth-enable}}",
+ "action-oathauth-enable": "{{doc-action|oathauth-enable}}",
+ "oathauth-auth-token-label": "Label of the second-factor field on special pages and in the API\n{{Identical|Token}}",
+ "oathauth-auth-token-help": "Extended help message for the second factor field in the API.",
+ "oathauth-auth-ui": "Shown on top of the login form when second factor is required",
+ "oathauth-throttled": "Error message when throttling limit is hit.\n\nParameters:\n* $1 - throttle block duration",
+ "oathauth-login-failed": "Error message when verifying the second factor failed.",
+ "oathauth-describe-provider": "Description of the extension as an authentication provider.",
+ "grant-group-authentication": "{{Related|Grant-group}}",
+ "grant-oath": "Name for grant \"oath\".\n{{Related|Grant}}",
+ "right-oathauth-api-all": "{{doc-right|oathauth-api-all}}",
+ "action-oathauth-api-all": "{{doc-action|oathauth-api-all}}",
+ "apihelp-query+oath-description": "{{doc-apihelp-description|query+oath}}",
+ "apihelp-query+oath-summary": "{{doc-apihelp-summary|query+oath}}",
+ "apihelp-query+oath-param-user": "{{doc-apihelp-param|query+oath|user}}",
+ "apihelp-query+oath-example-1": "{{doc-apihelp-example|query+oath}}",
+ "apihelp-query+oath-example-2": "{{doc-apihelp-example|query+oath}}",
+ "apihelp-oathvalidate-description": "{{doc-apihelp-description|oathvalidate}}",
+ "apihelp-oathvalidate-summary": "{{doc-apihelp-summary|oathvalidate}}",
+ "apihelp-oathvalidate-param-user": "{{doc-apihelp-param|oathvalidate|user}}",
+ "apihelp-oathvalidate-param-totp": "{{doc-apihelp-param|oathvalidate|totp}}",
+ "apihelp-oathvalidate-example-1": "{{doc-apihelp-example|oathvalidate}}",
+ "apihelp-oathvalidate-example-2": "{{doc-apihelp-example|oathvalidate}}"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/ro.json b/www/wiki/extensions/OATHAuth/i18n/ro.json
new file mode 100644
index 00000000..c368abda
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/ro.json
@@ -0,0 +1,16 @@
+{
+ "@metadata": {
+ "authors": [
+ "Firilacroco",
+ "Stelistcristi",
+ "Narlotep1",
+ "Strainu"
+ ]
+ },
+ "specialpages-group-oath": "Autentificare pe două niveluri",
+ "oathauth-account": "Numele contului:",
+ "oathauth-token": "Jeton",
+ "oathauth-prefs-label": "Autentificare pe două niveluri:",
+ "oathauth-step4": "Pasul 4: Verificare",
+ "oathauth-auth-ui": "Va rugam introduceti codul de autentificare de pe dispozitivul dumneavoastra"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/roa-tara.json b/www/wiki/extensions/OATHAuth/i18n/roa-tara.json
new file mode 100644
index 00000000..12edc7bb
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/roa-tara.json
@@ -0,0 +1,16 @@
+{
+ "@metadata": {
+ "authors": [
+ "Joetaras"
+ ]
+ },
+ "specialpages-group-oath": "Autendicazione a Doje Fattore",
+ "oathauth-account": "Nome d'u cunde:",
+ "oathauth-secret": "Chiave segrete de autendicazione a Doje Fattore:",
+ "oathauth-enable": "Abbilite l'Autendicazione a Doje Fattore",
+ "oathauth-token": "Gettone",
+ "oathauth-disable": "Disabbilite l'Autendicazione a Doje Fattore",
+ "oathauth-auth-token-label": "Gettone",
+ "apihelp-oathvalidate-example-1": "Valide 'nu gettone pe l'utende de mò",
+ "apihelp-oathvalidate-example-2": "Valide 'nu gettone pe l'utende <kbd>Esembie</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/ru.json b/www/wiki/extensions/OATHAuth/i18n/ru.json
new file mode 100644
index 00000000..df663213
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/ru.json
@@ -0,0 +1,57 @@
+{
+ "@metadata": {
+ "authors": [
+ "Okras",
+ "Kareyac",
+ "Iniquity",
+ "Ping08",
+ "Alexandr Efremov",
+ "NBS",
+ "Mouse21",
+ "Patrick Star"
+ ]
+ },
+ "oathauth-desc": "Обеспечивает поддержку проверки подлинности с помощью HMAC на основе одноразовых паролей",
+ "specialpages-group-oath": "Двухфакторная аутентификация",
+ "oathauth-account": "Имя учётной записи:",
+ "oathauth-secret": "Двухфакторный секретный ключ аутентификации:",
+ "oathauth-enable": "Включить двухфакторную аутентификацию",
+ "oathauth-scratchtokens": "Это — список одноразовых токенов. Эти токены могут быть использованы только один раз и предназначены для использования в чрезвычайных ситуациях. Пожалуйста, запишите их и хранить в безопасном месте. Если вы потеряете свой телефон, они будут единственным способом спасти ваш аккаунт. '''Эти токены больше никогда не будут показаны'''.",
+ "oathauth-token": "Токен",
+ "oathauth-disable": "Отключить двухфакторную аутентификацию",
+ "oathauth-validatedoath": "Двухфакторные учётные данные проверены. Теперь будет использоваться двухфакторная аутентификация.",
+ "oathauth-noscratchforvalidation": "Вы не можете использовать код scratch для подтверждения двухфакторной аутентификации. Коды scratch предназначены только для резервного копирования и случайного использования. Пожалуйста, используйте код подтверждения от вашего генератора кода.",
+ "oathauth-failedtovalidateoath": "Не удалось проверить двухфакторные учётные данные",
+ "oathauth-disabledoath": "Двухфакторная аутентификация отключена.",
+ "oathauth-prefs-label": "Двухфакторная аутентификация:",
+ "oathauth-step1": "Шаг 1: Загрузите программу двухфакторной аутентификации",
+ "oathauth-step1-test": "Загрузите программу для двухфакторной аутентификации. Это может быть мобильное приложение (например, Google Authenticator) или настольное приложение",
+ "oathauth-step2": "Шаг 2: Отсканируйте QR-код",
+ "oathauth-step2alt": "Или введите секретный код вручную:",
+ "oathauth-step3": "Шаг 3: Запишите скретч коды",
+ "oathauth-step4": "Шаг 4: Проверка",
+ "oathauth-entertoken": "Введите код с вашего устройства проверки подлинности, чтобы проверить:",
+ "right-oathauth-enable": "включение двухфакторной аутентификации",
+ "action-oathauth-enable": "включить двухфакторную аутентификацию",
+ "oathauth-auth-token-label": "Токен",
+ "oathauth-auth-token-help": "Одноразовый пароль используется как второй фактор двухфакторной аутентификации.",
+ "oathauth-auth-ui": "Пожалуйста, введите код подтверждения из устройства проверки подлинности",
+ "oathauth-throttled": "Слишком много попыток проверки! пожалуйста, подождите $1.",
+ "oathauth-login-failed": "Не удалось выполнить проверку.",
+ "oathauth-describe-provider": "Двухфакторная аутентификация (OATH).",
+ "grant-group-authentication": "Выполнять действия, связанные с проверкой подлинности для себя и других",
+ "grant-oath": "Доступ к двухфакторной аутентификации (OAUTH) для себя и других",
+ "right-oathauth-api-all": "Запрос и проверка информации OAUTH для себя и других",
+ "action-oathauth-api-all": "Проверить статус OATH",
+ "apihelp-query+oath-description": "Проверьте, включена ли двухфакторная аутентификация (OATH) для пользователя.",
+ "apihelp-query+oath-summary": "Проверьте, включена ли двухфакторная аутентификация (OATH) для пользователя.",
+ "apihelp-query+oath-param-user": "Пользователь для получения информации о. По умолчанию для текущего пользователя.",
+ "apihelp-query+oath-example-1": "Получить информацию о текущем пользователе",
+ "apihelp-query+oath-example-2": "Получать информацию о пользователе <kbd>Пример</kbd>",
+ "apihelp-oathvalidate-description": "Подтвердите двухфакторный токен аутентификации (OAUTH).",
+ "apihelp-oathvalidate-summary": "Подтвердите двухфакторный токен аутентификации (OAUTH).",
+ "apihelp-oathvalidate-param-user": "Пользователь для проверки токена для. По умолчанию для текущего пользователя.",
+ "apihelp-oathvalidate-param-totp": "Токен двухфакторной аутентификации (OAUTH).",
+ "apihelp-oathvalidate-example-1": "Проверка токена для текущего пользователя",
+ "apihelp-oathvalidate-example-2": "Проверить токен для пользователя <kbd>пример</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/sa.json b/www/wiki/extensions/OATHAuth/i18n/sa.json
new file mode 100644
index 00000000..175b9d05
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/sa.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "NehalDaveND"
+ ]
+ },
+ "oathauth-token": "स्तोकम् (token)"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/sah.json b/www/wiki/extensions/OATHAuth/i18n/sah.json
new file mode 100644
index 00000000..f0bebe83
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/sah.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Марианна"
+ ]
+ },
+ "oathauth-auth-token-label": "Токен"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/sd.json b/www/wiki/extensions/OATHAuth/i18n/sd.json
new file mode 100644
index 00000000..a5c4599f
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/sd.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Mehtab ahmed"
+ ]
+ },
+ "action-oathauth-api-all": "OATH جو مرتبو چڪاسيو"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/si.json b/www/wiki/extensions/OATHAuth/i18n/si.json
new file mode 100644
index 00000000..e6ed694a
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/si.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "පසිඳු කාවින්ද"
+ ]
+ },
+ "specialpages-group-oath": "ද්විසාධක සහතික කිරීම",
+ "oathauth-account": "ද්විසාධක ගිණුමේ නාමය:",
+ "oathauth-secret": "ද්විසාධක රහස් යතුර:",
+ "oathauth-enable": "ද්විසාධක සහතික කිරීම සක්‍රිය කරන්න",
+ "oathauth-token": "ටෝකනය",
+ "oathauth-disable": "ද්විසාධක සහතික කිරීම අක්‍රිය කරන්න",
+ "oathauth-disabledoath": "ද්විසාධක සහතික කිරීම අක්‍රිය කර ඇත."
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/sk.json b/www/wiki/extensions/OATHAuth/i18n/sk.json
new file mode 100644
index 00000000..6187b90d
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/sk.json
@@ -0,0 +1,38 @@
+{
+ "@metadata": {
+ "authors": [
+ "Teslaton"
+ ]
+ },
+ "oathauth-desc": "Poskytuje podporu pre autentifikáciu pomocou jednorázových hesiel, založených na HMAC",
+ "specialpages-group-oath": "Dvojfaktorová autentifikácia",
+ "oathauth-account": "Názov účtu:",
+ "oathauth-secret": "Tajný kľúč pre dvojfaktorovú autentifikáciu:",
+ "oathauth-enable": "Zapnutie dvojfaktorovej autentifikácie",
+ "oathauth-scratchtokens": "Nasledovný zoznam obsahuje jednorazové kódy. Tieto kódy možno použiť iba raz a slúžia pre prípad núdze. Opíšte si ich a uchovávajte ich na bezpečnom mieste. Ak stratíte svoj telefón, budú tieto kódy jediným spôsobom, ako zachrániť svoj účet. Tieto kódy sa už nikdy opätovne nezobrazia.",
+ "oathauth-token": "Kód",
+ "oathauth-disable": "Vypnutie dvojfaktorovej autentifikácie",
+ "oathauth-validatedoath": "Dvojfaktorové prihlásenie overené. Odteraz bude vynucovaná dvojfaktorová autentifikácia.",
+ "oathauth-failedtovalidateoath": "Nepodarilo se overiť dvojfaktorové prihlásenie.",
+ "oathauth-disabledoath": "Dvojfaktorová autentifikácia vypnutá.",
+ "oathauth-prefs-label": "Dvojfaktorová autentifikácia:",
+ "oathauth-step1": "Krok 1: Stiahnite si program pre dvojfaktorovú autentifikáciu",
+ "oathauth-step1-test": "Stiahnite si program alebo mobilnú aplikáciu (ako napr. Google Authenticator) pre dvojfaktorovú autentifikáciu",
+ "oathauth-step2": "Krok 2: Naskenujte QR kód",
+ "oathauth-step2alt": "Alebo zadajte tajomstvo manuálne:",
+ "oathauth-step3": "Krok 3: Zapíšte si jednorazové kódy",
+ "oathauth-step4": "Krok 4: Overenie",
+ "oathauth-entertoken": "Pre overenie zadajte kód z vášho autentifikačného zariadenia:",
+ "right-oathauth-enable": "Zapnutie dvojfaktorovej autentifikácie",
+ "action-oathauth-enable": "zapnúť dvojfaktorovú autentifikáciu",
+ "oathauth-auth-token-label": "Kód",
+ "oathauth-auth-token-help": "Jednorazové heslo používané ako druhý faktor dvojfaktorovej autentifikácie.",
+ "oathauth-auth-ui": "Zadajte prosím overovací kód zo svojho autentifikačného zariadenia",
+ "oathauth-throttled": "Príliš veľa pokusov o overenie! Počkajte prosím $1.",
+ "oathauth-login-failed": "Overenie sa nepodarilo.",
+ "oathauth-describe-provider": "Dvojfaktorová autentifikácia (OATH).",
+ "grant-group-authentication": "Vykonávanie autentifikačných akcií pre seba alebo pre ostatných",
+ "grant-oath": "Prístup k informáciám o dvojfaktorovej autentifikácii (OATH) k sebe a ostatným",
+ "right-oathauth-api-all": "Zisťovanie a overovanie informácií týkajúcich sa OATH k sebe a ostatným",
+ "action-oathauth-api-all": "kontrolovať stav OATH"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/sq.json b/www/wiki/extensions/OATHAuth/i18n/sq.json
new file mode 100644
index 00000000..883ade89
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/sq.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Liridon"
+ ]
+ },
+ "oathauth-account": "Emri i llogarisë",
+ "oathauth-auth-ui": "Ju lutemi shkruani kodin e verifikimit tuaj nga pajisje të legalizuara"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/sr-ec.json b/www/wiki/extensions/OATHAuth/i18n/sr-ec.json
new file mode 100644
index 00000000..5c193780
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/sr-ec.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Milicevic01",
+ "Obsuser"
+ ]
+ },
+ "oathauth-token": "Жетон",
+ "oathauth-noscratchforvalidation": "Не можете да користите скреч код за потврђивање двофакторске аутентификације. Скреч кодови су за подршку и случајну употребу само. Користите верификациони код из свог генератора кода."
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/sr-el.json b/www/wiki/extensions/OATHAuth/i18n/sr-el.json
new file mode 100644
index 00000000..65bea883
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/sr-el.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Milicevic01"
+ ]
+ },
+ "oathauth-token": "Žeton"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/sv.json b/www/wiki/extensions/OATHAuth/i18n/sv.json
new file mode 100644
index 00000000..1406e71a
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/sv.json
@@ -0,0 +1,32 @@
+{
+ "@metadata": {
+ "authors": [
+ "WikiPhoenix",
+ "Lokal Profil"
+ ]
+ },
+ "oathauth-desc": "Ger autentiseringsstöd med HMAC-baserade engångslösenord",
+ "specialpages-group-oath": "Tvåstegsverifiering",
+ "oathauth-account": "Kontonamn:",
+ "oathauth-secret": "Tvåstegs-hemlig nyckel:",
+ "oathauth-enable": "Aktivera tvåstegsverifiering",
+ "oathauth-scratchtokens": "Följande lista är en förteckning över engångsnycklar. Dessa nycklar kan enbart användas en gång och är avsedda för användning i nödfall. Vänligen skriv ner dessa och förvara dem på en säker plats. Om du förlorar din telefon, är dessa nycklar det enda sättet att rädda ditt konto. Dessa nycklar kommer aldrig att visas igen.",
+ "oathauth-token": "Nyckel",
+ "oathauth-disable": "Inaktivera tvåstegsverifiering",
+ "oathauth-validatedoath": "Validerade tvåstegs-autentiseringsuppgifter. Tvåstegsverifiering kommer nu att verkställas.",
+ "oathauth-failedtovalidateoath": "Misslyckades med att validera tvåstegs-autentiseringsuppgifter",
+ "oathauth-disabledoath": "Inaktivera tvåstegsverifiering.",
+ "oathauth-prefs-label": "Tvåstegsverifiering:",
+ "oathauth-step1": "Steg 1: Ladda ned appen",
+ "oathauth-step2": "Steg 2: Skanna QR-koden",
+ "oathauth-step4": "Steg 4: Verifiering",
+ "oathauth-auth-token-label": "Nyckel",
+ "oathauth-auth-ui": "Ange verifieringskoden från din autentiseringsenhet",
+ "oathauth-login-failed": "Verifiering misslyckades.",
+ "grant-group-authentication": "Använd OAuth för autentisering",
+ "action-oathauth-api-all": "kolla OATH-status",
+ "apihelp-query+oath-example-1": "Skaffa information om den aktuella användaren.",
+ "apihelp-query+oath-example-2": "Hämta information om användaren <kbd>Example</kbd>",
+ "apihelp-oathvalidate-example-1": "Bekräfta en nyckel för aktuell användare",
+ "apihelp-oathvalidate-example-2": "Bekräfta en nyckel för användaren <kbd>Example</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/ta.json b/www/wiki/extensions/OATHAuth/i18n/ta.json
new file mode 100644
index 00000000..a4885d2e
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/ta.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Karthi.dr"
+ ]
+ },
+ "oathauth-notloggedin": "புகுபதிகை செய்யப்பட வேண்டும்"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/te.json b/www/wiki/extensions/OATHAuth/i18n/te.json
new file mode 100644
index 00000000..5bbfee86
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/te.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Jedimaster26"
+ ]
+ },
+ "action-oathauth-api-all": "OATH స్థితి గమనించండి"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/th.json b/www/wiki/extensions/OATHAuth/i18n/th.json
new file mode 100644
index 00000000..70e32bc6
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/th.json
@@ -0,0 +1,21 @@
+{
+ "@metadata": {
+ "authors": [
+ "Aefgh39622"
+ ]
+ },
+ "oathauth-desc": "จัดหาการรองรับการยืนยันตัวตนโดยใช้รหัสผ่านแบบครั้งเดียวบนพื้นฐานของ HMAC",
+ "specialpages-group-oath": "การยืนยันตัวตนสองขั้นตอน",
+ "oathauth-account": "ชื่อบัญชี:",
+ "oathauth-secret": "คีย์ลับการยืนยันตัวตันสองขั้นตอน:",
+ "oathauth-enable": "เปิดใช้งานการยืนยันตัวตนสองขั้นตอน",
+ "oathauth-scratchtokens": "รายการต่อไปนี้เป็นรายการโทเค็นชั่วคราวสำหรับใช้ครั้งเดียว โทเค็นเหล่านี้สามารถใช้ได้เพียงครั้งเดียวเท่านั้น และใช้ในยามฉุกเฉิน กรุณาจดโทเค็นเหล่านี้ไว้และเก็บไว้ในที่ที่ปลอดภัย ถ้าโทรศัพท์ของคุณสูญหาย โทเค็นเหล่านี้จะเป็นเพียงวิธีเดียวในการช่วยเหลือบัญชีของคุณ '''โทเค็นเหล่านี้จะไม่ถูกแสดงอีก'''",
+ "oathauth-token": "โทเค็น",
+ "oathauth-disable": "ปิดใช้งานการยืนยันตัวตนสองขั้นตอน",
+ "oathauth-validatedoath": "ตรวจสอบข้อมูลประจำตัวแบบสองขั้นตอนแล้ว คุณจะถูกบังคับให้ทำการยืนยันตัวตนสองขั้นตอน",
+ "oathauth-failedtovalidateoath": "ไม่สามารถตรวจสอบข้อมูลประจำตัวแบบสองขั้นตอนได้",
+ "oathauth-disabledoath": "ปิดใช้งานการยืนยันตัวตนสองขั้นตอนแล้ว",
+ "oathauth-prefs-label": "การยืนยันตัวตนสองขั้นตอน:",
+ "right-oathauth-enable": "เปิดใช้งานการยืนยันตัวตนสองขั้นตอน",
+ "action-oathauth-enable": "เปิดใช้งานการยืนยันตัวตนสองขั้นตอน"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/tl.json b/www/wiki/extensions/OATHAuth/i18n/tl.json
new file mode 100644
index 00000000..fc64aeec
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/tl.json
@@ -0,0 +1,20 @@
+{
+ "@metadata": {
+ "authors": [
+ "AnakngAraw",
+ "Emem.calist"
+ ]
+ },
+ "oathauth-desc": "Nagbibigay ng suporta ng pagpapatunay sa pamamagitan ng pang-isang ulit na mga hudyat na nakabatay sa HMAC",
+ "specialpages-group-oath": "Dalawang Salik na Pagpapatunay",
+ "oathauth-account": "Dalawang Salik na Pangalan ng Akawnt:",
+ "oathauth-secret": "Dalawang Salik na Susi ng Lihim:",
+ "oathauth-enable": "Paganahin ang Dalawang Salik na Pagpapatunay",
+ "oathauth-scratchtokens": "Ang sumusunod na lista ay isang listahan ng mga pang-isahang ulit na paggamit na mga panghalip na nagagasgas. Ang mga panghalip na ito ay magagamit lamang nang isang beses, at mga para sa paggamit na pangkagipitan. Pakisulat ang mga ito at itabi sa isang ligtas na lugar. Kapag naiwala mo ang telepono mo, ang mga panghalip na ito lang ang makasasaklolo sa akawnt mo. Hindi na muling ipapakita pa ang mga panghalip na ito.",
+ "oathauth-token": "Kahalip",
+ "oathauth-disable": "Huwag Paganahin ang Dalawang Salik na Pagpapatunay",
+ "oathauth-validatedoath": "Nakapagpatunay ng dalawang salik na mga katibayan. Isasakatuparan na ngayon ang dalawang salik na pagpapatunay.",
+ "oathauth-failedtovalidateoath": "Nabigo sa pagpapatunay ng dalawang salik na mga kredensiyal",
+ "oathauth-disabledoath": "Hindi na pinaaandar ang dalawang salik na pagpapatunay.",
+ "apihelp-query+oath-example-1": "Maghanap ng kaukulang impormasyon tungkol sa 'current user'"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/tr.json b/www/wiki/extensions/OATHAuth/i18n/tr.json
new file mode 100644
index 00000000..7c2c555c
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/tr.json
@@ -0,0 +1,26 @@
+{
+ "@metadata": {
+ "authors": [
+ "Incelemeelemani",
+ "Hbseren",
+ "Sayginer",
+ "HakanIST"
+ ]
+ },
+ "oathauth-desc": "HMAC tabanlı tek kullanımlık şifreler kullanan bir kimlik doğrulama desteği sağlar",
+ "specialpages-group-oath": "İki faktörlü kimlik doğrulama",
+ "oathauth-account": "İki Faktörlü Hesap Adı:",
+ "oathauth-secret": "İki Faktörlü Gizli Anahtar:",
+ "oathauth-enable": "İki Faktörlü Doğrulamayı Etkinleştirin",
+ "oathauth-scratchtokens": "Aşağıdaki listede, yalnızca bir defa kullanımlık anahtar bulunmaktadır. Bu semboller yalnızca bir defalık kullanım içindir ve acil durum kullanımlarına özel olabilir. Lütfen aşağıda yazılı anahtarı bir yere yazın ve bunu güvenli bir yerde saklayın. Eğer telefonunuzu kaybederseniz, bu simgeler hesabınızı kurtarmanın tek yolu olacaktır. Bu simgeler asla tekrar gösterilmeyecektir.",
+ "oathauth-token": "Anahtar",
+ "oathauth-disable": "İki Faktörlü Kimlik Doğrulama Devre Dışı",
+ "oathauth-validatedoath": "İki Faktörlü kimlik onaylandı. İki faktörlü kimlik doğrulama artık etkinleştirilecektir.",
+ "oathauth-failedtovalidateoath": "İki faktörlü kimlik doğrulama başarısız oldu",
+ "oathauth-disabledoath": "İki faktörlü kimlik doğrulama devre dışı.",
+ "oathauth-prefs-label": "İki faktörlü kimlik doğrulama:",
+ "oathauth-step1": "1. Adım: Uygulamayı indirin",
+ "oathauth-step2": "2. Adım: QR kodunu tarayın",
+ "oathauth-step4": "4. Adım: Doğrulama",
+ "grant-group-authentication": "Kimlik doğrulama işlemlerini kendin ve diğerleri için uygula"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/uk.json b/www/wiki/extensions/OATHAuth/i18n/uk.json
new file mode 100644
index 00000000..fd84b820
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/uk.json
@@ -0,0 +1,52 @@
+{
+ "@metadata": {
+ "authors": [
+ "Andriykopanytsia",
+ "Piramidion",
+ "Ата"
+ ]
+ },
+ "oathauth-desc": "Забезпечує підтримку автентифікації за допомогою HMAC на основі одноразових паролів",
+ "specialpages-group-oath": "Двофакторна авторизація",
+ "oathauth-account": "Назва облікового запису:",
+ "oathauth-secret": "Секретний ключ двофакторної автентифікації:",
+ "oathauth-enable": "Увімкнути двофакторну авторизацію",
+ "oathauth-scratchtokens": "У цій таблиці наведено список одноразових кодів. Ці коди можна використовувати лише один раз. Вони призначені для використання в надзвичайних ситуаціях. Будь ласка, запишіть їх і зберігайте у безпечному місці. Якщо ваш телефон загубиться, ці коди єдиний спосіб врятувати ваш обліковий запис. Ці коди ніколи не з'являться знову.",
+ "oathauth-token": "Код",
+ "oathauth-disable": "Вимкнути двофакторну авторизацію",
+ "oathauth-validatedoath": "Перевірено двофакторні облікові дані. Двофакторна авторизація тепер буде застосована.",
+ "oathauth-noscratchforvalidation": "Ви не можете скористатись скретч-кодом для підтвердження двофакторної автентифікації. Скретч-коди призначені лише для використання як резервної копії або при виникненні незвичних інцидентів. Будь ласка, використовуйте код верифікації з Вашого генератора коду.",
+ "oathauth-failedtovalidateoath": "Не вдалося перевірити двофакторні повноваження",
+ "oathauth-disabledoath": "Вимкнено двофакторну авторизацію.",
+ "oathauth-prefs-label": "Двофакторна авторизація:",
+ "oathauth-step1": "Крок 1: Завантажте програму з двофакторної автентифікації",
+ "oathauth-step1-test": "Завантажте програму для двофакторної автентифікації. Це може бути мобільний застосунок (напр., Google Authenticator), або програма для комп'ютера.",
+ "oathauth-step2": "Крок 2: Зіскануйте QR-код",
+ "oathauth-step2alt": "Або введіть ключ вручну:",
+ "oathauth-step3": "Крок 3: Запишіть скретч-коди",
+ "oathauth-step4": "Крок 4: Перевірка",
+ "oathauth-entertoken": "Введіть код зі свого пристрою автентифікації для перевірки:",
+ "right-oathauth-enable": "вмикання двофакторної авторизації",
+ "action-oathauth-enable": "вмикання двофакторної авторизації",
+ "oathauth-auth-token-label": "Токен",
+ "oathauth-auth-token-help": "Одноразовий пароль використовується як другий фактор двофакторної автентифікації.",
+ "oathauth-auth-ui": "Будь ласка, введіть код верифікації з Вашого пристрою автентифікації",
+ "oathauth-throttled": "Забагато спроб верифікації! Будь ласка, зачекайте $1.",
+ "oathauth-login-failed": "Верифікація не вдалася.",
+ "oathauth-describe-provider": "Двофакторна автентифікація (OATH).",
+ "grant-group-authentication": "Виконати дії з автентифікації для себе й інших",
+ "grant-oath": "Отримати доступ до інформації про двофакторну автентифікацію (OATH) для себе й інших",
+ "right-oathauth-api-all": "Запитати та перевірити інформацію щодо OATH для себе та інших",
+ "action-oathauth-api-all": "перевірку статусу OATH",
+ "apihelp-query+oath-description": "Поставити позначку, щоб побачити, чи двофакторна автентифікація (OATH) увімкнена для користувача.",
+ "apihelp-query+oath-summary": "Проведіть перевірку, щоб побачити, чи двофакторна автентифікація (OATH) увімкнена для користувача.",
+ "apihelp-query+oath-param-user": "Користувач, про якого треба отримати інформацію. За замовчуванням ним є поточний користувач.",
+ "apihelp-query+oath-example-1": "Отримати інформацію про поточного користувача",
+ "apihelp-query+oath-example-2": "Отримати інформацію про користувача <kbd>Example</kbd>",
+ "apihelp-oathvalidate-description": "Перевірити токен двофакторної автентифікації (OATH).",
+ "apihelp-oathvalidate-summary": "Перевірити токен двофакторної автентифікації (OATH).",
+ "apihelp-oathvalidate-param-user": "Користувач, для якого треба перевірити токен. За замовчуванням ним є поточний користувач.",
+ "apihelp-oathvalidate-param-totp": "Токен двофакторної автентифікації (OATH).",
+ "apihelp-oathvalidate-example-1": "Перевірити токен для поточного користувача",
+ "apihelp-oathvalidate-example-2": "Перевірити токен для користувача <kbd>Example</kbd>"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/ur.json b/www/wiki/extensions/OATHAuth/i18n/ur.json
new file mode 100644
index 00000000..d60ea8dc
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/ur.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "පසිඳු කාවින්ද",
+ "Muhammad Shuaib"
+ ]
+ },
+ "oathauth-enable": "دو مرحلہ تصدیق فعال کریں",
+ "oathauth-disable": "دو مرحلہ تصدیق غیر فعال کریں",
+ "oathauth-disabledoath": "معذور تصدیق دو عنصر.",
+ "oathauth-prefs-label": "دو مرحلہ تصدیق:",
+ "right-oathauth-enable": "دو مرحلہ تصدیق کا نفاذ",
+ "action-oathauth-enable": "دو مرحلہ تصدیق فعال کرنے"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/vi.json b/www/wiki/extensions/OATHAuth/i18n/vi.json
new file mode 100644
index 00000000..8af48836
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/vi.json
@@ -0,0 +1,40 @@
+{
+ "@metadata": {
+ "authors": [
+ "Minh Nguyen"
+ ]
+ },
+ "oathauth-desc": "Cho phép xác nhận dùng các mật khẩu một lần dựa trên mã xác thực thông điệp được băm (HMAC)",
+ "oath": "Xác thực OATH",
+ "specialpages-group-oath": "Xác thực dùng hai nhân tố",
+ "oathauth-account": "Tên tài khoản:",
+ "oathauth-secret": "Chìa khóa bí mật để xác thực dùng hai nhân tố:",
+ "oathauth-enable": "Xác thực dùng hai nhân tố",
+ "oathauth-scratchtokens": "Danh sách sau có các mã dùng một lần. Các mã này chỉ có thể dùng một lần cho mục đích khẩn cấp. Xin hãy ghi xuống các mã này và lưu giữ chúng tại một nơi an toàn. Nếu bạn mất điện thoại của bạn, các mã này là phương sách duy nhất để cứu tài khoản của bạn. Từ nay các mã này sẽ không bao giờ được hiển thị.",
+ "oathauth-token": "Mã",
+ "oathauth-disable": "Tắt xác thực dùng hai nhân tố",
+ "oathauth-validatedoath": "Đã xác nhận thông tin xác thực dùng hai nhân tố. Từ nay sẽ áp dụng việc xác thực dùng hai nhân tố.",
+ "oathauth-noscratchforvalidation": "Bạn không thể sử dụng mã thẻ để xác thực dùng hai nhân tố. Các mã thẻ chỉ để sao lưu hoặc sử dụng tình cờ. Xin hãy nhập mã xác nhận từ trình xác thực của bạn.",
+ "oathauth-failedtovalidateoath": "Thất bại khi xác nhận thông tin xác thực dùng hai nhân tố",
+ "oathauth-disabledoath": "Đã tắt xác thực dùng hai nhân tố.",
+ "oathauth-prefs-label": "Xác thực dùng hai nhân tố:",
+ "oathauth-step1": "Bước 1: Tải về ứng dụng xác thực dùng hai nhân tố",
+ "oathauth-step1-test": "Tải một chương trình có chức năng xác thực dùng hai nhân tố, có thể là ứng dụng di động như trình xác thực Google Authenticator hoặc một ứng dụng dành cho máy tính để bàn.",
+ "oathauth-step2": "Bước 2: Quét mã QR",
+ "oathauth-step2alt": "Hoặc nhập mật mã thủ công:",
+ "oathauth-step3": "Bước 3: Ghi xuống các mã thẻ",
+ "oathauth-step4": "Bước 4: Xác nhận",
+ "oathauth-entertoken": "Nhập một mã từ thiết bị xác thực của bạn để xác nhận:",
+ "right-oathauth-enable": "Xác thực dùng hai nhân tố",
+ "action-oathauth-enable": "xác thực dùng hai nhân tố",
+ "oathauth-auth-token-label": "Mã",
+ "oathauth-auth-token-help": "Mật khẩu dùng một lần được sử dụng làm nhân tố thứ hai trong quá trình xác thực dùng hai nhân tố.",
+ "oathauth-auth-ui": "Xin hãy nhập mã xác nhận từ máy xác thực của bạn",
+ "oathauth-throttled": "Đã cố xác nhận danh tính nhiều lần quá! Xin hãy chờ $1.",
+ "oathauth-login-failed": "Xác nhận thất bại.",
+ "oathauth-describe-provider": "Xác thực dùng hai nhân tố (OATH).",
+ "grant-group-authentication": "Thực hiện các tác vụ xác thực cho chính mình và những người khác",
+ "grant-oath": "Truy cập thông tin xác thực dùng hai nhân tố (OATH) về chính mình và những người khác",
+ "right-oathauth-api-all": "Truy vấn và xác nhận thông tin OATH về chính mình và những người khác",
+ "action-oathauth-api-all": "kiểm tra trạng thái OATH"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/yi.json b/www/wiki/extensions/OATHAuth/i18n/yi.json
new file mode 100644
index 00000000..e4f926bd
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/yi.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "פוילישער"
+ ]
+ },
+ "oathauth-account": "קאנטע נאמען:"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/zh-hans.json b/www/wiki/extensions/OATHAuth/i18n/zh-hans.json
new file mode 100644
index 00000000..6c2a94bc
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/zh-hans.json
@@ -0,0 +1,53 @@
+{
+ "@metadata": {
+ "authors": [
+ "Hzy980512",
+ "Liuxinyu970226",
+ "Xiaomingyan",
+ "Yfdyh000"
+ ]
+ },
+ "oathauth-desc": "提供使用基于HMAC的一次性密码的身份验证支持",
+ "specialpages-group-oath": "双因素验证",
+ "oathauth-account": "账户名称:",
+ "oathauth-secret": "双因素验证密钥:",
+ "oathauth-enable": "启用双因素验证",
+ "oathauth-scratchtokens": "下面的列表是一次性使用的即时令牌列表。这些令牌只能使用一次,并应用于紧急用途。请将它们记下来,并保存在一个安全的位置。如果您遗失了手机,唯一能够找回您账户的方法就是这些令牌。'''这些令牌不会再显示'''。",
+ "oathauth-token": "令牌",
+ "oathauth-disable": "禁用双因素验证",
+ "oathauth-validatedoath": "已验证双因素证书。双因素验证现将实施生效。",
+ "oathauth-noscratchforvalidation": "您不能使用乱码确认双因素验证。乱码只用于备份及附带使用。请使用来自您的代码生成器的确认代码。",
+ "oathauth-failedtovalidateoath": "验证双因素凭据失败",
+ "oathauth-disabledoath": "已禁用双因素验证。",
+ "oathauth-prefs-label": "双因素验证:",
+ "oathauth-step1": "第1步:下载双因素验证程序",
+ "oathauth-step1-test": "下载用于双因素验证的程序。这可以是移动应用(例如Google Authenticator)或桌面端应用",
+ "oathauth-step2": "第2步:扫描二维码",
+ "oathauth-step2alt": "或手动输入密钥:",
+ "oathauth-step3": "第3步:记下即时性代码",
+ "oathauth-step4": "第4步:验证",
+ "oathauth-entertoken": "输入来自您的验证设备的代码进行验证:",
+ "right-oathauth-enable": "启用双因素验证",
+ "action-oathauth-enable": "启用双因素验证",
+ "oathauth-auth-token-label": "令牌",
+ "oathauth-auth-token-help": "用作双因素验证中第二因素的一次性密码。",
+ "oathauth-auth-ui": "请输入来自您的验证设备的验证码",
+ "oathauth-throttled": "尝试了太多次验证!请等待$1。",
+ "oathauth-login-failed": "验证失败。",
+ "oathauth-describe-provider": "双因素验证(OATH)。",
+ "grant-group-authentication": "为自己和他人执行身份验证操作",
+ "grant-oath": "访问自己和他人的双因素验证(OATH)信息",
+ "right-oathauth-api-all": "查询并验证自己和他人的OATH信息",
+ "action-oathauth-api-all": "检查OATH状态",
+ "apihelp-query+oath-description": "检查是否为某一用户启用双因素验证(OATH)。",
+ "apihelp-query+oath-summary": "检查双因素验证(OATH)是否为某一用户启用。",
+ "apihelp-query+oath-param-user": "获取相关信息的用户。默认为当前用户。",
+ "apihelp-query+oath-example-1": "获取有关当前用户的信息",
+ "apihelp-query+oath-example-2": "获取有关用户<kbd>Example</kbd>的信息",
+ "apihelp-oathvalidate-description": "验证一个双因素验证(OATH)令牌。",
+ "apihelp-oathvalidate-summary": "验证一个双因素验证(OATH)令牌。",
+ "apihelp-oathvalidate-param-user": "要验证令牌的用户。默认为当前用户。",
+ "apihelp-oathvalidate-param-totp": "双因素验证(OATH)令牌。",
+ "apihelp-oathvalidate-example-1": "验证当前用户的令牌",
+ "apihelp-oathvalidate-example-2": "验证用户<kbd>Example</kbd>的令牌"
+}
diff --git a/www/wiki/extensions/OATHAuth/i18n/zh-hant.json b/www/wiki/extensions/OATHAuth/i18n/zh-hant.json
new file mode 100644
index 00000000..667d1ba7
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/i18n/zh-hant.json
@@ -0,0 +1,43 @@
+{
+ "@metadata": {
+ "authors": [
+ "Simon Shek",
+ "Cwlin0416",
+ "LNDDYL",
+ "Alexsh",
+ "Waihorace",
+ "A2093064",
+ "Hello903hello"
+ ]
+ },
+ "oathauth-desc": "提供支援 HMAC 為基礎的一次性密碼認證",
+ "specialpages-group-oath": "雙重認證",
+ "oathauth-account": "帳號名稱:",
+ "oathauth-secret": "雙重認證秘密金鑰:",
+ "oathauth-enable": "開啟雙重認證",
+ "oathauth-scratchtokens": "以下是一次性使用的備用密碼,這些密碼於緊急情況使用,且只能使用一次。請寫下這些密碼,並存放在安全的地方。如果您遺失了您的行動電話,這些密碼可以回復您的帳號。\n這些密碼只會於現在顯示一次,之後將不會再次顯示。",
+ "oathauth-token": "金鑰",
+ "oathauth-disable": "停用雙重認證",
+ "oathauth-validatedoath": "已驗證您的代碼並啟動雙重認證。",
+ "oathauth-failedtovalidateoath": "驗證雙重認證失敗。",
+ "oathauth-disabledoath": "已停用雙重認證。",
+ "oathauth-prefs-label": "雙重認證:",
+ "oathauth-step1": "第1步:下載雙重認證應用程式",
+ "oathauth-step1-test": "下載雙重認證的應用程式。這可以是一個流動應用程式(例如Google Authenticator)或一個桌面應用程式",
+ "oathauth-step2": "掃描QR碼",
+ "oathauth-step2alt": "或手動輸入密鑰:",
+ "oathauth-step3": "記下備用碼",
+ "oathauth-step4": "驗證",
+ "oathauth-entertoken": "輸入顯示於您認證設備上應用程式的代碼:",
+ "right-oathauth-enable": "啟用雙重認證",
+ "action-oathauth-enable": "啟用雙重認證",
+ "oathauth-auth-token-label": "金鑰",
+ "oathauth-auth-token-help": "用作雙重認證的一次性密碼",
+ "oathauth-auth-ui": "請輸入您行動裝置上顯示的驗證碼",
+ "oathauth-throttled": "驗證次數過多,請稍等 $1。",
+ "oathauth-login-failed": "驗證失敗",
+ "oathauth-describe-provider": "雙重認證(OATH)",
+ "grant-group-authentication": "為自己及其他人進行身份認證動作",
+ "apihelp-oathvalidate-description": "驗證雙重認證金鑰",
+ "apihelp-oathvalidate-param-totp": "雙重認證金鑰"
+}
diff --git a/www/wiki/extensions/OATHAuth/includes/OATHAuthHooks.php b/www/wiki/extensions/OATHAuth/includes/OATHAuthHooks.php
new file mode 100644
index 00000000..8842b16a
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/includes/OATHAuthHooks.php
@@ -0,0 +1,221 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+use MediaWiki\Auth\AuthenticationRequest;
+use MediaWiki\MediaWikiServices;
+use Wikimedia\Rdbms\IDatabase;
+
+/**
+ * Hooks for Extension:OATHAuth
+ *
+ * @ingroup Extensions
+ */
+class OATHAuthHooks {
+ /**
+ * Get the singleton OATH user repository
+ *
+ * @return OATHUserRepository
+ */
+ public static function getOATHUserRepository() {
+ global $wgOATHAuthDatabase;
+
+ static $service = null;
+
+ if ( $service == null ) {
+ $factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+ $service = new OATHUserRepository(
+ $factory->getMainLB( $wgOATHAuthDatabase ),
+ new HashBagOStuff(
+ [
+ 'maxKeys' => 5,
+ ]
+ )
+ );
+ }
+
+ return $service;
+ }
+
+ /**
+ * @param AuthenticationRequest[] $requests
+ * @param array $fieldInfo Field information array (union of the
+ * AuthenticationRequest::getFieldInfo() responses).
+ * @param array &$formDescriptor HTMLForm descriptor. The special key 'weight' can be set
+ * to change the order of the fields.
+ * @param string $action One of the AuthManager::ACTION_* constants.
+ * @return bool
+ */
+ public static function onAuthChangeFormFields(
+ array $requests, array $fieldInfo, array &$formDescriptor, $action
+ ) {
+ if ( isset( $fieldInfo['OATHToken'] ) ) {
+ $formDescriptor['OATHToken'] += [
+ 'cssClass' => 'loginText',
+ 'id' => 'wpOATHToken',
+ 'size' => 20,
+ 'autofocus' => true,
+ 'persistent' => false,
+ 'autocomplete' => false,
+ 'spellcheck' => false,
+ ];
+ }
+ return true;
+ }
+
+ /**
+ * Determine if two-factor authentication is enabled for $wgUser
+ *
+ * This isn't the preferred mechanism for controlling access to sensitive features
+ * (see AuthManager::securitySensitiveOperationStatus() for that) but there is no harm in
+ * keeping it.
+ *
+ * @param bool &$isEnabled Will be set to true if enabled, false otherwise
+ * @return bool False if enabled, true otherwise
+ */
+ public static function onTwoFactorIsEnabled( &$isEnabled ) {
+ global $wgUser;
+
+ $user = self::getOATHUserRepository()->findByUser( $wgUser );
+ if ( $user && $user->getKey() !== null ) {
+ $isEnabled = true;
+ # This two-factor extension is enabled by the user,
+ # we don't need to check others.
+ return false;
+ } else {
+ $isEnabled = false;
+ # This two-factor extension isn't enabled by the user,
+ # but others may be.
+ return true;
+ }
+ }
+
+ /**
+ * Add the necessary user preferences for OATHAuth
+ *
+ * @param User $user
+ * @param array &$preferences
+ * @return bool
+ */
+ public static function onGetPreferences( User $user, array &$preferences ) {
+ $oathUser = self::getOATHUserRepository()->findByUser( $user );
+
+ // If there is no existing key, and the user is not allowed to enable it,
+ // we have nothing to show. (
+ if ( $oathUser->getKey() === null && !$user->isAllowed( 'oathauth-enable' ) ) {
+ return true;
+ }
+
+ $title = SpecialPage::getTitleFor( 'OATH' );
+ $msg = $oathUser->getKey() !== null ? 'oathauth-disable' : 'oathauth-enable';
+
+ $preferences[$msg] = [
+ 'type' => 'info',
+ 'raw' => 'true',
+ 'default' => Linker::link(
+ $title,
+ wfMessage( $msg )->escaped(),
+ [],
+ [ 'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText() ]
+ ),
+ 'label-message' => 'oathauth-prefs-label',
+ 'section' => 'personal/info', ];
+
+ return true;
+ }
+
+ /**
+ * @param DatabaseUpdater $updater
+ * @return bool
+ */
+ public static function onLoadExtensionSchemaUpdates( $updater ) {
+ $base = dirname( __DIR__ );
+ switch ( $updater->getDB()->getType() ) {
+ case 'mysql':
+ case 'sqlite':
+ $updater->addExtensionTable( 'oathauth_users', "$base/sql/mysql/tables.sql" );
+ $updater->addExtensionUpdate( [ [ __CLASS__, 'schemaUpdateOldUsersFromInstaller' ] ] );
+ $updater->dropExtensionField(
+ 'oathauth_users',
+ 'secret_reset',
+ "$base/sql/mysql/patch-remove_reset.sql"
+ );
+ break;
+
+ case 'oracle':
+ $updater->addExtensionTable( 'oathauth_users', "$base/sql/oracle/tables.sql" );
+ break;
+
+ case 'postgres':
+ $updater->addExtensionTable( 'oathauth_users', "$base/sql/postgres/tables.sql" );
+ break;
+ }
+
+ return true;
+ }
+
+ /**
+ * Helper function for converting old users to the new schema
+ * @see OATHAuthHooks::OATHAuthSchemaUpdates
+ *
+ * @param DatabaseUpdater $updater
+ *
+ * @return bool
+ */
+ public static function schemaUpdateOldUsersFromInstaller( DatabaseUpdater $updater ) {
+ return self::schemaUpdateOldUsers( $updater->getDB() );
+ }
+
+ /**
+ * Helper function for converting old users to the new schema
+ * @see OATHAuthHooks::OATHAuthSchemaUpdates
+ *
+ * @param IDatabase $db
+ * @return bool
+ */
+ public static function schemaUpdateOldUsers( IDatabase $db ) {
+ if ( !$db->fieldExists( 'oathauth_users', 'secret_reset' ) ) {
+ return true;
+ }
+
+ $res = $db->select(
+ 'oathauth_users',
+ [ 'id', 'scratch_tokens' ],
+ [ 'is_validated != 0' ],
+ __METHOD__
+ );
+
+ foreach ( $res as $row ) {
+ Wikimedia\suppressWarnings();
+ $scratchTokens = unserialize( base64_decode( $row->scratch_tokens ) );
+ Wikimedia\restoreWarnings();
+ if ( $scratchTokens ) {
+ $db->update(
+ 'oathauth_users',
+ [ 'scratch_tokens' => implode( ',', $scratchTokens ) ],
+ [ 'id' => $row->id ],
+ __METHOD__
+ );
+ }
+ }
+
+ // Remove rows from the table where user never completed the setup process
+ $db->delete( 'oathauth_users', [ 'is_validated' => 0 ], __METHOD__ );
+
+ return true;
+ }
+}
diff --git a/www/wiki/extensions/OATHAuth/includes/OATHAuthKey.php b/www/wiki/extensions/OATHAuth/includes/OATHAuthKey.php
new file mode 100644
index 00000000..2e178803
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/includes/OATHAuthKey.php
@@ -0,0 +1,187 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Class representing a two-factor key
+ *
+ * Keys can be tied to OATHUsers
+ *
+ * @ingroup Extensions
+ */
+class OATHAuthKey {
+ /**
+ * Represents that a token corresponds to the main secret
+ * @see verifyToken
+ */
+ const MAIN_TOKEN = 1;
+
+ /**
+ * Represents that a token corresponds to a scratch token
+ * @see verifyToken
+ */
+ const SCRATCH_TOKEN = -1;
+
+ /** @var array Two factor binary secret */
+ private $secret;
+
+ /** @var string[] List of scratch tokens */
+ private $scratchTokens;
+
+ /**
+ * Make a new key from random values
+ *
+ * @return OATHAuthKey
+ */
+ public static function newFromRandom() {
+ $object = new self(
+ Base32::encode( MWCryptRand::generate( 10, true ) ),
+ []
+ );
+
+ $object->regenerateScratchTokens();
+
+ return $object;
+ }
+
+ /**
+ * @param string $secret
+ * @param array $scratchTokens
+ */
+ public function __construct( $secret, array $scratchTokens ) {
+ // Currently harcoded values; might be used in future
+ $this->secret = [
+ 'mode' => 'hotp',
+ 'secret' => $secret,
+ 'period' => 30,
+ 'algorithm' => 'SHA1',
+ ];
+ $this->scratchTokens = $scratchTokens;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSecret() {
+ return $this->secret['secret'];
+ }
+
+ /**
+ * @return array
+ */
+ public function getScratchTokens() {
+ return $this->scratchTokens;
+ }
+
+ /**
+ * Verify a token against the secret or scratch tokens
+ *
+ * @param string $token Token to verify
+ * @param OATHUser $user
+ *
+ * @return int|false Returns a constant represent what type of token was matched,
+ * or false for no match
+ */
+ public function verifyToken( $token, OATHUser $user ) {
+ global $wgOATHAuthWindowRadius;
+
+ if ( $this->secret['mode'] !== 'hotp' ) {
+ throw new \DomainException( 'OATHAuth extension does not support non-HOTP tokens' );
+ }
+
+ // Prevent replay attacks
+ $memc = ObjectCache::newAnything( [] );
+ $uid = CentralIdLookup::factory()->centralIdFromLocalUser( $user->getUser() );
+ $memcKey = wfMemcKey( 'oathauth', 'usedtokens', $uid );
+ $lastWindow = (int)$memc->get( $memcKey );
+
+ $retval = false;
+ $results = HOTP::generateByTimeWindow(
+ Base32::decode( $this->secret['secret'] ),
+ $this->secret['period'], -$wgOATHAuthWindowRadius, $wgOATHAuthWindowRadius
+ );
+
+ // Remove any whitespace from the received token, which can be an intended group seperator
+ // or trimmeable whitespace
+ $token = preg_replace( '/\s+/', '', $token );
+
+ // Check to see if the user's given token is in the list of tokens generated
+ // for the time window.
+ foreach ( $results as $window => $result ) {
+ if ( $window > $lastWindow && $result->toHOTP( 6 ) === $token ) {
+ $lastWindow = $window;
+ $retval = self::MAIN_TOKEN;
+ break;
+ }
+ }
+
+ // See if the user is using a scratch token
+ if ( !$retval ) {
+ $length = count( $this->scratchTokens );
+ // Detect condition where all scratch tokens have been used
+ if ( $length == 1 && "" === $this->scratchTokens[0] ) {
+ $retval = false;
+ } else {
+ for ( $i = 0; $i < $length; $i++ ) {
+ if ( $token === $this->scratchTokens[$i] ) {
+ // If there is a scratch token, remove it from the scratch token list
+ unset( $this->scratchTokens[$i] );
+ $oathrepo = OATHAuthHooks::getOATHUserRepository();
+ $user->setKey( $this );
+ $oathrepo->persist( $user );
+ // Only return true if we removed it from the database
+ $retval = self::SCRATCH_TOKEN;
+ break;
+ }
+ }
+ }
+ }
+
+ if ( $retval ) {
+ $memc->set(
+ $memcKey,
+ $lastWindow,
+ $this->secret['period'] * ( 1 + 2 * $wgOATHAuthWindowRadius )
+ );
+ } else {
+ // Increase rate limit counter for failed request
+ $user->getUser()->pingLimiter( 'badoath' );
+ }
+
+ return $retval;
+ }
+
+ public function regenerateScratchTokens() {
+ $scratchTokens = [];
+ for ( $i = 0; $i < 5; $i++ ) {
+ array_push( $scratchTokens, Base32::encode( MWCryptRand::generate( 10, true ) ) );
+ }
+ $this->scratchTokens = $scratchTokens;
+ }
+
+ /**
+ * Check if a token is one of the scratch tokens for this two factor key.
+ *
+ * @param string $token Token to verify
+ *
+ * @return bool true if this is a scratch token.
+ */
+ public function isScratchToken( $token ) {
+ $token = preg_replace( '/\s+/', '', $token );
+ return in_array( $token, $this->scratchTokens, true );
+ }
+}
diff --git a/www/wiki/extensions/OATHAuth/includes/OATHAuthUtils.php b/www/wiki/extensions/OATHAuth/includes/OATHAuthUtils.php
new file mode 100644
index 00000000..2afd3bf8
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/includes/OATHAuthUtils.php
@@ -0,0 +1,141 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Utility class for various OATH functions
+ *
+ * @ingroup Extensions
+ */
+class OATHAuthUtils {
+ /**
+ * Check whether OATH two-factor authentication is enabled for a given user.
+ * This is a stable method that does not change and can be used in other extensions.
+ * @param User $user
+ * @return bool
+ */
+ public static function isEnabledFor( User $user ) {
+ $oathUser = OATHAuthHooks::getOATHUserRepository()->findByUser( $user );
+ return $oathUser && $oathUser->getKey();
+ }
+
+ /**
+ * Encrypt an aray of variables to put into the user's session. We use this
+ * when storing the user's password in their session. We can use json as the
+ * serialization format because $plaintextVars is an array of strings.
+ * @param array $plaintextVars array of user input strings
+ * @param int $userId passed to key derivation functions so each user uses
+ * distinct encryption and hmac keys
+ * @return string encrypted data packet
+ */
+ public static function encryptSessionData( array $plaintextVars, $userId ) {
+ $keyMaterial = self::getKeyMaterials();
+ $keys = self::getUserKeys( $keyMaterial, $userId );
+ return self::seal( json_encode( $plaintextVars ), $keys['encrypt'], $keys['hmac'] );
+ }
+
+ /**
+ * Decrypt an encrypted packet, generated with encryptSessionData
+ * @param string $ciphertext Encrypted data packet
+ * @param string|int $userId
+ * @return array of strings
+ */
+ public static function decryptSessionData( $ciphertext, $userId ) {
+ $keyMaterial = self::getKeyMaterials();
+ $keys = self::getUserKeys( $keyMaterial, $userId );
+ return json_decode( self::unseal( $ciphertext, $keys['encrypt'], $keys['hmac'] ), true );
+ }
+
+ /**
+ * Get the base secret for this wiki, used to derive all of the encryption
+ * keys. When $wgOATHAuthSecret is rotated, users who are part way through the
+ * two-step login will get an exception, and have to re-start the login.
+ * @return string
+ */
+ private static function getKeyMaterials() {
+ global $wgOATHAuthSecret, $wgSecretKey;
+ return $wgOATHAuthSecret ?: $wgSecretKey;
+ }
+
+ /**
+ * Generate encryption and hmac keys, unique to this user, based on a single
+ * wiki secret. Use a moderate pbkdf2 work factor in case we ever leak keys.
+ * @param string $secret
+ * @param string|int $userid
+ * @return array including key for encryption and integrity checking
+ */
+ private static function getUserKeys( $secret, $userid ) {
+ $keymats = hash_pbkdf2( 'sha256', $secret, "oath-$userid", 10001, 64, true );
+ return [
+ 'encrypt' => substr( $keymats, 0, 32 ),
+ 'hmac' => substr( $keymats, 32, 32 ),
+ ];
+ }
+
+ /**
+ * Actually encrypt the data, using a new random IV, and prepend the hmac
+ * of the encrypted data + IV, using a separate hmac key.
+ * @param string $data
+ * @param string $encKey
+ * @param string $hmacKey
+ * @return string $hmac.$iv.$ciphertext, each component b64 encoded
+ */
+ private static function seal( $data, $encKey, $hmacKey ) {
+ $iv = MWCryptRand::generate( 16, true );
+ $ciphertext = openssl_encrypt(
+ $data,
+ 'aes-256-ctr',
+ $encKey,
+ OPENSSL_RAW_DATA,
+ $iv
+ );
+ $sealed = base64_encode( $iv ) . '.' . base64_encode( $ciphertext );
+ $hmac = hash_hmac( 'sha256', $sealed, $hmacKey, true );
+ return base64_encode( $hmac ) . '.' . $sealed;
+ }
+
+ /**
+ * Decrypt data sealed using seal(). First checks the hmac to prevent various
+ * attacks.
+ * @param string $encrypted
+ * @param string $encKey
+ * @param string $hmacKey
+ * @return string plaintext
+ * @throws Exception
+ */
+ private static function unseal( $encrypted, $encKey, $hmacKey ) {
+ $pieces = explode( '.', $encrypted );
+ if ( count( $pieces ) !== 3 ) {
+ throw new InvalidArgumentException( 'Invalid sealed-secret format' );
+ }
+
+ list( $hmac, $iv, $ciphertext ) = $pieces;
+ $integCalc = hash_hmac( 'sha256', $iv . '.' . $ciphertext, $hmacKey, true );
+ if ( !hash_equals( $integCalc, base64_decode( $hmac ) ) ) {
+ throw new Exception( 'Sealed secret has been tampered with, aborting.' );
+ }
+
+ return openssl_decrypt(
+ base64_decode( $ciphertext ),
+ 'aes-256-ctr',
+ $encKey,
+ OPENSSL_RAW_DATA,
+ base64_decode( $iv )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/OATHAuth/includes/OATHUser.php b/www/wiki/extensions/OATHAuth/includes/OATHUser.php
new file mode 100644
index 00000000..ed1b4a16
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/includes/OATHUser.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Class representing a user from OATH's perspective
+ *
+ * @ingroup Extensions
+ */
+class OATHUser {
+ /** @var User */
+ private $user;
+
+ /** @var OATHAuthKey|null */
+ private $key;
+
+ /**
+ * Constructor. Can't be called directly. Use OATHUserRepository::findByUser instead.
+ * @param User $user
+ * @param OATHAuthKey|null $key
+ */
+ public function __construct( User $user, OATHAuthKey $key = null ) {
+ $this->user = $user;
+ $this->key = $key;
+ }
+
+ /**
+ * @return User
+ */
+ public function getUser() {
+ return $this->user;
+ }
+
+ /**
+ * @return String
+ */
+ public function getIssuer() {
+ global $wgSitename, $wgOATHAuthAccountPrefix;
+ if ( $wgOATHAuthAccountPrefix !== false ) {
+ return $wgOATHAuthAccountPrefix;
+ }
+ return $wgSitename;
+ }
+
+ /**
+ * @return String
+ */
+ public function getAccount() {
+ return $this->user->getName();
+ }
+
+ /**
+ * Get the key associated with this user.
+ *
+ * @return null|OATHAuthKey
+ */
+ public function getKey() {
+ return $this->key;
+ }
+
+ /**
+ * Set the key associated with this user.
+ *
+ * @param OATHAuthKey|null $key
+ */
+ public function setKey( OATHAuthKey $key = null ) {
+ $this->key = $key;
+ }
+}
diff --git a/www/wiki/extensions/OATHAuth/includes/OATHUserRepository.php b/www/wiki/extensions/OATHAuth/includes/OATHUserRepository.php
new file mode 100644
index 00000000..698ca49b
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/includes/OATHUserRepository.php
@@ -0,0 +1,103 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+use Wikimedia\Rdbms\LoadBalancer;
+use Wikimedia\Rdbms\DBConnRef;
+
+class OATHUserRepository {
+ /** @var LoadBalancer */
+ protected $lb;
+
+ /** @var BagOStuff */
+ protected $cache;
+
+ /**
+ * OATHUserRepository constructor.
+ * @param LoadBalancer $lb
+ * @param BagOStuff $cache
+ */
+ public function __construct( LoadBalancer $lb, BagOStuff $cache ) {
+ $this->lb = $lb;
+ $this->cache = $cache;
+ }
+
+ /**
+ * @param User $user
+ * @return OATHUser
+ */
+ public function findByUser( User $user ) {
+ $oathUser = $this->cache->get( $user->getName() );
+ if ( !$oathUser ) {
+ $oathUser = new OATHUser( $user, null );
+
+ $uid = CentralIdLookup::factory()->centralIdFromLocalUser( $user );
+ $res = $this->getDB( DB_REPLICA )->selectRow(
+ 'oathauth_users',
+ '*',
+ [ 'id' => $uid ],
+ __METHOD__
+ );
+ if ( $res ) {
+ $key = new OATHAuthKey( $res->secret, explode( ',', $res->scratch_tokens ) );
+ $oathUser->setKey( $key );
+ }
+
+ $this->cache->set( $user->getName(), $oathUser );
+ }
+ return $oathUser;
+ }
+
+ /**
+ * @param OATHUser $user
+ */
+ public function persist( OATHUser $user ) {
+ $this->getDB( DB_MASTER )->replace(
+ 'oathauth_users',
+ [ 'id' ],
+ [
+ 'id' => CentralIdLookup::factory()->centralIdFromLocalUser( $user->getUser() ),
+ 'secret' => $user->getKey()->getSecret(),
+ 'scratch_tokens' => implode( ',', $user->getKey()->getScratchTokens() ),
+ ],
+ __METHOD__
+ );
+ $this->cache->set( $user->getUser()->getName(), $user );
+ }
+
+ /**
+ * @param OATHUser $user
+ */
+ public function remove( OATHUser $user ) {
+ $this->getDB( DB_MASTER )->delete(
+ 'oathauth_users',
+ [ 'id' => CentralIdLookup::factory()->centralIdFromLocalUser( $user->getUser() ) ],
+ __METHOD__
+ );
+ $this->cache->delete( $user->getUser()->getName() );
+ }
+
+ /**
+ * @param integer $index DB_MASTER/DB_REPLICA
+ * @return DBConnRef
+ */
+ private function getDB( $index ) {
+ global $wgOATHAuthDatabase;
+
+ return $this->lb->getConnectionRef( $index, [], $wgOATHAuthDatabase );
+ }
+}
diff --git a/www/wiki/extensions/OATHAuth/includes/api/ApiOATHValidate.php b/www/wiki/extensions/OATHAuth/includes/api/ApiOATHValidate.php
new file mode 100644
index 00000000..14f46414
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/includes/api/ApiOATHValidate.php
@@ -0,0 +1,101 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Validate an OATH token.
+ *
+ * @ingroup API
+ * @ingroup Extensions
+ */
+class ApiOATHValidate extends ApiBase {
+ public function execute() {
+ // Be extra paranoid about the data that is sent
+ $this->requirePostedParameters( [ 'totp', 'token' ] );
+
+ $params = $this->extractRequestParams();
+ if ( $params['user'] === null ) {
+ $params['user'] = $this->getUser()->getName();
+ }
+
+ $this->checkUserRightsAny( 'oathauth-api-all' );
+
+ $user = User::newFromName( $params['user'] );
+ if ( $user === false ) {
+ $this->dieWithError( 'noname' );
+ }
+
+ // Don't increase pingLimiter, just check for limit exceeded.
+ if ( $user->pingLimiter( 'badoath', 0 ) ) {
+ $this->dieWithError( 'apierror-ratelimited' );
+ }
+
+ $result = [
+ ApiResult::META_BC_BOOLS => [ 'enabled', 'valid' ],
+ 'enabled' => false,
+ 'valid' => false,
+ ];
+
+ if ( !$user->isAnon() ) {
+ $oathUser = OATHAuthHooks::getOATHUserRepository()
+ ->findByUser( $user );
+ if ( $oathUser ) {
+ $key = $oathUser->getKey();
+ if ( $key !== null ) {
+ $result['enabled'] = true;
+ $result['valid'] = $key->verifyToken(
+ $params['totp'], $oathUser ) !== false;
+ }
+ }
+ }
+
+ $this->getResult()->addValue( null, $this->getModuleName(), $result );
+ }
+
+ public function getCacheMode( $params ) {
+ return 'private';
+ }
+
+ public function isInternal() {
+ return true;
+ }
+
+ public function needsToken() {
+ return 'csrf';
+ }
+
+ public function getAllowedParams() {
+ return [
+ 'user' => [
+ ApiBase::PARAM_TYPE => 'user',
+ ],
+ 'totp' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true,
+ ],
+ ];
+ }
+
+ protected function getExamplesMessages() {
+ return [
+ 'action=oathvalidate&totp=123456&token=123ABC'
+ => 'apihelp-oathvalidate-example-1',
+ 'action=oathvalidate&user=Example&totp=123456&token=123ABC'
+ => 'apihelp-oathvalidate-example-2',
+ ];
+ }
+}
diff --git a/www/wiki/extensions/OATHAuth/includes/api/ApiQueryOATH.php b/www/wiki/extensions/OATHAuth/includes/api/ApiQueryOATH.php
new file mode 100644
index 00000000..e131f193
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/includes/api/ApiQueryOATH.php
@@ -0,0 +1,90 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Query module to check if a user has OATH authentication enabled.
+ *
+ * Usage requires the 'oathauth-api-all' grant which is not given to any group
+ * by default. Use of this API is security sensitive and should not be granted
+ * lightly. Configuring a special 'oathauth' user group is recommended.
+ *
+ * @ingroup API
+ * @ingroup Extensions
+ */
+class ApiQueryOATH extends ApiQueryBase {
+ public function __construct( $query, $moduleName ) {
+ parent::__construct( $query, $moduleName, 'oath' );
+ }
+
+ public function execute() {
+ $params = $this->extractRequestParams();
+ if ( $params['user'] === null ) {
+ $params['user'] = $this->getUser()->getName();
+ }
+
+ $this->checkUserRightsAny( 'oathauth-api-all' );
+
+ $user = User::newFromName( $params['user'] );
+ if ( $user === false ) {
+ $this->dieWithError( 'noname' );
+ }
+
+ $result = $this->getResult();
+ $data = [
+ ApiResult::META_BC_BOOLS => [ 'enabled' ],
+ 'enabled' => false,
+ ];
+
+ if ( !$user->isAnon() ) {
+ $oathUser = OATHAuthHooks::getOATHUserRepository()
+ ->findByUser( $user );
+ $data['enabled'] = $oathUser && $oathUser->getKey() !== null;
+ }
+ $result->addValue( 'query', $this->getModuleName(), $data );
+ }
+
+ /**
+ * @param array $params
+ *
+ * @return string
+ */
+ public function getCacheMode( $params ) {
+ return 'private';
+ }
+
+ public function isInternal() {
+ return true;
+ }
+
+ public function getAllowedParams() {
+ return [
+ 'user' => [
+ ApiBase::PARAM_TYPE => 'user',
+ ],
+ ];
+ }
+
+ protected function getExamplesMessages() {
+ return [
+ 'action=query&meta=oath'
+ => 'apihelp-query+oath-example-1',
+ 'action=query&meta=oath&oathuser=Example'
+ => 'apihelp-query+oath-example-2',
+ ];
+ }
+}
diff --git a/www/wiki/extensions/OATHAuth/includes/auth/TOTPAuthenticationRequest.php b/www/wiki/extensions/OATHAuth/includes/auth/TOTPAuthenticationRequest.php
new file mode 100644
index 00000000..c10f5e2f
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/includes/auth/TOTPAuthenticationRequest.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+use MediaWiki\Auth\AuthenticationRequest;
+
+/**
+ * AuthManager value object for the TOTP second factor of an authentication:
+ * a pseudorandom token that is generated from the current time independently
+ * by the server and the client.
+ */
+class TOTPAuthenticationRequest extends AuthenticationRequest {
+ public $OATHToken;
+
+ public function describeCredentials() {
+ return [
+ 'provider' => wfMessage( 'oathauth-describe-provider' ),
+ 'account' => new \RawMessage( '$1', [ $this->username ] ),
+ ] + parent::describeCredentials();
+ }
+
+ public function getFieldInfo() {
+ return [
+ 'OATHToken' => [
+ 'type' => 'string',
+ 'label' => wfMessage( 'oathauth-auth-token-label' ),
+ 'help' => wfMessage( 'oathauth-auth-token-help' ), ], ];
+ }
+}
diff --git a/www/wiki/extensions/OATHAuth/includes/auth/TOTPSecondaryAuthenticationProvider.php b/www/wiki/extensions/OATHAuth/includes/auth/TOTPSecondaryAuthenticationProvider.php
new file mode 100644
index 00000000..90d69cc3
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/includes/auth/TOTPSecondaryAuthenticationProvider.php
@@ -0,0 +1,121 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+use MediaWiki\Auth\AbstractSecondaryAuthenticationProvider;
+use MediaWiki\Auth\AuthenticationRequest;
+use MediaWiki\Auth\AuthenticationResponse;
+use MediaWiki\Auth\AuthManager;
+
+/**
+ * AuthManager secondary authentication provider for TOTP second-factor authentication.
+ *
+ * After a successful primary authentication, requests a time-based one-time password
+ * (typically generated by a mobile app such as Google Authenticator) from the user.
+ *
+ * @see AuthManager
+ * @see https://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm
+ */
+class TOTPSecondaryAuthenticationProvider extends AbstractSecondaryAuthenticationProvider {
+
+ /**
+ * @param string $action
+ * @param array $options
+ *
+ * @return array
+ */
+ public function getAuthenticationRequests( $action, array $options ) {
+ switch ( $action ) {
+ case AuthManager::ACTION_LOGIN:
+ // don't ask for anything initially so the second factor is on a separate screen
+ return [];
+ default:
+ return [];
+ }
+ }
+
+ /**
+ * If the user has enabled two-factor authentication, request a second factor.
+ *
+ * @param User $user
+ * @param array $reqs
+ *
+ * @return AuthenticationResponse
+ */
+ public function beginSecondaryAuthentication( $user, array $reqs ) {
+ $oathuser = OATHAuthHooks::getOATHUserRepository()->findByUser( $user );
+
+ if ( $oathuser->getKey() === null ) {
+ return AuthenticationResponse::newAbstain();
+ } else {
+ return AuthenticationResponse::newUI( [ new TOTPAuthenticationRequest() ],
+ wfMessage( 'oathauth-auth-ui' ), 'warning' );
+ }
+ }
+
+ /**
+ * Verify the second factor.
+ * @inheritDoc
+ */
+ public function continueSecondaryAuthentication( $user, array $reqs ) {
+ /** @var TOTPAuthenticationRequest $request */
+ $request = AuthenticationRequest::getRequestByClass( $reqs, TOTPAuthenticationRequest::class );
+ if ( !$request ) {
+ return AuthenticationResponse::newUI( [ new TOTPAuthenticationRequest() ],
+ wfMessage( 'oathauth-login-failed' ), 'error' );
+ }
+
+ $oathuser = OATHAuthHooks::getOATHUserRepository()->findByUser( $user );
+ /** @suppress PhanUndeclaredProperty */
+ $token = $request->OATHToken;
+
+ if ( $oathuser->getKey() === null ) {
+ $this->logger->warning( 'Two-factor authentication was disabled mid-authentication for '
+ . $user->getName() );
+ return AuthenticationResponse::newAbstain();
+ }
+
+ // Don't increase pingLimiter, just check for limit exceeded.
+ if ( $user->pingLimiter( 'badoath', 0 ) ) {
+ return AuthenticationResponse::newUI(
+ [ new TOTPAuthenticationRequest() ],
+ new Message(
+ 'oathauth-throttled',
+ // Arbitrary duration given here
+ [ Message::durationParam( 60 ) ]
+ ), 'error' );
+ }
+
+ if ( $oathuser->getKey()->verifyToken( $token, $oathuser ) ) {
+ return AuthenticationResponse::newPass();
+ } else {
+ return AuthenticationResponse::newUI( [ new TOTPAuthenticationRequest() ],
+ wfMessage( 'oathauth-login-failed' ), 'error' );
+ }
+ }
+
+ /**
+ * @param User $user
+ * @param User $creator
+ * @param array $reqs
+ *
+ * @return AuthenticationResponse
+ */
+ public function beginSecondaryAccountCreation( $user, $creator, array $reqs ) {
+ return AuthenticationResponse::newAbstain();
+ }
+}
diff --git a/www/wiki/extensions/OATHAuth/includes/lib/base32.php b/www/wiki/extensions/OATHAuth/includes/lib/base32.php
new file mode 100644
index 00000000..d4ca1dff
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/includes/lib/base32.php
@@ -0,0 +1,105 @@
+<?php
+/**
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * PHP Google two-factor authentication module.
+ *
+ * See http://www.idontplaydarts.com/2011/07/google-totp-two-factor-authentication-for-php/
+ * for more details
+ *
+ * @author Phil
+ **/
+
+class Base32 {
+
+ private static $lut = array(
+ "A" => 0, "B" => 1,
+ "C" => 2, "D" => 3,
+ "E" => 4, "F" => 5,
+ "G" => 6, "H" => 7,
+ "I" => 8, "J" => 9,
+ "K" => 10, "L" => 11,
+ "M" => 12, "N" => 13,
+ "O" => 14, "P" => 15,
+ "Q" => 16, "R" => 17,
+ "S" => 18, "T" => 19,
+ "U" => 20, "V" => 21,
+ "W" => 22, "X" => 23,
+ "Y" => 24, "Z" => 25,
+ "2" => 26, "3" => 27,
+ "4" => 28, "5" => 29,
+ "6" => 30, "7" => 31
+ );
+
+ /**
+ * Decodes a base32 string into a binary string according to RFC 4648.
+ **/
+ public static function decode($b32) {
+
+ $b32 = strtoupper($b32);
+
+ if (!preg_match('/^[ABCDEFGHIJKLMNOPQRSTUVWXYZ234567]+$/', $b32, $match))
+ throw new Exception('Invalid characters in the base32 string.');
+
+ $l = strlen($b32);
+ $n = 0;
+ $j = 0;
+ $binary = "";
+
+ for ($i = 0; $i < $l; $i++) {
+ // Move buffer left by 5 to make room
+ $n = $n << 5;
+ // Add value into buffer
+ $n = $n + self::$lut[$b32[$i]];
+ // Keep track of number of bits in buffer
+ $j = $j + 5;
+
+ if ($j >= 8) {
+ $j = $j - 8;
+ $binary .= chr(($n & (0xFF << $j)) >> $j);
+ }
+ }
+
+ return $binary;
+ }
+
+ /**
+ * Encodes a binary string into a base32 string according to RFC 4648 (no padding).
+ **/
+ public static function encode($string) {
+
+ if (empty($string))
+ throw new Exception('Empty string.');
+
+ $b32 = "";
+ $binary = "";
+
+ $bytes = str_split($string);
+ $length = count( $bytes );
+ for ($i = 0; $i < $length; $i++) {
+ $bits = base_convert(ord($bytes[$i]), 10, 2);
+ $binary .= str_pad($bits, 8, '0', STR_PAD_LEFT);
+ }
+
+ $map = array_keys(self::$lut);
+ $fivebits = str_split($binary, 5);
+ $length = count( $fivebits );
+ for ($i = 0; $i < $length; $i++) {
+ $dec = base_convert(str_pad($fivebits[$i], 5, '0'), 2, 10);
+ $b32 .= $map[$dec];
+ }
+
+ return $b32;
+ }
+}
diff --git a/www/wiki/extensions/OATHAuth/includes/lib/hotp.php b/www/wiki/extensions/OATHAuth/includes/lib/hotp.php
new file mode 100644
index 00000000..8fd3d94b
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/includes/lib/hotp.php
@@ -0,0 +1,179 @@
+<?php
+/**
+ * HOTP Class
+ * Based on the work of OAuth, and the sample implementation of HMAC OTP
+ * http://tools.ietf.org/html/draft-mraihi-oath-hmac-otp-04#appendix-D
+ * @author Jakob Heuser (firstname)@felocity.com
+ * @copyright 2011
+ * @license BSD-3-Clause
+ * @version 1.0
+ */
+class HOTP {
+ /**
+ * Generate a HOTP key based on a counter value (event based HOTP)
+ * @param string $key the key to use for hashing
+ * @param int $counter the number of attempts represented in this hashing
+ * @return HOTPResult a HOTP Result which can be truncated or output
+ */
+ public static function generateByCounter( $key, $counter ) {
+ // the counter value can be more than one byte long,
+ // so we need to pack it down properly.
+ $cur_counter = array( 0, 0, 0, 0, 0, 0, 0, 0 );
+ for ( $i = 7; $i >= 0; $i-- ) {
+ $cur_counter[$i] = pack( 'C*', $counter );
+ $counter = $counter >> 8;
+ }
+
+ $bin_counter = implode( $cur_counter );
+
+ // Pad to 8 chars
+ if ( strlen( $bin_counter ) < 8 ) {
+ $bin_counter = str_repeat( "\0", 8 - strlen( $bin_counter ) ) . $bin_counter;
+ }
+
+ // HMAC
+ $hash = hash_hmac( 'sha1', $bin_counter, $key );
+
+ return new HOTPResult( $hash );
+ }
+
+ /**
+ * Generate a HOTP key based on a timestamp and window size
+ *
+ * @param string $key the key to use for hashing
+ * @param int $window the size of the window a key is valid for in seconds
+ * @param int|bool $timestamp a timestamp to calculate for, defaults to time()
+ *
+ * @return HOTPResult a HOTP Result which can be truncated or output
+ */
+ public static function generateByTime( $key, $window, $timestamp = false ) {
+ if ( !$timestamp && $timestamp !== 0 ) {
+ $timestamp = HOTP::getTime();
+ }
+
+ $counter = (int)( $timestamp / $window );
+
+ return HOTP::generateByCounter( $key, $counter );
+ }
+
+ /**
+ * Generate a HOTP key collection based on a timestamp and window size
+ * all keys that could exist between a start and end time will be included
+ * in the returned array
+ *
+ * @param string $key the key to use for hashing
+ * @param int $window the size of the window a key is valid for in seconds
+ * @param int $min the minimum window to accept before $timestamp
+ * @param int $max the maximum window to accept after $timestamp
+ * @param int|bool $timestamp a timestamp to calculate for, defaults to time()
+ *
+ * @return HOTPResult[]
+ */
+ public static function generateByTimeWindow( $key, $window, $min = -1,
+ $max = 1, $timestamp = false
+ ) {
+ if ( !$timestamp && $timestamp !== 0 ) {
+ $timestamp = HOTP::getTime();
+ }
+
+ $counter = (int)( $timestamp / $window );
+ $window = range( $min, $max );
+
+ $out = array();
+ $length = count( $window );
+ for ( $i = 0; $i < $length; $i++ ) {
+ $shift_counter = $counter + $window[$i];
+ $out[$shift_counter] = HOTP::generateByCounter($key, $shift_counter);
+ }
+
+ return $out;
+ }
+
+ /**
+ * Gets the current time
+ * Ensures we are operating in UTC for the entire framework
+ * Restores the timezone on exit.
+ * @return int the current time
+ */
+ public static function getTime() {
+ return time(); // PHP's time is always UTC
+ }
+}
+
+/**
+ * The HOTPResult Class converts an HOTP item to various forms
+ * Supported formats include hex, decimal, string, and HOTP
+ * @author Jakob Heuser (firstname)@felocity.com
+ */
+class HOTPResult {
+ protected $hash;
+ protected $binary;
+ protected $decimal;
+ protected $hex;
+
+ /**
+ * Build an HOTP Result
+ * @param string $value the value to construct with
+ */
+ public function __construct( $value ) {
+ // store raw
+ $this->hash = $value;
+
+ // store calculate decimal
+ $hmac_result = array();
+
+ // Convert to decimal
+ foreach ( str_split( $this->hash, 2 ) as $hex ) {
+ $hmac_result[] = hexdec($hex);
+ }
+
+ $offset = $hmac_result[19] & 0xf;
+
+ $this->decimal = (
+ ( ( $hmac_result[$offset+0] & 0x7f ) << 24 ) |
+ ( ( $hmac_result[$offset+1] & 0xff ) << 16 ) |
+ ( ( $hmac_result[$offset+2] & 0xff ) << 8 ) |
+ ( $hmac_result[$offset+3] & 0xff )
+ );
+
+ // calculate hex
+ $this->hex = dechex( $this->decimal );
+ }
+
+ /**
+ * Returns the string version of the HOTP
+ * @return string
+ */
+ public function toString() {
+ return $this->hash;
+ }
+
+ /**
+ * Returns the hex version of the HOTP
+ * @return string
+ */
+ public function toHex() {
+ return $this->hex;
+ }
+
+ /**
+ * Returns the decimal version of the HOTP
+ * @return int
+ */
+ public function toDec() {
+ return $this->decimal;
+ }
+
+ /**
+ * Returns the truncated decimal form of the HOTP
+ * @param int $length the length of the HOTP to return
+ * @return string
+ */
+ public function toHOTP( $length ) {
+ $str = str_pad( $this->toDec(), $length, "0", STR_PAD_LEFT );
+ $str = substr( $str, ( -1 * $length ) );
+
+ return $str;
+ }
+
+}
diff --git a/www/wiki/extensions/OATHAuth/includes/special/ProxySpecialPage.php b/www/wiki/extensions/OATHAuth/includes/special/ProxySpecialPage.php
new file mode 100644
index 00000000..bdb808f0
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/includes/special/ProxySpecialPage.php
@@ -0,0 +1,227 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * A proxy class that routes a special page to other special pages based on
+ * request parameters
+ */
+abstract class ProxySpecialPage extends SpecialPage {
+ /**
+ * @var SpecialPage|null Target page to execute
+ */
+ private $target = null;
+
+ /**
+ * Instantiate a SpecialPage based on request parameters
+ *
+ * The page returned by this function will be cached and used as
+ * the target page for this proxy object.
+ *
+ * @return SpecialPage
+ */
+ abstract protected function getTargetPage();
+
+ /**
+ * Helper function that initializes the target SpecialPage object
+ */
+ private function init() {
+ if ( $this->target === null ) {
+ $this->target = $this->getTargetPage();
+ }
+ }
+
+ /**
+ * Magic function that proxies function calls to the target object
+ *
+ * @param string $method Method name being called
+ * @param array $args Array of arguments
+ *
+ * @return mixed
+ */
+ public function __call( $method, $args ) {
+ $this->init();
+ return call_user_func_array( [ $this->target, $method ], $args );
+ }
+
+ /**
+ * @return string
+ */
+ function getName() {
+ $this->init();
+ return $this->target->getName();
+ }
+
+ /**
+ * @param string|bool $subpage
+ * @return Title
+ */
+ function getPageTitle( $subpage = false ) {
+ $this->init();
+ return $this->target->getPageTitle( $subpage );
+ }
+
+ /**
+ * @return string
+ */
+ function getLocalName() {
+ $this->init();
+ return $this->target->getLocalName();
+ }
+
+ /**
+ * @return string
+ */
+ function getRestriction() {
+ $this->init();
+ return $this->target->getRestriction();
+ }
+
+ /**
+ * @return bool
+ */
+ function isListed() {
+ $this->init();
+ return $this->target->isListed();
+ }
+
+ /**
+ * @param bool $listed
+ * @return bool
+ */
+ function setListed( $listed ) {
+ $this->init();
+ return $this->target->setListed( $listed );
+ }
+
+ /**
+ * @param bool $x
+ * @return bool
+ */
+ function listed( $x = null ) {
+ $this->init();
+ return $this->target->listed( $x );
+ }
+
+ /**
+ * @return bool
+ */
+ public function isIncludable() {
+ $this->init();
+ return $this->target->isIncludable();
+ }
+
+ /**
+ * @param bool $x
+ * @return bool
+ */
+ function including( $x = null ) {
+ $this->init();
+ return $this->target->including( $x );
+ }
+
+ /**
+ * @return bool
+ */
+ public function isRestricted() {
+ $this->init();
+ return $this->target->isRestricted();
+ }
+
+ /**
+ * @param User $user
+ * @return bool
+ */
+ public function userCanExecute( User $user ) {
+ $this->init();
+ return $this->target->userCanExecute( $user );
+ }
+
+ /**
+ * @throws PermissionsError
+ */
+ function displayRestrictionError() {
+ $this->init();
+ $this->target->displayRestrictionError();
+ }
+
+ /**
+ * @return void
+ * @throws PermissionsError
+ */
+ public function checkPermissions() {
+ $this->init();
+ $this->target->checkPermissions();
+ }
+
+ /**
+ * @param string|null $subPage
+ */
+ protected function beforeExecute( $subPage ) {
+ $this->init();
+ $this->target->beforeExecute( $subPage );
+ }
+
+ /**
+ * @param string|null $subPage
+ */
+ protected function afterExecute( $subPage ) {
+ $this->init();
+ $this->target->afterExecute( $subPage );
+ }
+
+ /**
+ * @param string|null $subPage
+ */
+ public function execute( $subPage ) {
+ $this->init();
+ $this->target->execute( $subPage );
+ }
+
+ /**
+ * @return string
+ */
+ function getDescription() {
+ $this->init();
+ return $this->target->getDescription();
+ }
+
+ /**
+ * @param IContextSource $context
+ */
+ public function setContext( $context ) {
+ $this->init();
+ $this->target->setContext( $context );
+ parent::setContext( $context );
+ }
+
+ /**
+ * @return string
+ */
+ protected function getRobotPolicy() {
+ $this->init();
+ return $this->target->getRobotPolicy();
+ }
+
+ /**
+ * @return string
+ */
+ protected function getGroupName() {
+ $this->init();
+ return $this->target->getGroupName();
+ }
+}
diff --git a/www/wiki/extensions/OATHAuth/includes/special/SpecialOATH.php b/www/wiki/extensions/OATHAuth/includes/special/SpecialOATH.php
new file mode 100644
index 00000000..21a854c2
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/includes/special/SpecialOATH.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Proxy page that redirects to the proper OATH special page
+ */
+class SpecialOATH extends ProxySpecialPage {
+ /**
+ * If the user already has OATH enabled, show them a page to disable
+ * If the user has OATH disabled, show them a page to enable
+ *
+ * @return SpecialOATHDisable|SpecialOATHEnable
+ */
+ protected function getTargetPage() {
+ $repo = OATHAuthHooks::getOATHUserRepository();
+
+ $user = $repo->findByUser( $this->getUser() );
+
+ if ( $user->getKey() === null ) {
+ return new SpecialOATHEnable( $repo, $user );
+ } else {
+ return new SpecialOATHDisable( $repo, $user );
+ }
+ }
+
+ protected function getGroupName() {
+ return 'oath';
+ }
+}
diff --git a/www/wiki/extensions/OATHAuth/includes/special/SpecialOATHDisable.php b/www/wiki/extensions/OATHAuth/includes/special/SpecialOATHDisable.php
new file mode 100644
index 00000000..7e5758b9
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/includes/special/SpecialOATHDisable.php
@@ -0,0 +1,136 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Special page to display key information to the user
+ *
+ * @ingroup Extensions
+ */
+class SpecialOATHDisable extends FormSpecialPage {
+ /** @var OATHUserRepository */
+ private $OATHRepository;
+
+ /** @var OATHUser */
+ private $OATHUser;
+
+ /**
+ * Initialize the OATH user based on the current local User object in the context
+ *
+ * @param OATHUserRepository $repository
+ * @param OATHUser $user
+ */
+ public function __construct( OATHUserRepository $repository, OATHUser $user ) {
+ parent::__construct( 'OATH', '', false );
+ $this->OATHRepository = $repository;
+ $this->OATHUser = $user;
+ }
+
+ public function doesWrites() {
+ return true;
+ }
+
+ /**
+ * Set the page title and add JavaScript RL modules
+ *
+ * @param HTMLForm $form
+ */
+ public function alterForm( HTMLForm $form ) {
+ $form->setMessagePrefix( 'oathauth' );
+ $form->setWrapperLegend( false );
+ $form->getOutput()->setPageTitle( $this->msg( 'oathauth-disable' ) );
+ }
+
+ /**
+ * @return string
+ */
+ protected function getDisplayFormat() {
+ return 'ooui';
+ }
+
+ /**
+ * @return bool
+ */
+ public function requiresUnblock() {
+ return false;
+ }
+
+ /**
+ * Require users to be logged in
+ *
+ * @param User $user
+ *
+ * @return bool|void
+ */
+ protected function checkExecutePermissions( User $user ) {
+ parent::checkExecutePermissions( $user );
+
+ $this->requireLogin();
+ }
+
+ /**
+ * @return array[]
+ */
+ protected function getFormFields() {
+ return [
+ 'token' => [
+ 'type' => 'text',
+ 'label-message' => 'oathauth-entertoken',
+ 'name' => 'token',
+ 'required' => true,
+ 'autofocus' => true,
+ ],
+ 'returnto' => [
+ 'type' => 'hidden',
+ 'default' => $this->getRequest()->getVal( 'returnto' ),
+ 'name' => 'returnto',
+ ],
+ 'returntoquery' => [
+ 'type' => 'hidden',
+ 'default' => $this->getRequest()->getVal( 'returntoquery' ),
+ 'name' => 'returntoquery',
+ ]
+ ];
+ }
+
+ /**
+ * @param array $formData
+ *
+ * @return array|bool
+ */
+ public function onSubmit( array $formData ) {
+ // Don't increase pingLimiter, just check for limit exceeded.
+ if ( $this->OATHUser->getUser()->pingLimiter( 'badoath', 0 ) ) {
+ // Arbitrary duration given here
+ return [ 'oathauth-throttled', Message::durationParam( 60 ) ];
+ }
+
+ if ( !$this->OATHUser->getKey()->verifyToken( $formData['token'], $this->OATHUser ) ) {
+ return [ 'oathauth-failedtovalidateoath' ];
+ }
+
+ $this->OATHUser->setKey( null );
+ $this->OATHRepository->remove( $this->OATHUser );
+
+ return true;
+ }
+
+ public function onSuccess() {
+ $this->getOutput()->addWikiMsg( 'oathauth-disabledoath' );
+ $this->getOutput()->returnToMain();
+ }
+}
diff --git a/www/wiki/extensions/OATHAuth/includes/special/SpecialOATHEnable.php b/www/wiki/extensions/OATHAuth/includes/special/SpecialOATHEnable.php
new file mode 100644
index 00000000..bf4b62e5
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/includes/special/SpecialOATHEnable.php
@@ -0,0 +1,241 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Special page to display key information to the user
+ *
+ * @ingroup Extensions
+ */
+class SpecialOATHEnable extends FormSpecialPage {
+ /** @var OATHUserRepository */
+ private $OATHRepository;
+
+ /** @var OATHUser */
+ private $OATHUser;
+
+ /**
+ * Initialize the OATH user based on the current local User object in the context
+ *
+ * @param OATHUserRepository $repository
+ * @param OATHUser $user
+ */
+ public function __construct( OATHUserRepository $repository, OATHUser $user ) {
+ parent::__construct( 'OATH', 'oathauth-enable', false );
+
+ $this->OATHRepository = $repository;
+ $this->OATHUser = $user;
+ }
+
+ public function doesWrites() {
+ return true;
+ }
+
+ /**
+ * Set the page title and add JavaScript RL modules
+ *
+ * @param HTMLForm $form
+ */
+ public function alterForm( HTMLForm $form ) {
+ $form->setMessagePrefix( 'oathauth' );
+ $form->setWrapperLegend( false );
+ $form->getOutput()->setPageTitle( $this->msg( 'oathauth-enable' ) );
+ $form->getOutput()->addModules( 'ext.oath.showqrcode' );
+ $form->getOutput()->addModuleStyles( 'ext.oath.showqrcode.styles' );
+ }
+
+ /**
+ * @return string
+ */
+ protected function getDisplayFormat() {
+ return 'ooui';
+ }
+
+ /**
+ * @return bool
+ */
+ public function requiresUnblock() {
+ return false;
+ }
+
+ /**
+ * Require users to be logged in
+ *
+ * @param User $user
+ *
+ * @return bool|void
+ */
+ protected function checkExecutePermissions( User $user ) {
+ parent::checkExecutePermissions( $user );
+
+ $this->requireLogin();
+ }
+
+ /**
+ * @return array[]
+ */
+ protected function getFormFields() {
+ $key = $this->getRequest()->getSessionData( 'oathauth_key' );
+
+ if ( $key === null ) {
+ $key = OATHAuthKey::newFromRandom();
+ $this->getRequest()->setSessionData( 'oathauth_key', $key );
+ }
+
+ $secret = $key->getSecret();
+ $label = "{$this->OATHUser->getIssuer()}:{$this->OATHUser->getAccount()}";
+ $qrcodeUrl = "otpauth://totp/"
+ . rawurlencode( $label )
+ . "?secret="
+ . rawurlencode( $secret )
+ . "&issuer="
+ . rawurlencode( $this->OATHUser->getIssuer() );
+
+ $qrcodeElement = Html::element( 'div', [
+ 'data-mw-qrcode-url' => $qrcodeUrl,
+ 'class' => 'mw-display-qrcode',
+ // Include width/height, so js won't re-arrange layout
+ // And non-js users will have this hidden with CSS
+ 'style' => 'width: 256px; height: 256px;'
+ ] );
+
+ return [
+ 'app' => [
+ 'type' => 'info',
+ 'default' => $this->msg( 'oathauth-step1-test' )->escaped(),
+ 'raw' => true,
+ 'section' => 'step1',
+ ],
+ 'qrcode' => [
+ 'type' => 'info',
+ 'default' => $qrcodeElement,
+ 'raw' => true,
+ 'section' => 'step2',
+ ],
+ 'manual' => [
+ 'type' => 'info',
+ 'label-message' => 'oathauth-step2alt',
+ 'default' =>
+ '<strong>' . $this->msg( 'oathauth-account' )->escaped() . '</strong><br/>'
+ . $this->OATHUser->getAccount() . '<br/><br/>'
+ . '<strong>' . $this->msg( 'oathauth-secret' )->escaped() . '</strong><br/>'
+ . '<kbd>' . $this->getSecretForDisplay( $key ) . '</kbd><br/>',
+ 'raw' => true,
+ 'section' => 'step2',
+ ],
+ 'scratchtokens' => [
+ 'type' => 'info',
+ 'default' =>
+ $this->msg( 'oathauth-scratchtokens' )
+ . $this->createResourceList( $this->getScratchTokensForDisplay( $key ) ),
+ 'raw' => true,
+ 'section' => 'step3',
+ ],
+ 'token' => [
+ 'type' => 'text',
+ 'default' => '',
+ 'label-message' => 'oathauth-entertoken',
+ 'name' => 'token',
+ 'section' => 'step4',
+ ],
+ 'returnto' => [
+ 'type' => 'hidden',
+ 'default' => $this->getRequest()->getVal( 'returnto' ),
+ 'name' => 'returnto',
+ ],
+ 'returntoquery' => [
+ 'type' => 'hidden',
+ 'default' => $this->getRequest()->getVal( 'returntoquery' ),
+ 'name' => 'returntoquery', ]
+ ];
+ }
+
+ /**
+ * @param array $formData
+ *
+ * @return array|bool
+ */
+ public function onSubmit( array $formData ) {
+ /** @var OATHAuthKey $key */
+ $key = $this->getRequest()->getSessionData( 'oathauth_key' );
+
+ if ( $key->isScratchToken( $formData['token'] ) ) {
+ // A scratch token is not allowed for enrollement
+ return [ 'oathauth-noscratchforvalidation' ];
+ }
+ if ( !$key->verifyToken( $formData['token'], $this->OATHUser ) ) {
+ return [ 'oathauth-failedtovalidateoath' ];
+ }
+
+ $this->getRequest()->setSessionData( 'oathauth_key', null );
+ $this->OATHUser->setKey( $key );
+ $this->OATHRepository->persist( $this->OATHUser );
+
+ return true;
+ }
+
+ public function onSuccess() {
+ $this->getOutput()->addWikiMsg( 'oathauth-validatedoath' );
+ $this->getOutput()->returnToMain();
+ }
+
+ /**
+ * @param $resources array
+ * @return string
+ */
+ private function createResourceList( $resources ) {
+ $resourceList = '';
+ foreach ( $resources as $resource ) {
+ $resourceList .= Html::rawElement( 'li', [], Html::rawElement( 'kbd', [], $resource ) );
+ }
+ return Html::rawElement( 'ul', [], $resourceList );
+ }
+
+ /**
+ * Retrieve the current secret for display purposes
+ *
+ * The characters of the token are split in groups of 4
+ *
+ * @param OATHAuthKey $key
+ * @return String
+ */
+ protected function getSecretForDisplay( OATHAuthKey $key ) {
+ return $this->tokenFormatterFunction( $key->getSecret() );
+ }
+
+ /**
+ * Retrieve current scratch tokens for display purposes
+ *
+ * The characters of the token are split in groups of 4
+ *
+ * @param OATHAuthKey $key
+ * @return string[]
+ */
+ protected function getScratchTokensForDisplay( OATHAuthKey $key ) {
+ return array_map( [ $this, 'tokenFormatterFunction' ], $key->getScratchTokens() );
+ }
+
+ /**
+ * Formats a key or scratch token by creating groups of 4 seperated by space characters
+ *
+ * @param string $token Token to format
+ * @return string The token formatted for display
+ */
+ private function tokenFormatterFunction( $token ) {
+ return implode( ' ', str_split( $token, 4 ) );
+ }
+}
diff --git a/www/wiki/extensions/OATHAuth/maintenance/disableOATHAuthForUser.php b/www/wiki/extensions/OATHAuth/maintenance/disableOATHAuthForUser.php
new file mode 100644
index 00000000..612f07d7
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/maintenance/disableOATHAuthForUser.php
@@ -0,0 +1,40 @@
+<?php
+
+if ( getenv( 'MW_INSTALL_PATH' ) ) {
+ $IP = getenv( 'MW_INSTALL_PATH' );
+} else {
+ $IP = __DIR__ . '/../../..';
+}
+require_once "$IP/maintenance/Maintenance.php";
+
+class DisableOATHAuthForUser extends Maintenance {
+ function __construct() {
+ parent::__construct();
+ $this->mDescription = 'Remove OATHAuth from a specific user';
+ $this->addArg( 'user', 'The username to remove OATHAuth from.' );
+ $this->requireExtension( 'OATHAuth' );
+ }
+
+ public function execute() {
+ $username = $this->getArg( 0 );
+
+ $user = User::newFromName( $username );
+ if ( $user && $user->getId() === 0 ) {
+ $this->error( "User $username doesn't exist!", 1 );
+ }
+
+ $repo = OATHAuthHooks::getOATHUserRepository();
+
+ $oathUser = $repo->findByUser( $user );
+
+ if ( $oathUser->getKey() === null ) {
+ $this->error( "User $username doesn't have OATHAuth enabled!", 1 );
+ }
+
+ $repo->remove( $oathUser );
+ $this->output( "OATHAuth disabled for $username.\n" );
+ }
+}
+
+$maintClass = "DisableOATHAuthForUser";
+require_once RUN_MAINTENANCE_IF_MAIN;
diff --git a/www/wiki/extensions/OATHAuth/maintenance/updateScratchTokenFormat.php b/www/wiki/extensions/OATHAuth/maintenance/updateScratchTokenFormat.php
new file mode 100644
index 00000000..81b4ea2d
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/maintenance/updateScratchTokenFormat.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * Update scratch_token column format
+ *
+ * Usage: php updateScratchTokenFormat.php
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @author Darian Anthony Patrick
+ * @ingroup Maintenance
+ */
+
+if ( getenv( 'MW_INSTALL_PATH' ) ) {
+ $IP = getenv( 'MW_INSTALL_PATH' );
+} else {
+ $IP = __DIR__ . '/../../..';
+}
+require_once "$IP/maintenance/Maintenance.php";
+
+class UpdateScratchTokenFormat extends Maintenance {
+ function __construct() {
+ parent::__construct();
+ $this->mDescription = 'Script to update scratch_token column format';
+ $this->requireExtension( 'OATHAuth' );
+ }
+
+ public function execute() {
+ $dbw = $this->getDB( DB_MASTER );
+ if ( !OATHAuthHooks::schemaUpdateOldUsers( $dbw ) ) {
+ $this->error( "Failed to update scratch_token rows.\n", 1 );
+ }
+ $this->output( "Done.\n" );
+ }
+}
+
+$maintClass = "UpdateScratchTokenFormat";
+require_once RUN_MAINTENANCE_IF_MAIN;
diff --git a/www/wiki/extensions/OATHAuth/modules/ext.oath.showqrcode.js b/www/wiki/extensions/OATHAuth/modules/ext.oath.showqrcode.js
new file mode 100644
index 00000000..9c7d2b52
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/modules/ext.oath.showqrcode.js
@@ -0,0 +1,6 @@
+(function ( $ ) {
+ $( function () {
+ var $elm = $( '.mw-display-qrcode' );
+ $elm.qrcode( $elm.data( 'mw-qrcode-url' ) );
+ } );
+} )( jQuery );
diff --git a/www/wiki/extensions/OATHAuth/modules/ext.oath.showqrcode.styles.css b/www/wiki/extensions/OATHAuth/modules/ext.oath.showqrcode.styles.css
new file mode 100644
index 00000000..0e2f48c4
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/modules/ext.oath.showqrcode.styles.css
@@ -0,0 +1,13 @@
+.client-nojs .mw-display-qrcode {
+ display: none;
+}
+
+kbd {
+ font-family: monospace, monospace;
+ white-space: nowrap;
+ font-size: larger;
+}
+
+fieldset {
+ page-break-inside: avoid;
+}
diff --git a/www/wiki/extensions/OATHAuth/modules/jquery.qrcode.js b/www/wiki/extensions/OATHAuth/modules/jquery.qrcode.js
new file mode 100644
index 00000000..15a1c663
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/modules/jquery.qrcode.js
@@ -0,0 +1,88 @@
+( function ( $ ) {
+ $.fn.qrcode = function ( options ) {
+ // if options is string,
+ if ( typeof options === 'string' ){
+ options = { text: options };
+ }
+
+ // set default values
+ // typeNumber < 1 for automatic calculation
+ options = $.extend( {}, {
+ render : "canvas",
+ width : 256,
+ height : 256,
+ typeNumber : -1,
+ correctLevel : QRErrorCorrectLevel.H,
+ background : "#ffffff",
+ foreground : "#000000"
+ }, options);
+
+ var createCanvas = function(){
+ // create the qrcode itself
+ var qrcode = new QRCode(options.typeNumber, options.correctLevel);
+ qrcode.addData(options.text);
+ qrcode.make();
+
+ // create canvas element
+ var canvas = document.createElement('canvas');
+ canvas.width = options.width;
+ canvas.height = options.height;
+ var ctx = canvas.getContext('2d');
+
+ // compute tileW/tileH based on options.width/options.height
+ var tileW = options.width / qrcode.getModuleCount();
+ var tileH = options.height / qrcode.getModuleCount();
+
+ // draw in the canvas
+ for( var row = 0; row < qrcode.getModuleCount(); row++ ){
+ for( var col = 0; col < qrcode.getModuleCount(); col++ ){
+ ctx.fillStyle = qrcode.isDark(row, col) ? options.foreground : options.background;
+ var w = (Math.ceil((col+1)*tileW) - Math.floor(col*tileW));
+ var h = (Math.ceil((row+1)*tileW) - Math.floor(row*tileW));
+ ctx.fillRect(Math.round(col*tileW),Math.round(row*tileH), w, h);
+ }
+ }
+ // return just built canvas
+ return canvas;
+ };
+
+ // from Jon-Carlos Rivera (https://github.com/imbcmdth)
+ var createTable = function(){
+ // create the qrcode itself
+ var qrcode = new QRCode(options.typeNumber, options.correctLevel);
+ qrcode.addData(options.text);
+ qrcode.make();
+
+ // create table element
+ var $table = $('<table></table>')
+ .css("width", options.width+"px")
+ .css("height", options.height+"px")
+ .css("border", "0px")
+ .css("border-collapse", "collapse")
+ .css('background-color', options.background);
+
+ // compute tileS percentage
+ var tileW = options.width / qrcode.getModuleCount();
+ var tileH = options.height / qrcode.getModuleCount();
+
+ // draw in the table
+ for(var row = 0; row < qrcode.getModuleCount(); row++ ){
+ var $row = $('<tr></tr>').css('height', tileH+"px").appendTo($table);
+
+ for(var col = 0; col < qrcode.getModuleCount(); col++ ){
+ $('<td></td>')
+ .css('width', tileW+"px")
+ .css('background-color', qrcode.isDark(row, col) ? options.foreground : options.background)
+ .appendTo($row);
+ }
+ }
+ // return just built canvas
+ return $table;
+ };
+
+ return this.each(function(){
+ var element = options.render === "canvas" ? createCanvas() : createTable();
+ $(element).appendTo(this);
+ } );
+ };
+}( jQuery ) );
diff --git a/www/wiki/extensions/OATHAuth/modules/qrcode.js b/www/wiki/extensions/OATHAuth/modules/qrcode.js
new file mode 100644
index 00000000..7775c90e
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/modules/qrcode.js
@@ -0,0 +1,1237 @@
+//---------------------------------------------------------------------
+// QRCode for JavaScript
+//
+// Copyright (c) 2009 Kazuhiko Arase
+//
+// URL: http://www.d-project.com/
+//
+// Licensed under the MIT license:
+// http://www.opensource.org/licenses/mit-license.php
+//
+// The word "QR Code" is registered trademark of
+// DENSO WAVE INCORPORATED
+// http://www.denso-wave.com/qrcode/faqpatent-e.html
+//
+//---------------------------------------------------------------------
+
+//---------------------------------------------------------------------
+// QR8bitByte
+//---------------------------------------------------------------------
+
+function QR8bitByte(data) {
+ this.mode = QRMode.MODE_8BIT_BYTE;
+ this.data = data;
+}
+
+QR8bitByte.prototype = {
+
+ getLength : function(buffer) {
+ return this.data.length;
+ },
+
+ write : function(buffer) {
+ for (var i = 0; i < this.data.length; i++) {
+ // not JIS ...
+ buffer.put(this.data.charCodeAt(i), 8);
+ }
+ }
+};
+
+//---------------------------------------------------------------------
+// QRCode
+//---------------------------------------------------------------------
+
+function QRCode(typeNumber, errorCorrectLevel) {
+ this.typeNumber = typeNumber;
+ this.errorCorrectLevel = errorCorrectLevel;
+ this.modules = null;
+ this.moduleCount = 0;
+ this.dataCache = null;
+ this.dataList = new Array();
+}
+
+QRCode.prototype = {
+
+ addData : function(data) {
+ var newData = new QR8bitByte(data);
+ this.dataList.push(newData);
+ this.dataCache = null;
+ },
+
+ isDark : function(row, col) {
+ if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) {
+ throw new Error(row + "," + col);
+ }
+ return this.modules[row][col];
+ },
+
+ getModuleCount : function() {
+ return this.moduleCount;
+ },
+
+ make : function() {
+ // Calculate automatically typeNumber if provided is < 1
+ if (this.typeNumber < 1 ){
+ var typeNumber = 1;
+ for (typeNumber = 1; typeNumber < 40; typeNumber++) {
+ var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, this.errorCorrectLevel);
+
+ var buffer = new QRBitBuffer();
+ var totalDataCount = 0;
+ for (var i = 0; i < rsBlocks.length; i++) {
+ totalDataCount += rsBlocks[i].dataCount;
+ }
+
+ for (var i = 0; i < this.dataList.length; i++) {
+ var data = this.dataList[i];
+ buffer.put(data.mode, 4);
+ buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber) );
+ data.write(buffer);
+ }
+ if (buffer.getLengthInBits() <= totalDataCount * 8)
+ break;
+ }
+ this.typeNumber = typeNumber;
+ }
+ this.makeImpl(false, this.getBestMaskPattern() );
+ },
+
+ makeImpl : function(test, maskPattern) {
+
+ this.moduleCount = this.typeNumber * 4 + 17;
+ this.modules = new Array(this.moduleCount);
+
+ for (var row = 0; row < this.moduleCount; row++) {
+
+ this.modules[row] = new Array(this.moduleCount);
+
+ for (var col = 0; col < this.moduleCount; col++) {
+ this.modules[row][col] = null;//(col + row) % 3;
+ }
+ }
+
+ this.setupPositionProbePattern(0, 0);
+ this.setupPositionProbePattern(this.moduleCount - 7, 0);
+ this.setupPositionProbePattern(0, this.moduleCount - 7);
+ this.setupPositionAdjustPattern();
+ this.setupTimingPattern();
+ this.setupTypeInfo(test, maskPattern);
+
+ if (this.typeNumber >= 7) {
+ this.setupTypeNumber(test);
+ }
+
+ if (this.dataCache == null) {
+ this.dataCache = QRCode.createData(this.typeNumber, this.errorCorrectLevel, this.dataList);
+ }
+
+ this.mapData(this.dataCache, maskPattern);
+ },
+
+ setupPositionProbePattern : function(row, col) {
+
+ for (var r = -1; r <= 7; r++) {
+
+ if (row + r <= -1 || this.moduleCount <= row + r) continue;
+
+ for (var c = -1; c <= 7; c++) {
+
+ if (col + c <= -1 || this.moduleCount <= col + c) continue;
+
+ if ( (0 <= r && r <= 6 && (c == 0 || c == 6) )
+ || (0 <= c && c <= 6 && (r == 0 || r == 6) )
+ || (2 <= r && r <= 4 && 2 <= c && c <= 4) ) {
+ this.modules[row + r][col + c] = true;
+ } else {
+ this.modules[row + r][col + c] = false;
+ }
+ }
+ }
+ },
+
+ getBestMaskPattern : function() {
+
+ var minLostPoint = 0;
+ var pattern = 0;
+
+ for (var i = 0; i < 8; i++) {
+
+ this.makeImpl(true, i);
+
+ var lostPoint = QRUtil.getLostPoint(this);
+
+ if (i == 0 || minLostPoint > lostPoint) {
+ minLostPoint = lostPoint;
+ pattern = i;
+ }
+ }
+
+ return pattern;
+ },
+
+ createMovieClip : function(target_mc, instance_name, depth) {
+
+ var qr_mc = target_mc.createEmptyMovieClip(instance_name, depth);
+ var cs = 1;
+
+ this.make();
+
+ for (var row = 0; row < this.modules.length; row++) {
+
+ var y = row * cs;
+
+ for (var col = 0; col < this.modules[row].length; col++) {
+
+ var x = col * cs;
+ var dark = this.modules[row][col];
+
+ if (dark) {
+ qr_mc.beginFill(0, 100);
+ qr_mc.moveTo(x, y);
+ qr_mc.lineTo(x + cs, y);
+ qr_mc.lineTo(x + cs, y + cs);
+ qr_mc.lineTo(x, y + cs);
+ qr_mc.endFill();
+ }
+ }
+ }
+
+ return qr_mc;
+ },
+
+ setupTimingPattern : function() {
+
+ for (var r = 8; r < this.moduleCount - 8; r++) {
+ if (this.modules[r][6] != null) {
+ continue;
+ }
+ this.modules[r][6] = (r % 2 == 0);
+ }
+
+ for (var c = 8; c < this.moduleCount - 8; c++) {
+ if (this.modules[6][c] != null) {
+ continue;
+ }
+ this.modules[6][c] = (c % 2 == 0);
+ }
+ },
+
+ setupPositionAdjustPattern : function() {
+
+ var pos = QRUtil.getPatternPosition(this.typeNumber);
+
+ for (var i = 0; i < pos.length; i++) {
+
+ for (var j = 0; j < pos.length; j++) {
+
+ var row = pos[i];
+ var col = pos[j];
+
+ if (this.modules[row][col] != null) {
+ continue;
+ }
+
+ for (var r = -2; r <= 2; r++) {
+
+ for (var c = -2; c <= 2; c++) {
+
+ if (r == -2 || r == 2 || c == -2 || c == 2
+ || (r == 0 && c == 0) ) {
+ this.modules[row + r][col + c] = true;
+ } else {
+ this.modules[row + r][col + c] = false;
+ }
+ }
+ }
+ }
+ }
+ },
+
+ setupTypeNumber : function(test) {
+
+ var bits = QRUtil.getBCHTypeNumber(this.typeNumber);
+
+ for (var i = 0; i < 18; i++) {
+ var mod = (!test && ( (bits >> i) & 1) == 1);
+ this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod;
+ }
+
+ for (var i = 0; i < 18; i++) {
+ var mod = (!test && ( (bits >> i) & 1) == 1);
+ this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
+ }
+ },
+
+ setupTypeInfo : function(test, maskPattern) {
+
+ var data = (this.errorCorrectLevel << 3) | maskPattern;
+ var bits = QRUtil.getBCHTypeInfo(data);
+
+ // vertical
+ for (var i = 0; i < 15; i++) {
+
+ var mod = (!test && ( (bits >> i) & 1) == 1);
+
+ if (i < 6) {
+ this.modules[i][8] = mod;
+ } else if (i < 8) {
+ this.modules[i + 1][8] = mod;
+ } else {
+ this.modules[this.moduleCount - 15 + i][8] = mod;
+ }
+ }
+
+ // horizontal
+ for (var i = 0; i < 15; i++) {
+
+ var mod = (!test && ( (bits >> i) & 1) == 1);
+
+ if (i < 8) {
+ this.modules[8][this.moduleCount - i - 1] = mod;
+ } else if (i < 9) {
+ this.modules[8][15 - i - 1 + 1] = mod;
+ } else {
+ this.modules[8][15 - i - 1] = mod;
+ }
+ }
+
+ // fixed module
+ this.modules[this.moduleCount - 8][8] = (!test);
+
+ },
+
+ mapData : function(data, maskPattern) {
+
+ var inc = -1;
+ var row = this.moduleCount - 1;
+ var bitIndex = 7;
+ var byteIndex = 0;
+
+ for (var col = this.moduleCount - 1; col > 0; col -= 2) {
+
+ if (col == 6) col--;
+
+ while (true) {
+
+ for (var c = 0; c < 2; c++) {
+
+ if (this.modules[row][col - c] == null) {
+
+ var dark = false;
+
+ if (byteIndex < data.length) {
+ dark = ( ( (data[byteIndex] >>> bitIndex) & 1) == 1);
+ }
+
+ var mask = QRUtil.getMask(maskPattern, row, col - c);
+
+ if (mask) {
+ dark = !dark;
+ }
+
+ this.modules[row][col - c] = dark;
+ bitIndex--;
+
+ if (bitIndex == -1) {
+ byteIndex++;
+ bitIndex = 7;
+ }
+ }
+ }
+
+ row += inc;
+
+ if (row < 0 || this.moduleCount <= row) {
+ row -= inc;
+ inc = -inc;
+ break;
+ }
+ }
+ }
+
+ }
+
+};
+
+QRCode.PAD0 = 0xEC;
+QRCode.PAD1 = 0x11;
+
+QRCode.createData = function(typeNumber, errorCorrectLevel, dataList) {
+
+ var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel);
+
+ var buffer = new QRBitBuffer();
+
+ for (var i = 0; i < dataList.length; i++) {
+ var data = dataList[i];
+ buffer.put(data.mode, 4);
+ buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber) );
+ data.write(buffer);
+ }
+
+ // calc num max data.
+ var totalDataCount = 0;
+ for (var i = 0; i < rsBlocks.length; i++) {
+ totalDataCount += rsBlocks[i].dataCount;
+ }
+
+ if (buffer.getLengthInBits() > totalDataCount * 8) {
+ throw new Error("code length overflow. ("
+ + buffer.getLengthInBits()
+ + ">"
+ + totalDataCount * 8
+ + ")");
+ }
+
+ // end code
+ if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
+ buffer.put(0, 4);
+ }
+
+ // padding
+ while (buffer.getLengthInBits() % 8 != 0) {
+ buffer.putBit(false);
+ }
+
+ // padding
+ while (true) {
+
+ if (buffer.getLengthInBits() >= totalDataCount * 8) {
+ break;
+ }
+ buffer.put(QRCode.PAD0, 8);
+
+ if (buffer.getLengthInBits() >= totalDataCount * 8) {
+ break;
+ }
+ buffer.put(QRCode.PAD1, 8);
+ }
+
+ return QRCode.createBytes(buffer, rsBlocks);
+}
+
+QRCode.createBytes = function(buffer, rsBlocks) {
+
+ var offset = 0;
+
+ var maxDcCount = 0;
+ var maxEcCount = 0;
+
+ var dcdata = new Array(rsBlocks.length);
+ var ecdata = new Array(rsBlocks.length);
+
+ for (var r = 0; r < rsBlocks.length; r++) {
+
+ var dcCount = rsBlocks[r].dataCount;
+ var ecCount = rsBlocks[r].totalCount - dcCount;
+
+ maxDcCount = Math.max(maxDcCount, dcCount);
+ maxEcCount = Math.max(maxEcCount, ecCount);
+
+ dcdata[r] = new Array(dcCount);
+
+ for (var i = 0; i < dcdata[r].length; i++) {
+ dcdata[r][i] = 0xff & buffer.buffer[i + offset];
+ }
+ offset += dcCount;
+
+ var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
+ var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1);
+
+ var modPoly = rawPoly.mod(rsPoly);
+ ecdata[r] = new Array(rsPoly.getLength() - 1);
+ for (var i = 0; i < ecdata[r].length; i++) {
+ var modIndex = i + modPoly.getLength() - ecdata[r].length;
+ ecdata[r][i] = (modIndex >= 0)? modPoly.get(modIndex) : 0;
+ }
+
+ }
+
+ var totalCodeCount = 0;
+ for (var i = 0; i < rsBlocks.length; i++) {
+ totalCodeCount += rsBlocks[i].totalCount;
+ }
+
+ var data = new Array(totalCodeCount);
+ var index = 0;
+
+ for (var i = 0; i < maxDcCount; i++) {
+ for (var r = 0; r < rsBlocks.length; r++) {
+ if (i < dcdata[r].length) {
+ data[index++] = dcdata[r][i];
+ }
+ }
+ }
+
+ for (var i = 0; i < maxEcCount; i++) {
+ for (var r = 0; r < rsBlocks.length; r++) {
+ if (i < ecdata[r].length) {
+ data[index++] = ecdata[r][i];
+ }
+ }
+ }
+
+ return data;
+
+}
+
+//---------------------------------------------------------------------
+// QRMode
+//---------------------------------------------------------------------
+
+var QRMode = {
+ MODE_NUMBER : 1 << 0,
+ MODE_ALPHA_NUM : 1 << 1,
+ MODE_8BIT_BYTE : 1 << 2,
+ MODE_KANJI : 1 << 3
+};
+
+//---------------------------------------------------------------------
+// QRErrorCorrectLevel
+//---------------------------------------------------------------------
+
+var QRErrorCorrectLevel = {
+ L : 1,
+ M : 0,
+ Q : 3,
+ H : 2
+};
+
+//---------------------------------------------------------------------
+// QRMaskPattern
+//---------------------------------------------------------------------
+
+var QRMaskPattern = {
+ PATTERN000 : 0,
+ PATTERN001 : 1,
+ PATTERN010 : 2,
+ PATTERN011 : 3,
+ PATTERN100 : 4,
+ PATTERN101 : 5,
+ PATTERN110 : 6,
+ PATTERN111 : 7
+};
+
+//---------------------------------------------------------------------
+// QRUtil
+//---------------------------------------------------------------------
+
+var QRUtil = {
+
+ PATTERN_POSITION_TABLE : [
+ [],
+ [6, 18],
+ [6, 22],
+ [6, 26],
+ [6, 30],
+ [6, 34],
+ [6, 22, 38],
+ [6, 24, 42],
+ [6, 26, 46],
+ [6, 28, 50],
+ [6, 30, 54],
+ [6, 32, 58],
+ [6, 34, 62],
+ [6, 26, 46, 66],
+ [6, 26, 48, 70],
+ [6, 26, 50, 74],
+ [6, 30, 54, 78],
+ [6, 30, 56, 82],
+ [6, 30, 58, 86],
+ [6, 34, 62, 90],
+ [6, 28, 50, 72, 94],
+ [6, 26, 50, 74, 98],
+ [6, 30, 54, 78, 102],
+ [6, 28, 54, 80, 106],
+ [6, 32, 58, 84, 110],
+ [6, 30, 58, 86, 114],
+ [6, 34, 62, 90, 118],
+ [6, 26, 50, 74, 98, 122],
+ [6, 30, 54, 78, 102, 126],
+ [6, 26, 52, 78, 104, 130],
+ [6, 30, 56, 82, 108, 134],
+ [6, 34, 60, 86, 112, 138],
+ [6, 30, 58, 86, 114, 142],
+ [6, 34, 62, 90, 118, 146],
+ [6, 30, 54, 78, 102, 126, 150],
+ [6, 24, 50, 76, 102, 128, 154],
+ [6, 28, 54, 80, 106, 132, 158],
+ [6, 32, 58, 84, 110, 136, 162],
+ [6, 26, 54, 82, 110, 138, 166],
+ [6, 30, 58, 86, 114, 142, 170]
+ ],
+
+ G15 : (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0),
+ G18 : (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0),
+ G15_MASK : (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1),
+
+ getBCHTypeInfo : function(data) {
+ var d = data << 10;
+ while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {
+ d ^= (QRUtil.G15 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) ) );
+ }
+ return ( (data << 10) | d) ^ QRUtil.G15_MASK;
+ },
+
+ getBCHTypeNumber : function(data) {
+ var d = data << 12;
+ while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {
+ d ^= (QRUtil.G18 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) ) );
+ }
+ return (data << 12) | d;
+ },
+
+ getBCHDigit : function(data) {
+
+ var digit = 0;
+
+ while (data != 0) {
+ digit++;
+ data >>>= 1;
+ }
+
+ return digit;
+ },
+
+ getPatternPosition : function(typeNumber) {
+ return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];
+ },
+
+ getMask : function(maskPattern, i, j) {
+
+ switch (maskPattern) {
+
+ case QRMaskPattern.PATTERN000 : return (i + j) % 2 == 0;
+ case QRMaskPattern.PATTERN001 : return i % 2 == 0;
+ case QRMaskPattern.PATTERN010 : return j % 3 == 0;
+ case QRMaskPattern.PATTERN011 : return (i + j) % 3 == 0;
+ case QRMaskPattern.PATTERN100 : return (Math.floor(i / 2) + Math.floor(j / 3) ) % 2 == 0;
+ case QRMaskPattern.PATTERN101 : return (i * j) % 2 + (i * j) % 3 == 0;
+ case QRMaskPattern.PATTERN110 : return ( (i * j) % 2 + (i * j) % 3) % 2 == 0;
+ case QRMaskPattern.PATTERN111 : return ( (i * j) % 3 + (i + j) % 2) % 2 == 0;
+
+ default :
+ throw new Error("bad maskPattern:" + maskPattern);
+ }
+ },
+
+ getErrorCorrectPolynomial : function(errorCorrectLength) {
+
+ var a = new QRPolynomial([1], 0);
+
+ for (var i = 0; i < errorCorrectLength; i++) {
+ a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0) );
+ }
+
+ return a;
+ },
+
+ getLengthInBits : function(mode, type) {
+
+ if (1 <= type && type < 10) {
+
+ // 1 - 9
+
+ switch(mode) {
+ case QRMode.MODE_NUMBER : return 10;
+ case QRMode.MODE_ALPHA_NUM : return 9;
+ case QRMode.MODE_8BIT_BYTE : return 8;
+ case QRMode.MODE_KANJI : return 8;
+ default :
+ throw new Error("mode:" + mode);
+ }
+
+ } else if (type < 27) {
+
+ // 10 - 26
+
+ switch(mode) {
+ case QRMode.MODE_NUMBER : return 12;
+ case QRMode.MODE_ALPHA_NUM : return 11;
+ case QRMode.MODE_8BIT_BYTE : return 16;
+ case QRMode.MODE_KANJI : return 10;
+ default :
+ throw new Error("mode:" + mode);
+ }
+
+ } else if (type < 41) {
+
+ // 27 - 40
+
+ switch(mode) {
+ case QRMode.MODE_NUMBER : return 14;
+ case QRMode.MODE_ALPHA_NUM : return 13;
+ case QRMode.MODE_8BIT_BYTE : return 16;
+ case QRMode.MODE_KANJI : return 12;
+ default :
+ throw new Error("mode:" + mode);
+ }
+
+ } else {
+ throw new Error("type:" + type);
+ }
+ },
+
+ getLostPoint : function(qrCode) {
+
+ var moduleCount = qrCode.getModuleCount();
+
+ var lostPoint = 0;
+
+ // LEVEL1
+
+ for (var row = 0; row < moduleCount; row++) {
+
+ for (var col = 0; col < moduleCount; col++) {
+
+ var sameCount = 0;
+ var dark = qrCode.isDark(row, col);
+
+ for (var r = -1; r <= 1; r++) {
+
+ if (row + r < 0 || moduleCount <= row + r) {
+ continue;
+ }
+
+ for (var c = -1; c <= 1; c++) {
+
+ if (col + c < 0 || moduleCount <= col + c) {
+ continue;
+ }
+
+ if (r == 0 && c == 0) {
+ continue;
+ }
+
+ if (dark == qrCode.isDark(row + r, col + c) ) {
+ sameCount++;
+ }
+ }
+ }
+
+ if (sameCount > 5) {
+ lostPoint += (3 + sameCount - 5);
+ }
+ }
+ }
+
+ // LEVEL2
+
+ for (var row = 0; row < moduleCount - 1; row++) {
+ for (var col = 0; col < moduleCount - 1; col++) {
+ var count = 0;
+ if (qrCode.isDark(row, col ) ) count++;
+ if (qrCode.isDark(row + 1, col ) ) count++;
+ if (qrCode.isDark(row, col + 1) ) count++;
+ if (qrCode.isDark(row + 1, col + 1) ) count++;
+ if (count == 0 || count == 4) {
+ lostPoint += 3;
+ }
+ }
+ }
+
+ // LEVEL3
+
+ for (var row = 0; row < moduleCount; row++) {
+ for (var col = 0; col < moduleCount - 6; col++) {
+ if (qrCode.isDark(row, col)
+ && !qrCode.isDark(row, col + 1)
+ && qrCode.isDark(row, col + 2)
+ && qrCode.isDark(row, col + 3)
+ && qrCode.isDark(row, col + 4)
+ && !qrCode.isDark(row, col + 5)
+ && qrCode.isDark(row, col + 6) ) {
+ lostPoint += 40;
+ }
+ }
+ }
+
+ for (var col = 0; col < moduleCount; col++) {
+ for (var row = 0; row < moduleCount - 6; row++) {
+ if (qrCode.isDark(row, col)
+ && !qrCode.isDark(row + 1, col)
+ && qrCode.isDark(row + 2, col)
+ && qrCode.isDark(row + 3, col)
+ && qrCode.isDark(row + 4, col)
+ && !qrCode.isDark(row + 5, col)
+ && qrCode.isDark(row + 6, col) ) {
+ lostPoint += 40;
+ }
+ }
+ }
+
+ // LEVEL4
+
+ var darkCount = 0;
+
+ for (var col = 0; col < moduleCount; col++) {
+ for (var row = 0; row < moduleCount; row++) {
+ if (qrCode.isDark(row, col) ) {
+ darkCount++;
+ }
+ }
+ }
+
+ var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
+ lostPoint += ratio * 10;
+
+ return lostPoint;
+ }
+
+};
+
+
+//---------------------------------------------------------------------
+// QRMath
+//---------------------------------------------------------------------
+
+var QRMath = {
+
+ glog : function(n) {
+
+ if (n < 1) {
+ throw new Error("glog(" + n + ")");
+ }
+
+ return QRMath.LOG_TABLE[n];
+ },
+
+ gexp : function(n) {
+
+ while (n < 0) {
+ n += 255;
+ }
+
+ while (n >= 256) {
+ n -= 255;
+ }
+
+ return QRMath.EXP_TABLE[n];
+ },
+
+ EXP_TABLE : new Array(256),
+
+ LOG_TABLE : new Array(256)
+
+};
+
+for (var i = 0; i < 8; i++) {
+ QRMath.EXP_TABLE[i] = 1 << i;
+}
+for (var i = 8; i < 256; i++) {
+ QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4]
+ ^ QRMath.EXP_TABLE[i - 5]
+ ^ QRMath.EXP_TABLE[i - 6]
+ ^ QRMath.EXP_TABLE[i - 8];
+}
+for (var i = 0; i < 255; i++) {
+ QRMath.LOG_TABLE[QRMath.EXP_TABLE[i] ] = i;
+}
+
+//---------------------------------------------------------------------
+// QRPolynomial
+//---------------------------------------------------------------------
+
+function QRPolynomial(num, shift) {
+
+ if (num.length == undefined) {
+ throw new Error(num.length + "/" + shift);
+ }
+
+ var offset = 0;
+
+ while (offset < num.length && num[offset] == 0) {
+ offset++;
+ }
+
+ this.num = new Array(num.length - offset + shift);
+ for (var i = 0; i < num.length - offset; i++) {
+ this.num[i] = num[i + offset];
+ }
+}
+
+QRPolynomial.prototype = {
+
+ get : function(index) {
+ return this.num[index];
+ },
+
+ getLength : function() {
+ return this.num.length;
+ },
+
+ multiply : function(e) {
+
+ var num = new Array(this.getLength() + e.getLength() - 1);
+
+ for (var i = 0; i < this.getLength(); i++) {
+ for (var j = 0; j < e.getLength(); j++) {
+ num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i) ) + QRMath.glog(e.get(j) ) );
+ }
+ }
+
+ return new QRPolynomial(num, 0);
+ },
+
+ mod : function(e) {
+
+ if (this.getLength() - e.getLength() < 0) {
+ return this;
+ }
+
+ var ratio = QRMath.glog(this.get(0) ) - QRMath.glog(e.get(0) );
+
+ var num = new Array(this.getLength() );
+
+ for (var i = 0; i < this.getLength(); i++) {
+ num[i] = this.get(i);
+ }
+
+ for (var i = 0; i < e.getLength(); i++) {
+ num[i] ^= QRMath.gexp(QRMath.glog(e.get(i) ) + ratio);
+ }
+
+ // recursive call
+ return new QRPolynomial(num, 0).mod(e);
+ }
+};
+
+//---------------------------------------------------------------------
+// QRRSBlock
+//---------------------------------------------------------------------
+
+function QRRSBlock(totalCount, dataCount) {
+ this.totalCount = totalCount;
+ this.dataCount = dataCount;
+}
+
+QRRSBlock.RS_BLOCK_TABLE = [
+
+ // L
+ // M
+ // Q
+ // H
+
+ // 1
+ [1, 26, 19],
+ [1, 26, 16],
+ [1, 26, 13],
+ [1, 26, 9],
+
+ // 2
+ [1, 44, 34],
+ [1, 44, 28],
+ [1, 44, 22],
+ [1, 44, 16],
+
+ // 3
+ [1, 70, 55],
+ [1, 70, 44],
+ [2, 35, 17],
+ [2, 35, 13],
+
+ // 4
+ [1, 100, 80],
+ [2, 50, 32],
+ [2, 50, 24],
+ [4, 25, 9],
+
+ // 5
+ [1, 134, 108],
+ [2, 67, 43],
+ [2, 33, 15, 2, 34, 16],
+ [2, 33, 11, 2, 34, 12],
+
+ // 6
+ [2, 86, 68],
+ [4, 43, 27],
+ [4, 43, 19],
+ [4, 43, 15],
+
+ // 7
+ [2, 98, 78],
+ [4, 49, 31],
+ [2, 32, 14, 4, 33, 15],
+ [4, 39, 13, 1, 40, 14],
+
+ // 8
+ [2, 121, 97],
+ [2, 60, 38, 2, 61, 39],
+ [4, 40, 18, 2, 41, 19],
+ [4, 40, 14, 2, 41, 15],
+
+ // 9
+ [2, 146, 116],
+ [3, 58, 36, 2, 59, 37],
+ [4, 36, 16, 4, 37, 17],
+ [4, 36, 12, 4, 37, 13],
+
+ // 10
+ [2, 86, 68, 2, 87, 69],
+ [4, 69, 43, 1, 70, 44],
+ [6, 43, 19, 2, 44, 20],
+ [6, 43, 15, 2, 44, 16],
+
+ // 11
+ [4, 101, 81],
+ [1, 80, 50, 4, 81, 51],
+ [4, 50, 22, 4, 51, 23],
+ [3, 36, 12, 8, 37, 13],
+
+ // 12
+ [2, 116, 92, 2, 117, 93],
+ [6, 58, 36, 2, 59, 37],
+ [4, 46, 20, 6, 47, 21],
+ [7, 42, 14, 4, 43, 15],
+
+ // 13
+ [4, 133, 107],
+ [8, 59, 37, 1, 60, 38],
+ [8, 44, 20, 4, 45, 21],
+ [12, 33, 11, 4, 34, 12],
+
+ // 14
+ [3, 145, 115, 1, 146, 116],
+ [4, 64, 40, 5, 65, 41],
+ [11, 36, 16, 5, 37, 17],
+ [11, 36, 12, 5, 37, 13],
+
+ // 15
+ [5, 109, 87, 1, 110, 88],
+ [5, 65, 41, 5, 66, 42],
+ [5, 54, 24, 7, 55, 25],
+ [11, 36, 12],
+
+ // 16
+ [5, 122, 98, 1, 123, 99],
+ [7, 73, 45, 3, 74, 46],
+ [15, 43, 19, 2, 44, 20],
+ [3, 45, 15, 13, 46, 16],
+
+ // 17
+ [1, 135, 107, 5, 136, 108],
+ [10, 74, 46, 1, 75, 47],
+ [1, 50, 22, 15, 51, 23],
+ [2, 42, 14, 17, 43, 15],
+
+ // 18
+ [5, 150, 120, 1, 151, 121],
+ [9, 69, 43, 4, 70, 44],
+ [17, 50, 22, 1, 51, 23],
+ [2, 42, 14, 19, 43, 15],
+
+ // 19
+ [3, 141, 113, 4, 142, 114],
+ [3, 70, 44, 11, 71, 45],
+ [17, 47, 21, 4, 48, 22],
+ [9, 39, 13, 16, 40, 14],
+
+ // 20
+ [3, 135, 107, 5, 136, 108],
+ [3, 67, 41, 13, 68, 42],
+ [15, 54, 24, 5, 55, 25],
+ [15, 43, 15, 10, 44, 16],
+
+ // 21
+ [4, 144, 116, 4, 145, 117],
+ [17, 68, 42],
+ [17, 50, 22, 6, 51, 23],
+ [19, 46, 16, 6, 47, 17],
+
+ // 22
+ [2, 139, 111, 7, 140, 112],
+ [17, 74, 46],
+ [7, 54, 24, 16, 55, 25],
+ [34, 37, 13],
+
+ // 23
+ [4, 151, 121, 5, 152, 122],
+ [4, 75, 47, 14, 76, 48],
+ [11, 54, 24, 14, 55, 25],
+ [16, 45, 15, 14, 46, 16],
+
+ // 24
+ [6, 147, 117, 4, 148, 118],
+ [6, 73, 45, 14, 74, 46],
+ [11, 54, 24, 16, 55, 25],
+ [30, 46, 16, 2, 47, 17],
+
+ // 25
+ [8, 132, 106, 4, 133, 107],
+ [8, 75, 47, 13, 76, 48],
+ [7, 54, 24, 22, 55, 25],
+ [22, 45, 15, 13, 46, 16],
+
+ // 26
+ [10, 142, 114, 2, 143, 115],
+ [19, 74, 46, 4, 75, 47],
+ [28, 50, 22, 6, 51, 23],
+ [33, 46, 16, 4, 47, 17],
+
+ // 27
+ [8, 152, 122, 4, 153, 123],
+ [22, 73, 45, 3, 74, 46],
+ [8, 53, 23, 26, 54, 24],
+ [12, 45, 15, 28, 46, 16],
+
+ // 28
+ [3, 147, 117, 10, 148, 118],
+ [3, 73, 45, 23, 74, 46],
+ [4, 54, 24, 31, 55, 25],
+ [11, 45, 15, 31, 46, 16],
+
+ // 29
+ [7, 146, 116, 7, 147, 117],
+ [21, 73, 45, 7, 74, 46],
+ [1, 53, 23, 37, 54, 24],
+ [19, 45, 15, 26, 46, 16],
+
+ // 30
+ [5, 145, 115, 10, 146, 116],
+ [19, 75, 47, 10, 76, 48],
+ [15, 54, 24, 25, 55, 25],
+ [23, 45, 15, 25, 46, 16],
+
+ // 31
+ [13, 145, 115, 3, 146, 116],
+ [2, 74, 46, 29, 75, 47],
+ [42, 54, 24, 1, 55, 25],
+ [23, 45, 15, 28, 46, 16],
+
+ // 32
+ [17, 145, 115],
+ [10, 74, 46, 23, 75, 47],
+ [10, 54, 24, 35, 55, 25],
+ [19, 45, 15, 35, 46, 16],
+
+ // 33
+ [17, 145, 115, 1, 146, 116],
+ [14, 74, 46, 21, 75, 47],
+ [29, 54, 24, 19, 55, 25],
+ [11, 45, 15, 46, 46, 16],
+
+ // 34
+ [13, 145, 115, 6, 146, 116],
+ [14, 74, 46, 23, 75, 47],
+ [44, 54, 24, 7, 55, 25],
+ [59, 46, 16, 1, 47, 17],
+
+ // 35
+ [12, 151, 121, 7, 152, 122],
+ [12, 75, 47, 26, 76, 48],
+ [39, 54, 24, 14, 55, 25],
+ [22, 45, 15, 41, 46, 16],
+
+ // 36
+ [6, 151, 121, 14, 152, 122],
+ [6, 75, 47, 34, 76, 48],
+ [46, 54, 24, 10, 55, 25],
+ [2, 45, 15, 64, 46, 16],
+
+ // 37
+ [17, 152, 122, 4, 153, 123],
+ [29, 74, 46, 14, 75, 47],
+ [49, 54, 24, 10, 55, 25],
+ [24, 45, 15, 46, 46, 16],
+
+ // 38
+ [4, 152, 122, 18, 153, 123],
+ [13, 74, 46, 32, 75, 47],
+ [48, 54, 24, 14, 55, 25],
+ [42, 45, 15, 32, 46, 16],
+
+ // 39
+ [20, 147, 117, 4, 148, 118],
+ [40, 75, 47, 7, 76, 48],
+ [43, 54, 24, 22, 55, 25],
+ [10, 45, 15, 67, 46, 16],
+
+ // 40
+ [19, 148, 118, 6, 149, 119],
+ [18, 75, 47, 31, 76, 48],
+ [34, 54, 24, 34, 55, 25],
+ [20, 45, 15, 61, 46, 16]
+];
+
+QRRSBlock.getRSBlocks = function(typeNumber, errorCorrectLevel) {
+
+ var rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel);
+
+ if (rsBlock == undefined) {
+ throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + errorCorrectLevel);
+ }
+
+ var length = rsBlock.length / 3;
+
+ var list = new Array();
+
+ for (var i = 0; i < length; i++) {
+
+ var count = rsBlock[i * 3 + 0];
+ var totalCount = rsBlock[i * 3 + 1];
+ var dataCount = rsBlock[i * 3 + 2];
+
+ for (var j = 0; j < count; j++) {
+ list.push(new QRRSBlock(totalCount, dataCount) );
+ }
+ }
+
+ return list;
+}
+
+QRRSBlock.getRsBlockTable = function(typeNumber, errorCorrectLevel) {
+
+ switch(errorCorrectLevel) {
+ case QRErrorCorrectLevel.L :
+ return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];
+ case QRErrorCorrectLevel.M :
+ return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
+ case QRErrorCorrectLevel.Q :
+ return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
+ case QRErrorCorrectLevel.H :
+ return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
+ default :
+ return undefined;
+ }
+}
+
+//---------------------------------------------------------------------
+// QRBitBuffer
+//---------------------------------------------------------------------
+
+function QRBitBuffer() {
+ this.buffer = new Array();
+ this.length = 0;
+}
+
+QRBitBuffer.prototype = {
+
+ get : function(index) {
+ var bufIndex = Math.floor(index / 8);
+ return ( (this.buffer[bufIndex] >>> (7 - index % 8) ) & 1) == 1;
+ },
+
+ put : function(num, length) {
+ for (var i = 0; i < length; i++) {
+ this.putBit( ( (num >>> (length - i - 1) ) & 1) == 1);
+ }
+ },
+
+ getLengthInBits : function() {
+ return this.length;
+ },
+
+ putBit : function(bit) {
+
+ var bufIndex = Math.floor(this.length / 8);
+ if (this.buffer.length <= bufIndex) {
+ this.buffer.push(0);
+ }
+
+ if (bit) {
+ this.buffer[bufIndex] |= (0x80 >>> (this.length % 8) );
+ }
+
+ this.length++;
+ }
+};
diff --git a/www/wiki/extensions/OATHAuth/sql/mssql/tables.sql b/www/wiki/extensions/OATHAuth/sql/mssql/tables.sql
new file mode 100644
index 00000000..f4db281c
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/sql/mssql/tables.sql
@@ -0,0 +1,10 @@
+CREATE TABLE /*_*/oathauth_users (
+ -- User ID
+ id INT NOT NULL PRIMARY KEY IDENTITY(0,1),
+
+ -- Secret key
+ secret NVARCHAR(255) NULL DEFAULT NULL,
+
+ -- Scratch tokens
+ scratch_tokens varbinary(511) NULL DEFAULT NULL
+);
diff --git a/www/wiki/extensions/OATHAuth/sql/mysql/patch-remove_reset.sql b/www/wiki/extensions/OATHAuth/sql/mysql/patch-remove_reset.sql
new file mode 100644
index 00000000..6b37a344
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/sql/mysql/patch-remove_reset.sql
@@ -0,0 +1,8 @@
+ALTER TABLE /*_*/oathauth_users
+ DROP COLUMN secret_reset;
+
+ALTER TABLE /*_*/oathauth_users
+ DROP COLUMN scratch_tokens_reset;
+
+ALTER TABLE /*_*/oathauth_users
+ DROP COLUMN is_validated;
diff --git a/www/wiki/extensions/OATHAuth/sql/mysql/tables.sql b/www/wiki/extensions/OATHAuth/sql/mysql/tables.sql
new file mode 100644
index 00000000..1d531f04
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/sql/mysql/tables.sql
@@ -0,0 +1,11 @@
+CREATE TABLE /*_*/oathauth_users (
+ -- User ID
+ id int not null primary key,
+
+ -- Secret key
+ secret varbinary(255) null,
+
+ -- Scratch tokens
+ scratch_tokens varbinary(511) null
+
+) /*$wgDBTableOptions*/;
diff --git a/www/wiki/extensions/OATHAuth/sql/oracle/tables.sql b/www/wiki/extensions/OATHAuth/sql/oracle/tables.sql
new file mode 100644
index 00000000..51bf328a
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/sql/oracle/tables.sql
@@ -0,0 +1,15 @@
+define mw_prefix='{$wgDBprefix}';
+
+CREATE SEQUENCE oathauth_users_id_seq;
+CREATE TABLE &mw_prefix.oathauth_users (
+ -- User ID
+ id NUMBER NOT NULL,
+
+ -- Secret key
+ secret VARCHAR2(255) NULL,
+
+ -- Scratch tokens
+ scratch_tokens varbinary(511) NULL
+
+);
+ALTER TABLE &mw_prefix.oathauth_users ADD CONSTRAINT &mw_prefix.oathauth_users_pk PRIMARY KEY (id); \ No newline at end of file
diff --git a/www/wiki/extensions/OATHAuth/sql/postgres/tables.sql b/www/wiki/extensions/OATHAuth/sql/postgres/tables.sql
new file mode 100644
index 00000000..35dabedb
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/sql/postgres/tables.sql
@@ -0,0 +1,19 @@
+BEGIN;
+SET client_min_messages = 'ERROR';
+
+DROP SEQUENCE IF EXISTS oathauth_users_id_seq CASCADE;
+
+CREATE SEQUENCE oathauth_users_id_seq MINVALUE 0 START WITH 0;
+CREATE TABLE oathauth_users (
+ -- User ID
+ id INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('oathauth_users_id_seq'),
+
+ -- Secret key
+ secret TEXT NULL,
+
+ -- Scratch tokens
+ scratch_tokens TEXT NULL
+
+);
+
+COMMIT; \ No newline at end of file
diff --git a/www/wiki/extensions/OATHAuth/tests/phan/config.php b/www/wiki/extensions/OATHAuth/tests/phan/config.php
new file mode 100644
index 00000000..f2660b8a
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/tests/phan/config.php
@@ -0,0 +1,3 @@
+<?php
+
+return require __DIR__ . '/../../vendor/mediawiki/mediawiki-phan-config/src/config.php';
diff --git a/www/wiki/extensions/OATHAuth/tests/phpunit/TOTPAuthenticationRequestTest.php b/www/wiki/extensions/OATHAuth/tests/phpunit/TOTPAuthenticationRequestTest.php
new file mode 100644
index 00000000..56403848
--- /dev/null
+++ b/www/wiki/extensions/OATHAuth/tests/phpunit/TOTPAuthenticationRequestTest.php
@@ -0,0 +1,17 @@
+<?php
+
+use MediaWiki\Auth\AuthenticationRequestTestCase;
+
+class TOTPAuthenticationRequestTest extends AuthenticationRequestTestCase {
+
+ protected function getInstance( array $args = [] ) {
+ return new TOTPAuthenticationRequest();
+ }
+
+ public function provideLoadFromSubmission() {
+ return [
+ [ [], [], false ],
+ [ [], [ 'OATHToken' => '123456' ], [ 'OATHToken' => '123456' ] ],
+ ];
+ }
+}