forked from canada-ca/tracker
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathset-phone-number.js
More file actions
135 lines (119 loc) · 3.87 KB
/
Copy pathset-phone-number.js
File metadata and controls
135 lines (119 loc) · 3.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import crypto from 'crypto'
import { GraphQLNonNull } from 'graphql'
import { mutationWithClientMutationId } from 'graphql-relay'
import { GraphQLPhoneNumber } from 'graphql-scalars'
import { t } from '@lingui/macro'
import { setPhoneNumberUnion } from '../unions'
const { CIPHER_KEY } = process.env
export const setPhoneNumber = new mutationWithClientMutationId({
name: 'SetPhoneNumber',
description:
'This mutation is used for setting a new phone number for a user, and sending a code for verifying the new number.',
inputFields: () => ({
phoneNumber: {
type: GraphQLNonNull(GraphQLPhoneNumber),
description: 'The phone number that the text message will be sent to.',
},
}),
outputFields: () => ({
result: {
type: setPhoneNumberUnion,
description:
'`SetPhoneNumberUnion` returning either a `SetPhoneNumberResult`, or `SetPhoneNumberError` object.',
resolve: (payload) => payload,
},
}),
mutateAndGetPayload: async (
args,
{
i18n,
query,
collections,
transaction,
auth: { userRequired },
loaders: { loadUserByKey },
validators: { cleanseInput },
notify: { sendTfaTextMsg },
},
) => {
// Cleanse input
const phoneNumber = cleanseInput(args.phoneNumber)
// Get User From Db
let user = await userRequired()
// Generate TFA code
const tfaCode = Math.floor(100000 + Math.random() * 900000)
// Generate Phone Details
const phoneDetails = {
iv: crypto.randomBytes(12).toString('hex'),
}
const cipher = crypto.createCipheriv(
'aes-256-ccm',
String(CIPHER_KEY),
Buffer.from(phoneDetails.iv, 'hex'),
{ authTagLength: 16 },
)
let encrypted = cipher.update(phoneNumber, 'utf8', 'hex')
encrypted += cipher.final('hex')
phoneDetails.phoneNumber = encrypted
phoneDetails.tag = cipher.getAuthTag().toString('hex')
// Set TFA method to backup incase user gets logged out, so they're not locked out of their account
let tfaSendMethod = 'none'
if (user.emailValidated && user.tfaSendMethod !== 'none') {
tfaSendMethod = 'email'
}
// Generate list of collections names
const collectionStrings = []
for (const property in collections) {
collectionStrings.push(property.toString())
}
// Setup Transaction
const trx = await transaction(collectionStrings)
// Insert TFA code into DB
try {
await trx.step(
() => query`
WITH users
UPSERT { _key: ${user._key} }
INSERT {
tfaCode: ${tfaCode},
phoneDetails: ${phoneDetails},
phoneValidated: false,
tfaSendMethod: ${tfaSendMethod}
}
UPDATE {
tfaCode: ${tfaCode},
phoneDetails: ${phoneDetails},
phoneValidated: false,
tfaSendMethod: ${tfaSendMethod}
}
IN users
`,
)
} catch (err) {
console.error(
`Trx step error occurred for user: ${user._key} when upserting phone number information: ${err}`,
)
throw new Error(i18n._(t`Unable to set phone number, please try again.`))
}
try {
await trx.commit()
} catch (err) {
console.error(
`Trx commit error occurred for user: ${user._key} when upserting phone number information: ${err}`,
)
throw new Error(i18n._(t`Unable to set phone number, please try again.`))
}
// Get newly updated user
await loadUserByKey.clear(user._key)
user = await loadUserByKey.load(user._key)
await sendTfaTextMsg({ phoneNumber, user })
console.info(`User: ${user._key} successfully set phone number.`)
return {
_type: 'regular',
user: user,
status: i18n._(
t`Phone number has been successfully set, you will receive a verification text message shortly.`,
),
}
},
})