Skip to content

Commit

Permalink
Recover password - enter paperkey -> reset password (keybase#19886)
Browse files Browse the repository at this point in the history
* entry point

* nav to screen

* make errors actually hiddenstrings

* Make password screen impl explicit about only calling back when password >8 length and matches confirm

* submit it to RPC

* polist & get ready for waiting keys & service does not double ask for pw

* comment

* Waiting states for recover password via paper key (keybase#19911)

* cleanup from view management effort

* manage waiting

* use waitingKey in RPC rather than trying to manage manually

* fix test
  • Loading branch information
buoyad authored Sep 27, 2019
1 parent da933dc commit 3fbbc42
Showing 20 changed files with 195 additions and 82 deletions.
5 changes: 1 addition & 4 deletions go/engine/passphrase_recover.go
Original file line number Diff line number Diff line change
@@ -324,10 +324,7 @@ func (e *PassphraseRecover) promptPassphrase(mctx libkb.MetaContext) (string, er
arg.Prompt = fmt.Sprintf("Pick a new strong passphrase (%d+ characters)", libkb.MinPassphraseLength)
arg.Type = keybase1.PassphraseType_VERIFY_PASS_PHRASE

ppres, err := libkb.GetNewKeybasePassphrase(
mctx, mctx.UIs().SecretUI, arg,
"Please reenter your new passphrase for confirmation",
)
ppres, err := libkb.GetKeybasePassphrase(mctx, mctx.UIs().SecretUI, arg)
if err != nil {
return "", err
}
2 changes: 1 addition & 1 deletion go/engine/passphrase_recover_test.go
Original file line number Diff line number Diff line change
@@ -68,7 +68,7 @@ func TestPassphraseRecoverLegacy(t *testing.T) {
PaperKey: paperkey,
}
m = m.WithUIs(uis)
require.IsType(t, libkb.PassphraseError{},
require.IsType(t, libkb.RetryExhaustedError{},
NewPassphraseRecover(tc.G, arg).Run(m))
// We should not be logged in even though paperKey login succeeded
AssertLoggedInLPK(&tc, false)
12 changes: 8 additions & 4 deletions shared/actions/json/recover-password.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"prelude": [
"import * as Types from '../constants/types/provision'"
"import * as Types from '../constants/types/provision'",
"import HiddenString from '../util/hidden-string'"
],
"actions": {
"startRecoverPassword": {
@@ -20,10 +21,13 @@

"submitResetPrompt": {"action": "boolean"},

"setPaperKeyError": {"error": "string"},
"submitPaperKey": {"paperKey": "string"},
"setPaperKeyError": {"error": "HiddenString"},
"submitPaperKey": {"paperKey": "HiddenString"},
"abortPaperKey": {},

"displayError": {"error": "string"}
"setPasswordError": {"error": "HiddenString"},
"submitPassword": {"password": "HiddenString"},

"displayError": {"error": "HiddenString"}
}
}
29 changes: 26 additions & 3 deletions shared/actions/recover-password-gen.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

107 changes: 68 additions & 39 deletions shared/actions/recover-password.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import * as Saga from '../util/saga'
import * as RPCTypes from '../constants/types/rpc-gen'
import * as RecoverPasswordGen from '../actions/recover-password-gen'
import * as RecoverPasswordGen from './recover-password-gen'
import * as ProvisionGen from '../actions/provision-gen'
import * as RouteTreeGen from '../actions/route-tree-gen'
import * as Constants from '../constants/provision'
import * as RouteTreeGen from './route-tree-gen'
import * as ProvisionConstants from '../constants/provision'
import * as Constants from '../constants/recover-password'
import * as Container from '../util/container'
import HiddenString from '../util/hidden-string'
import {RPCError} from '../util/errors'

const chooseDevice = (
@@ -15,7 +17,7 @@ const chooseDevice = (
}
) => {
return Saga.callUntyped(function*() {
const devices = (params.devices || []).map(d => Constants.rpcDeviceToDevice(d))
const devices = (params.devices || []).map(d => ProvisionConstants.rpcDeviceToDevice(d))
yield Saga.put(RecoverPasswordGen.createDisplayDeviceSelect({devices}))

const action:
@@ -99,69 +101,91 @@ const promptReset = (
})
}

const inputPaperKey = (
const getPaperKeyOrPw = (
params: RPCTypes.MessageTypes['keybase.1.secretUi.getPassphrase']['inParam'],
response: {
result: (res: {passphrase: string; storeSecret: boolean}) => void
error: (res: {code: RPCTypes.StatusCode; desc: string}) => void
}
) => {
return Saga.callUntyped(function*() {
if (params.pinentry.retryLabel) {
if (params.pinentry.type === RPCTypes.PassphraseType.paperKey) {
if (params.pinentry.retryLabel) {
yield Saga.put(
RecoverPasswordGen.createSetPaperKeyError({
error: new HiddenString(params.pinentry.retryLabel),
})
)
}
yield Saga.put(
RecoverPasswordGen.createSetPaperKeyError({
error: params.pinentry.retryLabel,
RouteTreeGen.createNavigateAppend({
path: ['recoverPasswordPaperKey'],
replace: true,
})
)
}
yield Saga.put(
RouteTreeGen.createNavigateAppend({
path: ['recoverPasswordPaperKey'],
replace: true,
})
)
const action:
| RecoverPasswordGen.SubmitPaperKeyPayload
| RecoverPasswordGen.AbortPaperKeyPayload = yield Saga.take([
RecoverPasswordGen.submitPaperKey,
RecoverPasswordGen.abortPaperKey,
])
const action:
| RecoverPasswordGen.SubmitPaperKeyPayload
| RecoverPasswordGen.AbortPaperKeyPayload = yield Saga.take([
RecoverPasswordGen.submitPaperKey,
RecoverPasswordGen.abortPaperKey,
])

if (action.type === RecoverPasswordGen.submitPaperKey) {
response.result({
passphrase: action.payload.paperKey,
storeSecret: false,
})
if (action.type === RecoverPasswordGen.submitPaperKey) {
response.result({
passphrase: action.payload.paperKey.stringValue(),
storeSecret: false,
})
} else {
response.error({
code: RPCTypes.StatusCode.scinputcanceled,
desc: 'Input canceled',
})
yield Saga.put(RecoverPasswordGen.createRestartRecovery())
}
} else {
response.error({
code: RPCTypes.StatusCode.scinputcanceled,
desc: 'Input canceled',
})
yield Saga.put(RecoverPasswordGen.createRestartRecovery())
if (params.pinentry.retryLabel) {
yield Saga.put(
RecoverPasswordGen.createSetPasswordError({error: new HiddenString(params.pinentry.retryLabel)})
)
} else {
// TODO maybe wait for loggedIn, for now the service promises to send this after login.
yield Saga.put(RouteTreeGen.createNavigateAppend({path: ['recoverPasswordSetPassword']}))
}
const action: RecoverPasswordGen.SubmitPasswordPayload = yield Saga.take([
RecoverPasswordGen.submitPassword,
])
response.result({passphrase: action.payload.password.stringValue(), storeSecret: true})
}
})
}

function* startRecoverPassword(_: any, action: RecoverPasswordGen.StartRecoverPasswordPayload) {
function* startRecoverPassword(
_: any,
action: RecoverPasswordGen.StartRecoverPasswordPayload,
logger: Saga.SagaLogger
) {
if (action.payload.abortProvisioning) {
yield Saga.put(ProvisionGen.createCancelProvision())
}

let hadError = false
try {
yield RPCTypes.loginRecoverPassphraseRpcSaga({
customResponseIncomingCallMap: {
'keybase.1.loginUi.promptResetAccount': promptReset,
'keybase.1.provisionUi.chooseDevice': chooseDevice,
'keybase.1.secretUi.getPassphrase': inputPaperKey,
'keybase.1.secretUi.getPassphrase': getPaperKeyOrPw,
},
incomingCallMap: {
'keybase.1.loginUi.explainDeviceRecovery': explainDevice,
},
params: {
username: action.payload.username,
},
waitingKey: Constants.waitingKey,
})
} catch (e) {
hadError = true
logger.warn('RPC returned error: ' + e.message)
if (
!(
e instanceof RPCError &&
@@ -170,11 +194,15 @@ function* startRecoverPassword(_: any, action: RecoverPasswordGen.StartRecoverPa
) {
yield Saga.put(
RecoverPasswordGen.createDisplayError({
error: e.toString(),
error: new HiddenString(e.message),
})
)
}
}
logger.info(`finished ${hadError ? 'with error' : 'without error'}`)
if (!hadError) {
yield Saga.put(RouteTreeGen.createClearModals())
}
}

const displayDeviceSelect = () => {
@@ -202,11 +230,12 @@ const restartRecovery = (state: Container.TypedState) => {
function* recoverPasswordSaga() {
yield* Saga.chainGenerator<RecoverPasswordGen.StartRecoverPasswordPayload>(
RecoverPasswordGen.startRecoverPassword,
startRecoverPassword
startRecoverPassword,
'startRecoverPassword'
)
yield* Saga.chainAction2(RecoverPasswordGen.displayDeviceSelect, displayDeviceSelect)
yield* Saga.chainAction2(RecoverPasswordGen.displayError, displayError)
yield* Saga.chainAction2(RecoverPasswordGen.restartRecovery, restartRecovery)
yield* Saga.chainAction2(RecoverPasswordGen.displayDeviceSelect, displayDeviceSelect, 'displayDeviceSelect')
yield* Saga.chainAction2(RecoverPasswordGen.displayError, displayError, 'displayError')
yield* Saga.chainAction2(RecoverPasswordGen.restartRecovery, restartRecovery, 'restartRecovery')
}

export default recoverPasswordSaga
2 changes: 2 additions & 0 deletions shared/actions/typed-actions-gen.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions shared/constants/recover-password.tsx
Original file line number Diff line number Diff line change
@@ -9,5 +9,6 @@ export const makeState = I.Record<Types._State>({
error: new HiddenString(''),
explainedDevice: undefined,
paperKeyError: new HiddenString(''),
passwordError: new HiddenString(''),
username: '',
})
1 change: 1 addition & 0 deletions shared/constants/types/recover-password.tsx
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ export type _State = {
type: RPCTypes.DeviceType
}
paperKeyError: HiddenString
passwordError: HiddenString
username: string
}

4 changes: 3 additions & 1 deletion shared/login/recover-password/paper-key/container.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as Container from '../../../util/container'
import * as RecoverPasswordGen from '../../../actions/recover-password-gen'
import HiddenString from '../../../util/hidden-string'
import PaperKey from '.'

type OwnProps = {}
@@ -10,7 +11,8 @@ const ConnectedPaperKey = Container.connect(
}),
dispatch => ({
onBack: () => dispatch(RecoverPasswordGen.createAbortPaperKey()),
onSubmit: (paperKey: string) => dispatch(RecoverPasswordGen.createSubmitPaperKey({paperKey})),
onSubmit: (paperKey: string) =>
dispatch(RecoverPasswordGen.createSubmitPaperKey({paperKey: new HiddenString(paperKey)})),
}),
(s, d, o: OwnProps) => ({...o, ...s, ...d})
)(PaperKey)
4 changes: 3 additions & 1 deletion shared/login/recover-password/paper-key/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react'
import * as Constants from '../../../constants/recover-password'
import * as Kb from '../../../common-adapters'
import * as Styles from '../../../styles'
import {SignupScreen, InfoIcon} from '../../../signup/common'
@@ -12,7 +13,7 @@ export type Props = {

const PaperKey = (props: Props) => {
const [paperKey, setPaperKey] = React.useState('')
const onSubmit = React.useCallback(() => props.onSubmit(paperKey), [paperKey])
const onSubmit = React.useCallback(() => paperKey && props.onSubmit(paperKey), [paperKey])

return (
<SignupScreen
@@ -22,6 +23,7 @@ const PaperKey = (props: Props) => {
label: 'Continue',
onClick: onSubmit,
type: 'Default' as ButtonType,
waitingKey: Constants.waitingKey,
},
]}
onBack={props.onBack}
Loading

0 comments on commit 3fbbc42

Please sign in to comment.