如果你能为你的用户提供持续的认证,而不需要密码或向他们发送一次性的密码,那会怎么样?有了Twilio的Verify Push API,你就可以做到了。
Verify Push API的强大之处不仅仅在于它的推送通知部分。该API使用公钥加密技术,将任何设备变成一个安全的密钥。这允许你的应用程序注册受信任的设备,并使用它们作为强大的验证器。当认证在注册的设备上完成时,一切都可以在后台默默地发生,没有任何用户参与。这降低了摩擦,增加了可用性,并仍然提供强大的安全性。
以下是你在本教程中要建立的流程:
- 用户被注册/认证(在我们的例子中使用短信验证,但也可以是用户名/密码)
- 设备被注册为安全密钥("因子")。
- 用户尝试登录
- 应用程序默默地对用户进行认证("挑战")。
本教程不包括 "推送通知 "组件,但如果你选择这样做,你完全可以启用它。
如果你想跳过前面的内容,可以在我的GitHub上找到完整的项目。
设置你的开发环境
设置React Native CLI
对于本教程,你只需要设置一个目标操作系统(Android或iOS)。按照React Native CLI快速入门标签下的说明,选择您的开发操作系统和目标操作系统。 在"创建一个新的应用程序 "之前停止。
你将用于无声审批的React Native验证推送SDK与Expo不兼容,所以你必须使用React Native CLI设置。
从Code Exchange部署两个应用程序
在继续之前,你需要一个Verify Push的样本后台,你可以从我们的代码交换中心点击几下就可以部署。使用默认的 "哈希 "值进行身份处理,这将混淆任何PII。要完成这一步,你需要你的验证服务的服务SID。
你还需要部署这个来自Code Exchange的一次性密码验证应用程序,以便使电话验证部分发挥作用。这个应用程序将使用相同的验证服务SID。部署后,点击进入实时应用程序,这将在你的浏览器中打开一个新标签。暂时保持打开状态。
克隆启动程序
你需要一个应用程序来构建无声审批,你可以使用:
- 一个现有的应用程序
- 基于本博文中建立的电话验证的启动程序
如果你已经有一个可以添加无声授权的应用,你可以跳到下一步。如果你没有,请继续阅读以获得预建的启动程序。
在你的终端,导航到一个合适的工作目录,并运行以下命令:
git clone -b starter https://github.com/robinske/verify-push-silent-auth-react-native.git && cd verify-push-silent-auth-react-native
接下来,用(npx pod-install仅在iOS目标中需要)安装依赖项:
yarn install
npx pod-install
打开你用上述命令创建的文件夹,验证-push-silent-auth-react-native。在这个文件夹中,你会看到一个名为*.env.example的文件。将该文件的名称改为.env*(去掉*.example*扩展名)。
在你喜欢的文本编辑器中打开你的*.env*文件,并寻找以下突出显示的一行:
# Follow instructions to deploy this project
# https://www.twilio.com/code-exchange/one-time-passcode-verification-otp
# will look like https://verify-xxxx-xxxxxx.twil.io
BASE_URL=
从你从Code Exchange部署的一次性密码应用中找到你的基本URL。它将是实时应用程序的URL的第一部分。它应该看起来像verify-XXXX-bohzzb.twil.io。
把这个BASE_URL 添加到你的.env文件中(这里有详细描述)。
测试你的应用程序
通过运行yarn ios 或yarn android 来测试你的应用程序,这取决于你喜欢的目标操作系统。如果你遇到任何问题,在这篇博文中有更多关于构建和运行应用程序的细节。
如果你在任何时候被卡住了,这里有启动程序和本教程所涉及的全部代码差异。
如何将一个设备注册为安全钥匙:验证因素
你需要做的第一件事是注册设备。你可以用Verify Push SDK来做这件事,它将在设备上生成一个密钥对,并将公钥发送给Twilio,这样你就可以直接使用API而不是自己做密钥管理。
你可以通过注册一个Factor来做到这一点。
在你克隆的启动程序中,打开src/screens/RegisterPush.tsx。这是一旦有人注册或认证,并且你足够信任他们,让他们把设备注册为安全密钥时,你将显示的屏幕。做这个的好时机包括在注册时或登录后。
用以下代码替换现有的RegisterPush 组件的内容:
const [spinner, setSpinner] = useState(false);
const { phoneNumber } = route.params;
return (
<SafeAreaView style={commonStyles.wrapper}>
<Spinner
visible={spinner}
textContent={"One moment..."}
textStyle={commonStyles.spinnerTextStyle}
/>
<Text style={commonStyles.prompt}>
Secure your account with this device?
</Text>
<Text style={commonStyles.message}>
Whenever there's a new login, we'll send a notification to this phone. It's safer than a text message and you can instantly approve or deny access.
</Text>
<TouchableOpacity
style={{ backgroundColor: "#36D576", ...styles.button }}
onPress={() => {
setSpinner(true);
}}
>
<Text style={commonStyles.buttonText}>Yes, use this device</Text>
</TouchableOpacity>
<TouchableOpacity
style={{ backgroundColor: "#AEB2C1", ...styles.button }}
onPress={() => console.log("skipping push registration")}
>
<Text style={commonStyles.buttonText}>Not now</Text>
</TouchableOpacity>
</SafeAreaView>
);
这将呈现以下屏幕:

加密身份以混淆PII
接下来,你需要实际处理当用户点击是,使用这个设备。打开src/api/verify.js。你要用下面的步骤来填写createFactor() 函数。
这段代码使用用户的电话号码作为身份识别的基础,所以你要把它模糊化。在文件的顶部,就在存在import 行的下面,导入sha256 方法:
import { sha256 } from "react-native-sha256";
然后在createFactor() 函数中添加以下代码:
const identity = await sha256(phoneNumber);
创建因子
SDK文档告诉你如何创建一个因子,并解释说你需要以下参数:
factorName- ✅
verifyServiceSid - ✅
identity pushTokenaccessToken
你已经有了#2和#3。
因素名称可以是任何字符串,但你也可以使用react-native-device-info 库来抓取设备名称(如 "Kelley的iPhone 12")。导入该库(如果你使用的是启动项目,它应该已经安装好了,否则请按照安装说明进行操作:
import { getDeviceName, getDeviceToken } from "react-native-device-info";
在createFactor() 函数中添加以下内容:
const deviceName = await getDeviceName().catch(
() => `${phoneNumber}'s Device'`
);
const deviceToken = await getDeviceToken().catch(
() => "000000000000000000000000000000000000"
);
这将照顾到factorName 和pushToken 。你需要的最后一件事是一个访问令牌,以便从SDK中与Twilio的API通信。
如果你还没有,请使用你的Verify Service SID部署这个Verify Push Backend。打开你的.env 文件并添加PUSH_BACKEND_URL :
# will look like https://verify-push-backend-1234-abcdef.twil.io
PUSH_BACKEND_URL=<your verify push backend url here>
在对身份进行哈希运算后,在createFactor() 函数中添加以下内容:
const response = await fetch(`${PUSH_BACKEND_URL}/access-token`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
identity,
}),
});
const json = await response.json();
在这一点上,你有我们所需要的一切来创建因子。
添加Twilio Verify Push React Native SDK
在终端上,安装Verify Push SDK:
yarn add https://github.com/twilio/twilio-verify-for-react-native.git
如果你的目标是iOS,还要安装Pods:
npx pod-install
在verify.js中,导入你需要的SDK组件以创建一个因子:
import TwilioVerify, {
PushFactorPayload,
VerifyPushFactorPayload,
} from "@twilio/twilio-verify-for-react-native";
在createFactor() 函数中添加以下代码:
const payload = new PushFactorPayload(
deviceName,
json.serviceSid,
json.identity,
deviceToken,
json.token
);
let factor = await TwilioVerify.createFactor(payload);
最后,立即验证Factor并存储Factor SID供将来参考:
factor = await TwilioVerify.verifyFactor(
new VerifyPushFactorPayload(factor.sid)
);
AsyncStorage.setItem("@factor_sid", factor.sid);
AsyncStorage.setItem("@identity", identity);
return factor.sid;
下面是最后的createFactor() 函数的样子:
export const createFactor = async (phoneNumber) => {
// identity should not contain PII
const identity = await sha256(phoneNumber);
const response = await fetch(`${PUSH_BACKEND_URL}/access-token`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
identity,
}),
});
const json = await response.json();
const deviceName = await getDeviceName().catch(
() => `${phoneNumber}'s Device'`
);
const deviceToken = await getDeviceToken().catch(
() => "000000000000000000000000000000000000"
);
const payload = new PushFactorPayload(
deviceName,
json.serviceSid,
json.identity,
deviceToken,
json.token
);
let factor = await TwilioVerify.createFactor(payload);
factor = await TwilioVerify.verifyFactor(
new VerifyPushFactorPayload(factor.sid)
);
AsyncStorage.setItem("@factor_sid", factor.sid);
AsyncStorage.setItem("@identity", identity);
return factor.sid;
};
触发设备注册
回到RegisterPush.tsx文件中,更新Yes, use this device按钮来调用你新的createFactor() 函数。
导入该函数:
import { createFactor } from "../api/verify";
用下面的代码替换onPress() 方法(在TouchableOpacity 组件里面,就在 "每当有新的登录 "消息下面):
onPress={() => {
setSpinner(true);
createFactor(phoneNumber)
.then(() => {
setSpinner(false);
navigation.replace("Gated");
})
.catch((e) => {
console.error(e);
});
}}
最后,在src/screens/Otp.tsx中更新导航,以提示设备注册,其中有如下说明 *// TODO - add a step to allow user to register device as a secure key*:
- success && navigation.replace("Gated");
+ success && navigation.replace("RegisterPush", { phoneNumber });
在这一点上,你可以测试一下因子的创建。确保你的*.env*文件的来源,以获取新的变量,并用yarn ios 或yarn android 重新启动应用程序。 完成电话验证,你应该能够点击是,使用这个设备,并被重新引导到银行图像。潜在的错误应该被记录在Metro窗口中,以帮助你调试任何问题。
默默地授权未来的登录
现在设备已经注册,你可以像钥匙一样使用设备,而不是发送短信OTP或要求密码。
本节将构建欢迎屏幕上的登录按钮背后的功能。
在src/screens/Welcome.tsx中,在Welcome() 组件函数中寻找写着// TODO add silent authorization 的那一行。它应该在第21行的某个地方。用下面的导航来替换这个注释:
navigation.replace("Verifying")
然后到*/src/api/verify.js*去写你的函数来发出挑战。挑战书是Verify API跟踪验证尝试的方式。SDK将用你注册的因子与Verify API对话,以验证该设备是否有效。由于你是在设备上完成的,所以它可以在后台无缝地进行,因此沉默授权中的 "沉默 "是指。
有4个步骤来完成无声授权:
- 从存储器中获取身份
- 创建新的挑战
- 立即验证挑战
- 检查挑战状态以进行验证
这些步骤中的每一步都将在下面的章节中描述。
从存储器中获取身份
silentAuthorization() 函数希望传入factorSid ,所以你只需要从存储空间获取身份,以便创建挑战。
在silentAuthorization() 函数中的try 块内,在TODO 的注释中加入以下代码:
const identity = await AsyncStorage.getItem("@identity");
const data = JSON.stringify({
identity,
factor: factorSid,
message: "Login request",
});
创建新的挑战
你还需要用于创建挑战的端点。幸运的是,你用于访问令牌端点的后端包括一个。在你定义了数据之后,通过调用端点来创建一个挑战。在你上一步添加的代码之后,立即添加以下内容:
const response = await fetch(`${PUSH_BACKEND_URL}/create-challenge`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: data,
});
const json = await response.json();
const challengeSid = json.sid;
立即验证挑战
首先,从Verify Push SDK中导入必要的组件。在同一个verify.js文件的顶部,编辑你的导入,在VerifyPushFactorPayload :
UpdatePushChallengePayload,
ChallengeStatus,
在silentAuthorization() 函数中的try 块的末尾添加以下代码,在您在前面步骤中添加的代码下面,以 "更新 "挑战 - 这就是进行验证的原因:
const payload = new UpdatePushChallengePayload(
factorSid,
challengeSid,
ChallengeStatus.Approved
);
let updated = await TwilioVerify.updateChallenge(payload);
updated = await TwilioVerify.getChallenge(challengeSid, factorSid);
检查挑战状态以进行验证
最后,检查状态以确保它被批准并返回结果:
return updated.status === ChallengeStatus.Approved;
登录时触发无声授权
到*/src/screens/Verifying.tsx*去建立授权检查。该组件有一些模板,让用户了解发生了什么。
验证推送挑战是非常安全和非常快速的。我们建议向用户展示有关该过程的信息,以便他们知道他们的账户受到保护。
你将使用React Hook在页面加载时触发验证,因为你不要求用户再点击任何按钮。
用以下代码替换// TODO :
useEffect(() => {
AsyncStorage.getItem("@factor_sid")
.then((factorSid) => {
silentAuthorization(factorSid).then((approved) => {
if (approved) {
navigation.replace("Gated");
}
});
})
.catch((e) => {
console.error(e);
navigation.replace("PhoneNumber");
});
}, [isFocused]);
来自导航库的isFocused 帮助器是必要的,它可以告诉组件在页面加载时有一个状态变化并触发useEffect 钩子。
重新加载应用程序并尝试点击登录。你应该能短暂地看到验证屏幕。因为它发生得太快了,你可以考虑建立一个超时器,向用户显示至少1或2秒的信息。
推送通知呢?
你使用验证推送API来构建这个演示,但实际上并没有对推送通知做任何事情。你绝对可以添加这个功能,并将移动设备作为桌面登录、呼叫中心认证等的关键。在SDK文档中了解更多关于如何启用推送通知的信息。
像这样的设备授权的挑战之一是如何管理账户恢复:当终端用户失去他们的设备时,会发生什么?更多的开发者看到了推送作为第一层无摩擦防御的价值,然后会在设备不可用的情况下回退到其他渠道,如一次性密码。Verify API提供了许多后退渠道。