-
Notifications
You must be signed in to change notification settings - Fork 210
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(password): Add ability to set password after third party login t…
…o Sync
- Loading branch information
Showing
9 changed files
with
316 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
...a-content-server/app/scripts/templates/post_verify/third_party_auth/set_password.mustache
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
<div class="card"> | ||
<header class="mb-2"> | ||
<h1 id="fxa-signup-password-header" class="card-header"> | ||
{{#t}}Set your password{{/t}} | ||
</h1> | ||
</header> | ||
|
||
<section> | ||
<div class="error"></div> | ||
<div class="success"></div> | ||
|
||
<form novalidate> | ||
<p id="prefillEmail" class="text-base break-all mb-5">{{ email }}</p> | ||
|
||
<p class="mb-5"> | ||
{{#t}}Please create a password to continue to Firefox Sync. Your data is encrypted with your password to protect your privacy.{{/t}} | ||
</p> | ||
|
||
<div class="tooltip-container mb-5"> | ||
<input name="password" id="password" type="password" class="input-text tooltip-below" placeholder="{{#t}}Password{{/t}}" value="{{ password }}" pattern=".{8,}" required autofocus data-form-prefill="password" data-synchronize-show="true" /> | ||
<div id="password-strength-balloon-container" tabindex="-1"></div> | ||
</div> | ||
|
||
<div class="tooltip-container mb-5"> | ||
<input name="vpassword" id="vpassword" type="password" class="input-text tooltip-below" placeholder="{{#t}}Repeat password{{/t}}" pattern=".{8,}" required data-synchronize-show="true" /> | ||
|
||
<div class="input-balloon hidden" id="vpassword-balloon"> | ||
<div class="before:content-key flex before:w-8"> | ||
<p class="ltr:pl-2 rtl:pr-2"> | ||
{{#unsafeTranslate}}You need this password to access any encrypted data you store with us.<br class="mb-3">A reset means potentially losing data like passwords and bookmarks.{{/unsafeTranslate}} | ||
</p> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<header> | ||
<h2 id="fxa-choose-what-to-sync-header" class="font-normal mb-5 text-base"> | ||
{{#t}}Choose what to sync{{/t}} | ||
</h2> | ||
</header> | ||
<div class="flex flex-wrap text-start ltr:mobileLandscape:ml-6 rtl:mobileLandscape:mr-6 mb-1"> | ||
{{#engines}} | ||
<div class="mb-4 relative flex-50% rtl:mobileLandscape:pr-6 ltr:mobileLandscape:pl-6 rtl:pr-3 ltr:pl-3 flex items-center"> | ||
<input name="sync-content" class="input-checkbox" id="sync-engine-{{id}}" type="checkbox" {{#checked}}checked="checked"{{/checked}} value="{{id}}" tabindex="{{tabindex}}" /><label class="input-checkbox-label" for="sync-engine-{{id}}">{{text}}</label> | ||
</div> | ||
{{/engines}} | ||
</div> | ||
|
||
<div class="flex"> | ||
<button id="submit-btn" class="cta-primary cta-xl" type="submit">{{#t}}Create Password{{/t}}</button> | ||
</div> | ||
</form> | ||
</section> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
87 changes: 87 additions & 0 deletions
87
packages/fxa-content-server/app/scripts/views/post_verify/third_party_auth/set_password.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
import AuthErrors from '../../../lib/auth-errors'; | ||
import Cocktail from '../../../lib/cocktail'; | ||
import Template from '../../../templates/post_verify/third_party_auth/set_password.mustache'; | ||
import FormView from '../../form'; | ||
import FlowEventsMixin from '../../mixins/flow-events-mixin'; | ||
import PasswordMixin from '../../mixins/password-mixin'; | ||
import PasswordStrengthMixin from '../../mixins/password-strength-mixin'; | ||
import CWTSOnSignupPasswordMixin from '../../mixins/cwts-on-signup-password'; | ||
import ServiceMixin from '../../mixins/service-mixin'; | ||
import AccountSuggestionMixin from '../../mixins/account-suggestion-mixin'; | ||
import SigninMixin from '../../mixins/signin-mixin'; | ||
|
||
const PASSWORD_INPUT_SELECTOR = '#password'; | ||
const VPASSWORD_INPUT_SELECTOR = '#vpassword'; | ||
|
||
class SetPassword extends FormView { | ||
template = Template; | ||
|
||
_getPassword() { | ||
return this.$(PASSWORD_INPUT_SELECTOR).val(); | ||
} | ||
|
||
_getVPassword() { | ||
return this.$(VPASSWORD_INPUT_SELECTOR).val(); | ||
} | ||
|
||
isValidEnd() { | ||
return this._getPassword() === this._getVPassword(); | ||
} | ||
|
||
showValidationErrorsEnd() { | ||
if (this._getPassword() !== this._getVPassword()) { | ||
const err = AuthErrors.toError('PASSWORDS_DO_NOT_MATCH'); | ||
this.showValidationError(this.$(PASSWORD_INPUT_SELECTOR), err, true); | ||
} | ||
} | ||
|
||
getAccount() { | ||
return this.getSignedInAccount(); | ||
} | ||
|
||
beforeRender() { | ||
const account = this.getSignedInAccount(); | ||
if (account.isDefault()) { | ||
return this.replaceCurrentPage('/'); | ||
} | ||
} | ||
|
||
setInitialContext(context) { | ||
const email = this.getAccount().get('email'); | ||
context.set({ | ||
email, | ||
}); | ||
} | ||
|
||
submit() { | ||
const account = this.getAccount(); | ||
const password = this._getPassword(); | ||
|
||
return account.createPassword(account.get('email'), password) | ||
.then(() => { | ||
// After the user has set a password, initiated the standard Sync | ||
// login flow with the password they set. | ||
return this.signIn(account, password); | ||
}); | ||
} | ||
} | ||
|
||
Cocktail.mixin( | ||
SetPassword, | ||
PasswordMixin, | ||
CWTSOnSignupPasswordMixin, | ||
PasswordStrengthMixin({ | ||
balloonEl: '#password-strength-balloon-container', | ||
passwordEl: PASSWORD_INPUT_SELECTOR, | ||
}), | ||
ServiceMixin, | ||
AccountSuggestionMixin, | ||
FlowEventsMixin, | ||
SigninMixin | ||
); | ||
|
||
export default SetPassword; |
147 changes: 147 additions & 0 deletions
147
...ages/fxa-content-server/app/tests/spec/views/post_verify/third_party_auth/set_password.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
import _ from 'underscore'; | ||
import { assert } from 'chai'; | ||
import Account from 'models/account'; | ||
import Backbone from 'backbone'; | ||
import BaseBroker from 'models/auth_brokers/base'; | ||
import Metrics from 'lib/metrics'; | ||
import Relier from 'models/reliers/relier'; | ||
import SentryMetrics from 'lib/sentry'; | ||
import sinon from 'sinon'; | ||
import User from 'models/user'; | ||
import View from 'views/post_verify/third_party_auth/set_password'; | ||
import WindowMock from '../../../../mocks/window'; | ||
import $ from 'jquery'; | ||
|
||
const PASSWORD_INPUT_SELECTOR = '#password'; | ||
const VPASSWORD_INPUT_SELECTOR = '#vpassword'; | ||
const PASSWORD = 'passwordzxcv'; | ||
|
||
describe('views/post_verify/third_party_auth/set_password', () => { | ||
let account; | ||
let broker; | ||
let metrics; | ||
let model; | ||
let notifier; | ||
let relier; | ||
let sentryMetrics; | ||
let user; | ||
let view; | ||
let windowMock; | ||
|
||
beforeEach(() => { | ||
windowMock = new WindowMock(); | ||
relier = new Relier({ | ||
window: windowMock, | ||
}); | ||
broker = new BaseBroker({ | ||
relier, | ||
window: windowMock, | ||
}); | ||
account = new Account({ | ||
email: '[email protected]', | ||
uid: 'uid', | ||
}); | ||
model = new Backbone.Model({ | ||
account, | ||
}); | ||
notifier = _.extend({}, Backbone.Events); | ||
sentryMetrics = new SentryMetrics(); | ||
metrics = new Metrics({ notifier, sentryMetrics }); | ||
user = new User(); | ||
view = new View({ | ||
broker, | ||
metrics, | ||
model, | ||
notifier, | ||
relier, | ||
user, | ||
}); | ||
|
||
sinon.stub(view, 'getSignedInAccount').callsFake(() => account); | ||
sinon | ||
.stub(account, 'createPassword') | ||
.callsFake(() => Promise.resolve()); | ||
sinon | ||
.stub(view, 'signIn') | ||
.callsFake(() => Promise.resolve()); | ||
|
||
return view.render().then(() => $('#container').html(view.$el)); | ||
}); | ||
|
||
afterEach(function () { | ||
view.remove(); | ||
view.destroy(); | ||
}); | ||
|
||
describe('render', () => { | ||
it('renders the view', () => { | ||
assert.lengthOf(view.$('#fxa-signup-password-header'), 1); | ||
assert.include(view.$('#prefillEmail').text(), '[email protected]'); | ||
assert.lengthOf(view.$('#fxa-choose-what-to-sync-header'), 1); | ||
assert.lengthOf(view.$('#submit-btn'), 1); | ||
}); | ||
|
||
describe('without an account', () => { | ||
beforeEach(() => { | ||
account = new Account({}); | ||
sinon.spy(view, 'navigate'); | ||
return view.render(); | ||
}); | ||
|
||
it('redirects to the email first page', () => { | ||
assert.isTrue(view.navigate.calledWith('/')); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('validateAndSubmit', () => { | ||
beforeEach(() => { | ||
sinon.stub(view, 'submit').callsFake(() => Promise.resolve()); | ||
sinon.spy(view, 'showValidationError'); | ||
}); | ||
|
||
describe('with invalid password', () => { | ||
beforeEach(() => { | ||
view.$(PASSWORD_INPUT_SELECTOR).val(''); | ||
return view.validateAndSubmit().then(assert.fail, () => {}); | ||
}); | ||
|
||
it('displays a tooltip, does not call submit', () => { | ||
assert.isTrue(view.showValidationError.called); | ||
assert.isFalse(view.submit.called); | ||
}); | ||
}); | ||
|
||
describe('with valid password', () => { | ||
beforeEach(() => { | ||
view.$(PASSWORD_INPUT_SELECTOR).val(PASSWORD); | ||
view.$(VPASSWORD_INPUT_SELECTOR).val(PASSWORD); | ||
return view.validateAndSubmit(); | ||
}); | ||
|
||
it('calls submit', () => { | ||
assert.equal(view.submit.callCount, 1); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('submit', () => { | ||
describe('success', () => { | ||
beforeEach(() => { | ||
sinon.spy(view, 'navigate'); | ||
view.$(PASSWORD_INPUT_SELECTOR).val(PASSWORD); | ||
view.$(VPASSWORD_INPUT_SELECTOR).val(PASSWORD); | ||
return view.submit(); | ||
}); | ||
|
||
it('calls correct methods', () => { | ||
assert.isTrue(account.createPassword.calledWith('[email protected]', PASSWORD),); | ||
assert.isTrue(view.signIn.calledWith(account, PASSWORD,)); | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters