Skip to content

Commit

Permalink
feat(modern auth): dispalay message for connected device
Browse files Browse the repository at this point in the history
  • Loading branch information
milan-bc committed Sep 24, 2020
1 parent 8e2d026 commit 90850e5
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1719,6 +1719,12 @@ type MessagesType = {
'scenes.login.sendguid': 'Send my Wallet ID'
'scenes.login.wallet.link': "Don't have a wallet?"
'scenes.login.wallet.signup': 'Sign up Now'
'scenes.login.wallet.message.sent': 'Message Sent'
'scenes.login.wallet.connected.title': 'Mobile Device Connected'
'scenes.login.wallet.connected.description_1': 'We sent your connected mobile device a notification. Open the app to auto-log in on the web.'
'scenes.login.wallet.connected.description_2': 'Didn’t get the notification? Make sure you have push notifications enabled.'
'scenes.login.wallet.connected.send_it_again': 'Send Again'
'scenes.login.wallet.connected.add_a_new_device': 'Add a New Device'
'scenes.login.welcome': 'Welcome back!'
'scenes.login.wrong_password': 'Error decrypting wallet. Wrong password'
'scenes.login.yubikey': 'Yubikey'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export const GUID_ENTERED = '@EVENT.GUID_ENTERED'
export const CHANNEL_PRIV_KEY_CREATED = '@EVENT.CHANNEL_PRIV_KEY_CREATED'
export const CHANNEL_RUID_CREATED = '@EVENT.CHANNEL_RUID_CREATED'
export const CHANNEL_PHONE_CONNECTED = '@EVENT.CHANNEL_PHONE_CONNECTED'
export const DISCONNECT_CHANNEL_PHONE = '@EVENT.DISCONNECT_CHANNEL_PHONE'
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ export const channelPhoneConnected = phonePubkey => ({
type: AT.CHANNEL_PHONE_CONNECTED,
payload: { phonePubkey }
})
export const disconnectChannelPhone = () => ({
type: AT.DISCONNECT_CHANNEL_PHONE
})
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ const cache = (state = INITIAL_STATE, action) => {
const { phonePubkey } = payload
return assoc('channelPhonePubkey', phonePubkey, state)
}
case AT.DISCONNECT_CHANNEL_PHONE: {
return assoc('channelPhonePubkey', undefined, state)
}
default:
return state
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ function uuidv4 () {
export default ({ api, socket }) => {
const send = socket.send.bind(socket)

const pingPhone = function * (ruid, secretHex, phonePubkey, guid) {
const pingPhone = function * (channelId, secretHex, phonePubkey, guid) {
let currentState = yield select(selectors.auth.getSecureChannelLogin)
if (currentState !== Remote.NotAsked) {
return
}
let msg = {
type: 'login_wallet',
ruid: ruid,
channelId: channelId,
timestamp: Date.now()
}

Expand All @@ -56,30 +56,30 @@ export default ({ api, socket }) => {

const onOpen = function * () {
let secretHex = yield select(selectors.cache.getChannelPrivKey)
let ruid = yield select(selectors.cache.getChannelRuid)
let channelId = yield select(selectors.cache.getChannelRuid)

if (!secretHex || !ruid) {
if (!secretHex || !channelId) {
secretHex = crypto.randomBytes(32).toString('hex')
yield put(actions.cache.channelPrivKeyCreated(secretHex))

ruid = uuidv4()
yield put(actions.cache.channelRuidCreated(ruid))
channelId = uuidv4()
yield put(actions.cache.channelRuidCreated(channelId))
}

yield call(
send,
JSON.stringify({
command: 'subscribe',
entity: 'secure_channel',
param: { ruid: ruid }
param: { channelId: channelId }
})
)

// Also, if we already know a phone, let's ping it to give us it's secrets
let phonePubkey = yield select(selectors.cache.getPhonePubkey)
let guid = yield select(selectors.cache.getLastGuid)
if (phonePubkey && guid) {
yield pingPhone(ruid, secretHex, phonePubkey, guid)
yield pingPhone(channelId, secretHex, phonePubkey, guid)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,22 +79,24 @@ const mapStateToProps = state => ({
qr_data: selectors.cache.getChannelPrivKey(state)
? JSON.stringify({
type: 'login_wallet',
ruid: selectors.cache.getChannelRuid(state),
channelId: selectors.cache.getChannelRuid(state),
pubkey: wCrypto
.derivePubFromPriv(
Buffer.from(selectors.cache.getChannelPrivKey(state), 'hex')
)
.toString('hex')
})
: ''
: '',
phonePubKey: selectors.cache.getPhonePubkey(state)
})

const mapDispatchToProps = dispatch => ({
authActions: bindActionCreators(actions.auth, dispatch),
middlewareActions: bindActionCreators(actions.ws, dispatch),
alertActions: bindActionCreators(actions.alerts, dispatch),
formActions: bindActionCreators(actions.form, dispatch),
modalActions: bindActionCreators(actions.modals, dispatch)
modalActions: bindActionCreators(actions.modals, dispatch),
cacheActions: bindActionCreators(actions.cache, dispatch)
})

const connector = connect(mapStateToProps, mapDispatchToProps)
Expand Down
207 changes: 148 additions & 59 deletions packages/blockchain-wallet-v4-frontend/src/scenes/Login/template.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ const SideWrapper = styled.div`
padding: 12px 0 12px 0;
display: flex;
flex-direction: column;
z-index: -1;
`
const CenterWrapper = styled.div`
display: flex;
Expand Down Expand Up @@ -153,6 +152,9 @@ export const CartridgeContainer = styled.div`
text-transform: uppercase;
}
`
export const CartridgeSentContainer = styled.div`
width: auto;
`

const LinkAccountTitle = () => (
<TitleWrapper>
Expand All @@ -177,6 +179,8 @@ const Login = (props: InjectedFormProps<{}, Props> & Props) => {
loginError,
password,
submitting,
phonePubKey,
cacheActions,
...rest
} = props
const { handleSubmit, handleSmsResend, authType } = rest
Expand Down Expand Up @@ -446,67 +450,152 @@ const Login = (props: InjectedFormProps<{}, Props> & Props) => {
</CenterWrapper>
<SideWrapper>
<PublicSideWrapper>
<CartridgeContainer>
<SuccessCartridge>
<FormattedMessage id='copy.new' defaultMessage='New' />
</SuccessCartridge>
</CartridgeContainer>
{!phonePubKey && (
<>
<CartridgeContainer>
<SuccessCartridge>
<FormattedMessage id='copy.new' defaultMessage='New' />
</SuccessCartridge>
</CartridgeContainer>

<Text
size='16px'
color='grey900'
weight={600}
style={{ marginTop: '8px' }}
>
<FormattedMessage
id='scenes.login.qrcodelogin'
defaultMessage='QR Code Log In'
/>
</Text>
<TextGroup inline style={{ marginTop: '8px', lineHeight: '18px' }}>
<DescriptionText size='12px' color='grey900' weight={500}>
<FormattedMessage
id='scenes.login.qrcodelogin_description_1'
defaultMessage='Open your mobile Blockchain App, tap the QR Code Scanner'
/>
</DescriptionText>
<DescriptionIcon color='grey900' name='qr-camera' size='16px' />
<Text size='12px' color='grey900' weight={500}>
<FormattedMessage
id='scenes.login.qrcodelogin_description_2'
defaultMessage='in the top right & scan this code to log in.'
/>
</Text>
</TextGroup>
<Text
size='16px'
color='grey900'
weight={600}
style={{ marginTop: '8px' }}
>
<FormattedMessage
id='scenes.login.qrcodelogin'
defaultMessage='QR Code Log In'
/>
</Text>
<TextGroup
inline
style={{ marginTop: '8px', lineHeight: '18px' }}
>
<DescriptionText size='12px' color='grey900' weight={500}>
<FormattedMessage
id='scenes.login.qrcodelogin_description_1'
defaultMessage='Open your mobile Blockchain App, tap the QR Code Scanner'
/>
</DescriptionText>
<DescriptionIcon color='grey900' name='qr-camera' size='16px' />
<Text size='12px' color='grey900' weight={500}>
<FormattedMessage
id='scenes.login.qrcodelogin_description_2'
defaultMessage='in the top right & scan this code to log in.'
/>
</Text>
</TextGroup>

<QRCodeContainer>
{props.secureChannelLoginState.cata({
Success: () => {
return (
<Text size='14px' weight={600}>
Success! Logging in...
</Text>
)
},
Failure: e => (
<Text>{typeof e === 'string' ? e : 'Unknown Error'}</Text>
),
Loading: () => {
return (
<Text size='14px' weight={600}>
Please confirm the login on your mobile device...
</Text>
)
},
NotAsked: () => (
<QRCodeWrapper
value={qr_data}
size={qr_data.length}
showImage
<QRCodeContainer>
{props.secureChannelLoginState.cata({
Success: () => {
return (
<Text size='14px' weight={600}>
Success! Logging in...
</Text>
)
},
Failure: e => (
<Text>{typeof e === 'string' ? e : 'Unknown Error'}</Text>
),
Loading: () => {
return (
<Text size='14px' weight={600}>
Please confirm the login on your mobile device...
</Text>
)
},
NotAsked: () => (
<QRCodeWrapper
value={qr_data}
size={qr_data.length}
showImage
/>
)
})}
</QRCodeContainer>
</>
)}

{phonePubKey && (
<>
<CartridgeSentContainer>
<SuccessCartridge>
<FormattedMessage
id='scenes.login.wallet.message.sent'
defaultMessage='Message Sent'
/>
</SuccessCartridge>
</CartridgeSentContainer>

<Text
size='16px'
color='grey900'
weight={600}
style={{ marginTop: '8px' }}
>
<FormattedMessage
id='scenes.login.wallet.connected.title'
defaultMessage='Mobile Device Connected'
/>
)
})}
</QRCodeContainer>
</Text>

<Text
size='12px'
color='grey900'
weight={500}
style={{ marginTop: '8px' }}
>
<FormattedMessage
id='scenes.login.wallet.connected.description_1'
defaultMessage='We sent your connected mobile device a notification. Open the app to auto-log in on the web.'
/>
</Text>
<Text
size='12px'
color='grey900'
weight={500}
style={{ marginTop: '24px' }}
>
<FormattedMessage
id='scenes.login.wallet.connected.description_2'
defaultMessage='Didn’t get the notification? Make sure you have push notifications enabled.'
/>
</Text>

<TextGroup
inline
style={{ marginTop: '8px', lineHeight: '18px' }}
>
<Link size='12px' weight={500}>
<FormattedMessage
id='scenes.login.wallet.connected.send_it_again'
defaultMessage='Send Again'
/>
</Link>

<Text size='12px' color='grey900' weight={500}>
<FormattedMessage
id='modals.mobilenumberverify.getcode2'
defaultMessage='or'
/>
</Text>

<Link
size='12px'
weight={500}
onClick={() => cacheActions.disconnectChannelPhone()}
>
<FormattedMessage
id='scenes.login.wallet.connected.add_a_new_device'
defaultMessage='Add a New Device'
/>
</Link>
</TextGroup>
</>
)}
</PublicSideWrapper>
</SideWrapper>
</OuterWrapper>
Expand Down

0 comments on commit 90850e5

Please sign in to comment.