mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-11 02:12:21 -05:00
Updated 2fa flow copy
closes https://linear.app/tryghost/issue/ENG-1654 closes https://linear.app/tryghost/issue/ENG-1656 closes https://linear.app/tryghost/issue/ENG-1657 - updated copy - improved error handling when verification fails - refactored some duplication of steps in Admin authentication tests
This commit is contained in:
parent
85d305ebf7
commit
6c4de6a937
3 changed files with 76 additions and 46 deletions
|
@ -3,6 +3,7 @@ import Controller from '@ember/controller';
|
|||
import DS from 'ember-data';
|
||||
import {TrackedArray} from 'tracked-built-ins';
|
||||
import {action} from '@ember/object';
|
||||
import {isUnauthorizedError} from 'ember-ajax/errors';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {task} from 'ember-concurrency';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
@ -77,11 +78,14 @@ export default class SigninVerifyController extends Controller {
|
|||
yield this.session.authenticate('authenticator:cookie', {token: this.verifyData.token});
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error && error.payload && error.payload.errors) {
|
||||
if (isUnauthorizedError(error)) {
|
||||
this.flowErrors = 'Your verification code is incorrect.';
|
||||
} else if (error && error.payload && error.payload.errors) {
|
||||
this.flowErrors = error.payload.errors[0].message;
|
||||
} else {
|
||||
this.flowErrors = 'There was a problem with the verification token.';
|
||||
this.flowErrors = 'There was a problem verifying the code. Please try again.';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
<form id="login" method="post" action="javascript:void(0)" class="gh-signin" novalidate="novalidate" {{on "submit" (perform this.verifyTokenTask)}}>
|
||||
<header>
|
||||
<div class="gh-site-icon" style={{site-icon-style}}></div>
|
||||
<h1>New browser detected</h1>
|
||||
<h1>Verify it's really you</h1>
|
||||
</header>
|
||||
|
||||
<p>
|
||||
For security, you need to verify your sign-in.
|
||||
An email has been sent to you with a 6-digit code, please enter it below.
|
||||
We don't recognize this device.
|
||||
To keep your account safe, we've sent a 6-digit verification code to your email to make sure it's you.
|
||||
</p>
|
||||
<GhFormGroup @errors={{this.verifyData.errors}} @hasValidated={{this.verifyData.hasValidated}} @property="token">
|
||||
<label for="token">Verification code</label>
|
||||
|
@ -49,7 +49,7 @@
|
|||
</GhFormGroup>
|
||||
|
||||
<GhTaskButton
|
||||
@buttonText="Verify sign-in →"
|
||||
@buttonText="Verify signin →"
|
||||
@task={{this.verifyTokenTask}}
|
||||
@showSuccess={{false}}
|
||||
@class="login gh-btn gh-btn-login gh-btn-block gh-btn-icon"
|
||||
|
|
|
@ -36,6 +36,28 @@ describe('Acceptance: Authentication', function () {
|
|||
describe('general page', function () {
|
||||
let newLocation;
|
||||
|
||||
function setupVerificationRequired(server) {
|
||||
server.post('/session', function () {
|
||||
return new Response(403, {}, {
|
||||
errors: [{
|
||||
code: '2FA_TOKEN_REQUIRED'
|
||||
}]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setupVerificationSuccess(server) {
|
||||
server.put('/session/verify', function () {
|
||||
return new Response(201);
|
||||
});
|
||||
}
|
||||
|
||||
function setupVerificationFailed(server) {
|
||||
server.put('/session/verify', function () {
|
||||
return new Response(401, {}, null);
|
||||
});
|
||||
}
|
||||
|
||||
async function completeSignIn() {
|
||||
await invalidateSession();
|
||||
await visit('/signin');
|
||||
|
@ -44,6 +66,15 @@ describe('Acceptance: Authentication', function () {
|
|||
await click('[data-test-button="sign-in"]');
|
||||
}
|
||||
|
||||
async function completeVerification() {
|
||||
await fillIn('[data-test-input="token"]', 123456);
|
||||
await click('[data-test-button="verify"]');
|
||||
}
|
||||
|
||||
function testMainErrorMessage(expectedMessage) {
|
||||
expect(find('[data-test-flow-notification]')).to.have.trimmed.text(expectedMessage);
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
originalReplaceLocation = windowProxy.replaceLocation;
|
||||
windowProxy.replaceLocation = function (url) {
|
||||
|
@ -104,7 +135,6 @@ describe('Acceptance: Authentication', function () {
|
|||
|
||||
it('doesn\'t show navigation menu on invalid url when not authenticated', async function () {
|
||||
await invalidateSession();
|
||||
|
||||
await visit('/');
|
||||
|
||||
expect(currentURL(), 'current url').to.equal('/signin');
|
||||
|
@ -127,59 +157,57 @@ describe('Acceptance: Authentication', function () {
|
|||
});
|
||||
|
||||
it('has 2fa code happy path', async function () {
|
||||
this.server.post('/session', function () {
|
||||
return new Response(403, {}, {
|
||||
errors: [{
|
||||
code: '2FA_TOKEN_REQUIRED'
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
this.server.put('/session/verify', function () {
|
||||
return new Response(201);
|
||||
});
|
||||
setupVerificationRequired(this.server);
|
||||
setupVerificationSuccess(this.server);
|
||||
|
||||
await completeSignIn();
|
||||
|
||||
expect(currentURL(), 'url after email+password submit').to.equal('/signin/verify');
|
||||
|
||||
await fillIn('[data-test-input="token"]', 123456);
|
||||
await click('[data-test-button="verify"]');
|
||||
|
||||
await completeVerification();
|
||||
expect(currentURL()).to.equal('/dashboard');
|
||||
});
|
||||
|
||||
it('handles 2fa code verification errors', async function () {
|
||||
this.server.post('/session', function () {
|
||||
return new Response(403, {}, {
|
||||
errors: [{
|
||||
code: '2FA_TOKEN_REQUIRED'
|
||||
}]
|
||||
});
|
||||
});
|
||||
it('handles 2fa code verification failure', async function () {
|
||||
setupVerificationRequired(this.server);
|
||||
setupVerificationFailed(this.server);
|
||||
|
||||
await completeSignIn();
|
||||
await completeVerification();
|
||||
|
||||
testMainErrorMessage('Your verification code is incorrect.');
|
||||
});
|
||||
|
||||
it('handles known 2fa code verification error', async function () {
|
||||
setupVerificationRequired(this.server);
|
||||
this.server.put('/session/verify', function () {
|
||||
return new Response(401, {}, {
|
||||
return new Response(422, {}, {
|
||||
errors: [{
|
||||
message: 'Invalid or expired token'
|
||||
message: 'Could not find session. Please try to signin again.'
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
await completeSignIn();
|
||||
await completeVerification();
|
||||
|
||||
await fillIn('[data-test-input="token"]', 123456);
|
||||
await click('[data-test-button="verify"]');
|
||||
|
||||
expect(find('[data-test-flow-notification]')).to.have.trimmed.text('Invalid or expired token');
|
||||
testMainErrorMessage('Could not find session. Please try to signin again.');
|
||||
});
|
||||
|
||||
it('handles 2fa-required on a 2xx response', async function () {
|
||||
it('handles unknown 2fa code verification error', async function () {
|
||||
setupVerificationRequired(this.server);
|
||||
this.server.put('/session/verify', function () {
|
||||
return new Response(400);
|
||||
});
|
||||
|
||||
await completeSignIn();
|
||||
await completeVerification();
|
||||
|
||||
testMainErrorMessage('There was a problem verifying the code. Please try again.');
|
||||
});
|
||||
|
||||
it('handles 2fa-required on a 2xx response from signin', async function () {
|
||||
this.server.post('/session', function () {
|
||||
return new Response(200, {}, {
|
||||
errors: [{
|
||||
code: '2FA_TOKEN_REQUIRED'
|
||||
}]
|
||||
errors: [{code: '2FA_TOKEN_REQUIRED'}]
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -188,19 +216,17 @@ describe('Acceptance: Authentication', function () {
|
|||
expect(currentURL(), 'url after email+password submit').to.equal('/signin/verify');
|
||||
});
|
||||
|
||||
it('handles non-2fa 403 response', async function () {
|
||||
it('handles non-2fa 403 response on signin', async function () {
|
||||
this.server.post('/session', function () {
|
||||
return new Response(403, {}, {
|
||||
errors: [{
|
||||
message: 'Insufficient permissions'
|
||||
}]
|
||||
errors: [{message: 'Insufficient permissions'}]
|
||||
});
|
||||
});
|
||||
|
||||
await completeSignIn();
|
||||
|
||||
expect(currentURL(), 'url after email+password submit').to.equal('/signin');
|
||||
expect(find('[data-test-flow-notification]')).to.have.trimmed.text('Insufficient permissions');
|
||||
testMainErrorMessage('Insufficient permissions');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue