mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/OATHAuth
synced 2024-11-27 09:40:28 +00:00
Add copy button for recovery codes
Bug: T354028 Co-Authored-by: TheDJ <hartman.wiki@gmail.com> Change-Id: I3edf9f698aa1f7f5a9881516027a65a88ea1cac4
This commit is contained in:
parent
dc63d00723
commit
ffb7da7a45
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"root": true,
|
"root": true,
|
||||||
"extends": [
|
"extends": [
|
||||||
"wikimedia/client-es5",
|
"wikimedia/client",
|
||||||
"wikimedia/jquery",
|
"wikimedia/jquery",
|
||||||
"wikimedia/mediawiki"
|
"wikimedia/mediawiki"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* eslint-env node */
|
/* eslint-env node */
|
||||||
module.exports = function ( grunt ) {
|
module.exports = function ( grunt ) {
|
||||||
var conf = grunt.file.readJSON( 'extension.json' );
|
const conf = grunt.file.readJSON( 'extension.json' );
|
||||||
|
|
||||||
grunt.loadNpmTasks( 'grunt-banana-checker' );
|
grunt.loadNpmTasks( 'grunt-banana-checker' );
|
||||||
grunt.loadNpmTasks( 'grunt-eslint' );
|
grunt.loadNpmTasks( 'grunt-eslint' );
|
||||||
|
|
|
@ -101,16 +101,25 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ResourceModules": {
|
"ResourceModules": {
|
||||||
"ext.oath.totp.showqrcode.styles": {
|
"ext.oath.styles": {
|
||||||
"class": "MediaWiki\\ResourceLoader\\CodexModule",
|
"class": "MediaWiki\\ResourceLoader\\CodexModule",
|
||||||
"styles": [
|
"styles": [
|
||||||
"totp/ext.oath.showqrcode.styles.less"
|
"totp/ext.oath.showqrcode.styles.less",
|
||||||
|
"recovery/ext.oauth.recovery.less"
|
||||||
],
|
],
|
||||||
"codexStyleOnly": "true",
|
"codexStyleOnly": "true",
|
||||||
"codexComponents": [
|
"codexComponents": [
|
||||||
"CdxButton",
|
"CdxButton",
|
||||||
"CdxIcon"
|
"CdxIcon"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"ext.oath": {
|
||||||
|
"packageFiles": [
|
||||||
|
"recovery/ext.oath.recovery.copy.js"
|
||||||
|
],
|
||||||
|
"messages": [
|
||||||
|
"oathauth-recoverycodes-copy-success"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ResourceFileModulePaths": {
|
"ResourceFileModulePaths": {
|
||||||
|
|
|
@ -16,7 +16,9 @@
|
||||||
"oathauth-recoverycodes-important": "This step is important! Do not skip this step!",
|
"oathauth-recoverycodes-important": "This step is important! Do not skip this step!",
|
||||||
"oathauth-recoverycodes-neveragain": "These codes will never be shown again!",
|
"oathauth-recoverycodes-neveragain": "These codes will never be shown again!",
|
||||||
"oathauth-recoverytokens-createdat": "Recovery tokens created: $1",
|
"oathauth-recoverytokens-createdat": "Recovery tokens created: $1",
|
||||||
"oathauth-recoverycodes-download": "Download recovery codes",
|
"oathauth-recoverycodes-download": "Download",
|
||||||
|
"oathauth-recoverycodes-copy": "Copy",
|
||||||
|
"oathauth-recoverycodes-copy-success": "Recovery codes were copied to your clipboard!",
|
||||||
"oathauth-disable": "Disable two-factor authentication",
|
"oathauth-disable": "Disable two-factor authentication",
|
||||||
"oathauth-validatedoath": "Validated two-factor credentials. Two-factor authentication will now be enforced.",
|
"oathauth-validatedoath": "Validated two-factor credentials. Two-factor authentication will now be enforced.",
|
||||||
"oathauth-noscratchforvalidation": "You cannot use a recovery code to confirm two-factor authentication. Recovery codes are for backup and incidental use only. Please use a code from your two-factor authentication application (such as Google Authenticator).",
|
"oathauth-noscratchforvalidation": "You cannot use a recovery code to confirm two-factor authentication. Recovery codes are for backup and incidental use only. Please use a code from your two-factor authentication application (such as Google Authenticator).",
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
"oathauth-recoverycodes-neveragain": "Plain text, found on Special:OATH while enabling OATH.",
|
"oathauth-recoverycodes-neveragain": "Plain text, found on Special:OATH while enabling OATH.",
|
||||||
"oathauth-recoverytokens-createdat": "Plain text, found on Special:OATH while enabling OATH.",
|
"oathauth-recoverytokens-createdat": "Plain text, found on Special:OATH while enabling OATH.",
|
||||||
"oathauth-recoverycodes-download": "Plain text, text of the download link on Special:OATH while enabling OATH.",
|
"oathauth-recoverycodes-download": "Plain text, text of the download link on Special:OATH while enabling OATH.",
|
||||||
|
"oathauth-recoverycodes-copy": "Plain text, label of the button on Special:OATH to copy the recovery codes",
|
||||||
|
"oathauth-recoverycodes-copy-success": "Notification bubble presented on Special:OATH after copying recovery codes",
|
||||||
"oathauth-disable": "Page title on Special:OATH while disabling OATH.\n\nSee [https://en.wikipedia.org/wiki/Two_factor_authentication two factor authentication]",
|
"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-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-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]",
|
||||||
|
|
40
modules/recovery/ext.oath.recovery.copy.js
Normal file
40
modules/recovery/ext.oath.recovery.copy.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/**
|
||||||
|
* 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 CopyButton {
|
||||||
|
|
||||||
|
static attach() {
|
||||||
|
// eslint-disable-next-line no-jquery/no-global-selector
|
||||||
|
$( '.mw-oathauth-recoverycodes-copy-button' )
|
||||||
|
.addClass( 'clipboard-api-supported' )
|
||||||
|
.on( 'click', ( e ) => {
|
||||||
|
e.preventDefault();
|
||||||
|
// eslint-disable-next-line compat/compat
|
||||||
|
navigator.clipboard.writeText( mw.config.get( 'oathauth-recoverycodes' ) ).then( () => {
|
||||||
|
mw.notify( mw.msg( 'oathauth-recoverycodes-copy-success' ), {
|
||||||
|
type: 'success',
|
||||||
|
tag: 'recoverycodes'
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( navigator.clipboard && navigator.clipboard.writeText ) {
|
||||||
|
// navigator.clipboard() is not supported in Safari 11.1, iOS Safari 11.3-11.4
|
||||||
|
$( CopyButton.attach );
|
||||||
|
}
|
21
modules/recovery/ext.oauth.recovery.less
Normal file
21
modules/recovery/ext.oauth.recovery.less
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
@import 'mediawiki.skin.variables.less';
|
||||||
|
|
||||||
|
.mw-oathauth-recoverycodes-download-icon {
|
||||||
|
.cdx-mixin-css-icon( @cdx-icon-download, @param-is-button-icon: true );
|
||||||
|
}
|
||||||
|
|
||||||
|
.mw-oathauth-recoverycodes-copy-button {
|
||||||
|
&.cdx-button {
|
||||||
|
margin-right: @spacing-horizontal-button;
|
||||||
|
margin-inline-end: @spacing-horizontal-button;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cdx-button__icon {
|
||||||
|
.cdx-mixin-css-icon( @cdx-icon-copy, @param-is-button-icon: true );
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-nojs &,
|
||||||
|
&:not( .clipboard-api-supported ) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,3 @@
|
||||||
@import 'mediawiki.skin.variables.less';
|
|
||||||
|
|
||||||
kbd {
|
kbd {
|
||||||
font-family: monospace, monospace;
|
font-family: monospace, monospace;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -9,11 +7,3 @@ kbd {
|
||||||
fieldset {
|
fieldset {
|
||||||
page-break-inside: avoid;
|
page-break-inside: avoid;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cdx-button.mw-oathauth-recoverycodes-download {
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mw-oathauth-recoverycodes-download-icon {
|
|
||||||
.cdx-mixin-css-icon( @cdx-icon-download, @param-is-button-icon: true );
|
|
||||||
}
|
|
||||||
|
|
|
@ -20,7 +20,9 @@ class TOTPEnableForm extends OATHAuthOOUIHTMLForm {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getHTML( $submitResult ) {
|
public function getHTML( $submitResult ) {
|
||||||
$this->getOutput()->addModuleStyles( 'ext.oath.totp.showqrcode.styles' );
|
$out = $this->getOutput();
|
||||||
|
$out->addModuleStyles( 'ext.oath.styles' );
|
||||||
|
$out->addModules( 'ext.oath' );
|
||||||
|
|
||||||
return parent::getHTML( $submitResult );
|
return parent::getHTML( $submitResult );
|
||||||
}
|
}
|
||||||
|
@ -69,6 +71,8 @@ class TOTPEnableForm extends OATHAuthOOUIHTMLForm {
|
||||||
->build();
|
->build();
|
||||||
|
|
||||||
$now = wfTimestampNow();
|
$now = wfTimestampNow();
|
||||||
|
$recoveryCodes = $this->getScratchTokensForDisplay( $key );
|
||||||
|
$this->getOutput()->addJsConfigVars( 'oathauth-recoverycodes', $this->createTextList( $recoveryCodes ) );
|
||||||
|
|
||||||
// messages used: oathauth-step1, oathauth-step2, oathauth-step3, oathauth-step4
|
// messages used: oathauth-step1, oathauth-step2, oathauth-step3, oathauth-step4
|
||||||
return [
|
return [
|
||||||
|
@ -115,9 +119,10 @@ class TOTPEnableForm extends OATHAuthOOUIHTMLForm {
|
||||||
. $this->msg( 'word-separator' )->escaped()
|
. $this->msg( 'word-separator' )->escaped()
|
||||||
. $this->msg( 'parentheses' )->rawParams( wfTimestamp( TS_ISO_8601, $now ) )->escaped()
|
. $this->msg( 'parentheses' )->rawParams( wfTimestamp( TS_ISO_8601, $now ) )->escaped()
|
||||||
) . '<br/>' .
|
) . '<br/>' .
|
||||||
$this->createResourceList( $this->getScratchTokensForDisplay( $key ) ) . '<br/>' .
|
$this->createResourceList( $recoveryCodes ) . '<br/>' .
|
||||||
'<strong>' . $this->msg( 'oathauth-recoverycodes-neveragain' )->escaped() . '</strong><br/>' .
|
'<strong>' . $this->msg( 'oathauth-recoverycodes-neveragain' )->escaped() . '</strong><br/>' .
|
||||||
$this->createDownloadLink( $this->getScratchTokensForDisplay( $key ) ),
|
$this->createCopyButton() .
|
||||||
|
$this->createDownloadLink( $recoveryCodes ),
|
||||||
'raw' => true,
|
'raw' => true,
|
||||||
'section' => 'step3',
|
'section' => 'step3',
|
||||||
],
|
],
|
||||||
|
@ -146,6 +151,15 @@ class TOTPEnableForm extends OATHAuthOOUIHTMLForm {
|
||||||
return Html::rawElement( 'ul', [], $resourceList );
|
return Html::rawElement( 'ul', [], $resourceList );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $items
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function createTextList( $items ) {
|
||||||
|
return "* " . implode( "\n* ", $items );
|
||||||
|
}
|
||||||
|
|
||||||
private function createDownloadLink( array $scratchTokensForDisplay ): string {
|
private function createDownloadLink( array $scratchTokensForDisplay ): string {
|
||||||
$icon = Html::element( 'span', [
|
$icon = Html::element( 'span', [
|
||||||
'class' => [ 'mw-oathauth-recoverycodes-download-icon', 'cdx-button__icon' ],
|
'class' => [ 'mw-oathauth-recoverycodes-download-icon', 'cdx-button__icon' ],
|
||||||
|
@ -167,6 +181,16 @@ class TOTPEnableForm extends OATHAuthOOUIHTMLForm {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function createCopyButton(): string {
|
||||||
|
return Html::rawElement( 'button', [
|
||||||
|
'class' => 'cdx-button mw-oathauth-recoverycodes-copy-button'
|
||||||
|
], Html::element( 'span', [
|
||||||
|
'class' => 'cdx-button__icon',
|
||||||
|
'aria-hidden' => 'true',
|
||||||
|
] ) . $this->msg( 'oathauth-recoverycodes-copy' )->escaped()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the current secret for display purposes
|
* Retrieve the current secret for display purposes
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in a new issue