上次修改日期:2025 年 9 月 10 日
概览
本文档介绍了通过 TS.43 电话号码验证将运营商加入 Firebase 电话号码验证 (FPNV) 的所有必需步骤。
术语
涉及的当事方
- CSP:通信服务提供商
- 例如:移动运营商
- 聚合信息网站
- 面向应用的聚合器:允许应用在不直接与运营商互动的情况下执行验证的聚合器
- 例如,Firebase 电话号码验证
- 元聚合器:支持运营商加入面向应用的聚合器的聚合器
- 元聚合器可能负责为运营商设置授权服务器,并/或使用面向应用的聚合器配置授权服务器的详细信息
- FPNV:Firebase 电话号码验证
- Google TAM:Google 技术支持客户经理,负责帮助运营商加入 FPNV
- Android Telephony:在 Android 上提供电话号码 API,包括供运营商和聚合商提供 TS.43 验证的平台
- GSMA:移动网络运营商协会,负责定义规范,包括 TS.43
- CAMARA:Linux 开源项目,与 GSMA 合作定义运营商 API
验证条款
- PNV:电话号码验证
- TS.43:定义了移动客户端和服务器使用 HTTP 与运营商通信的协议
- EAP-AKA:在 https://www.rfc-editor.org/rfc/rfc4187 中定义的身份验证方法,不需要与用户互动
- ECS:授权配置服务器
- 聚合器与运营商通信的入口点
- ODSA:设备端服务激活
- 指 ECS 提供的用于在设备上激活服务的不同操作
- 例如,AcquireTemporaryToken;GetPhoneNumber
运营商授权服务器和 PNV 端点
创建必要的端点
操作 1:运营商实现以下端点,所有端点均可通过互联网访问。如需详细了解实现,请参阅附录 A。
技术要求
一般性能:所有端点的正常运行时间应至少为 99.99%。
安全性:出于安全考虑,运营商端点必须满足以下要求:
- EAP-AKA 身份验证令牌:必须在 1 小时内过期
- 临时令牌:单次使用,有效期为 5 分钟
- 方案 1 - Vanilla TS.43
- OAuth 令牌:必须在 1 小时内过期
- 方案 2 - CAMARA
- CAMARA 访问令牌:单次使用,有效期为 5 分钟
API 数据质量:成功响应的内容应 100% 准确(即 MSISDN 应准确无误)。
FPNV 支持两种 TS.43。主要区别在于 FPNV 服务器将如何与运营商交换 TempToken。
选项 1 - Vanilla TS.43 实现
来自 Android 设备的请求
- EAP-AKA 端点:返回身份验证令牌
- AcquireTemporaryToken 端点:根据身份验证令牌返回 TempToken
来自 FPNV 服务器的请求
- OAuth 2.0 端点 - OAuth 客户端 ID/密钥流程:根据 OAuth 客户端 ID/密钥返回 OAuth 访问令牌
- GetPhoneNumber 端点:根据 OAuth 访问令牌和 TempToken 返回相应的电话号码
选项 2 - CAMARA 实现
CAMARA 实现与原始 TS.43 实现类似,只是处理来自 FPNV 服务器的请求的端点不同。
来自 Android 设备的请求
- EAP-AKA 端点:返回身份验证令牌
- AcquireTemporaryToken 端点:给定身份验证令牌,返回 TempToken
来自 FPNV 服务器的请求
- OAuth 2.0 端点 - JWT 不记名令牌流程:给定包含 TempToken 的 JWT,返回 CAMARA 访问令牌
- CAMARA NumberVerification v2 端点:给定 CAMARA 访问令牌,返回相应的电话号码
加入 Android 电话和 FPNV
运营商测试应用
操作 2:运营商联系 Google 技术支持客户经理 (TAM),TAM 会与运营商分享 FPNV 运营商测试应用。此运营商测试应用会模拟 FPNV 将发送的请求,而不会涉及 FPNV 服务器。此运营商测试应用可帮助运营商验证其端点是否正常运行。
操作 3:运营商使用 FPNV 运营商测试应用验证上述端点是否可以端到端正常运行。
设置必要的生产配置
Android 配置 - EAP-AKA / AcquireTempToken
操作 4:运营商针对 Android 电话发出的 EAP-AKA/AcquireTempToken 请求定义其生产配置
- 配置:
- 相应运营商的 Android 标准运营商 ID
- TS.43 use_cases 值:
use_case=GetPhoneNumber - EAP-AKA/AcquireTempToken 的正式版授权服务器网址
- Firebase 的正式版 x509 证书的 SAN 和指纹
- SAN:
fpnv.googleapis.com - 指纹:
aad068c93399a22fc2b11ab58468e8cb72b8f9fc53700991799a8b764c589c7e
Firebase 配置 - 交换 TempToken 以获取电话号码
ACTION5:用于从运营商处检索 OAuth 令牌的 Firebase 凭据
- Vanilla TS.43
- 运营商为 FPNV 的请求创建 OAuth 客户端 ID 和密钥。然后,运营商会配置其 OAuth 端点,以针对这些凭据返回访问令牌
- CAMARA
- Google TAM 提供 Google 的公钥,以便运营商的 OAuth 端点可以验证 JWT 是否由 Google 签名
操作 6:运营商为 FPNV 服务器定义生产配置,以交换手机的 TempToken
- 相应 MNO 的 Android 规范运营商 ID
- Vanilla TS.43
- OAuth - 客户端 ID/密钥流程
- OAuth 端点网址
- OAuth 客户端 ID/密钥
- OAuth 范围(如果有)
- GetPhoneNumber
- GetPhoneNumber 端点网址
- CAMARA
- OAuth - JWT 不记名令牌流程
- OAuth 端点网址
- NumberVerification API v2
- NumberVerification 端点网址
共享凭据/配置
Firebase 电话号码验证
操作 7:运营商与 Google 技术客户经理分享其在操作 4 和操作 6 中生成的生产配置。
- [重要提示] 必须使用安全的带外(不通过电子邮件、文档等)机制与 Google 共享 OAuth 密钥。此带外机制将由运营商和 Google TAM 协商确定。
操作 8:Google TAM 使用运营商测试应用验证配置是否能正常运行。之后,Google TAM 会将 OAuth 凭据存储在 Google 安全存储空间中,并更新 FPNV 的配置,以将 TempToken 换成电话(即操作 6 的配置)。
Android 电话
ACTION9:运营商按照“Google Open Gateway CSP Onboarding”文档(Google TAM 会与运营商分享)操作。运营商或其 Google TAM 提交 Buganizer 工单,以加入 Android Telephony 的配置:https://issuetracker.google.com/issues/new?component=1861595&template=2168610。此 bug 将从 ACTION4 中获取生产配置。
如果元聚合平台代表运营商设置 FPNV 集成,则必须提供运营商领导(总监级别及以上)的同意声明(电子邮件、PDF、信函等),以确认其与相应运营商的业务关系。之后,元聚合器可以代表运营商向 Android Telephony 提供运营商的配置。
附件 A. 详细实现
区分大小写
- HTTP 标头不区分大小写
- 不过,XML 和 JSON 格式区分大小写。因此,对于请求/响应字段,请确保这些字段与本文档完全一致。
第 1 步 - EAP-AKA / AcquireTempToken
端点:EAP-AKA 和 AcquireTempToken 必须使用相同的 ECS 端点。
EAP-AKA 质询
参考资料:TS.43 v12.0 - 第 2.8.1 节 -“通过授权配置服务器进行嵌入式 EAP-AKA 身份验证”。
EAP-AKA 第 1 步 - 身份验证质询
EAP-AKA #1 - 向 ECS 发送 GET 请求
Android 电话模块向运营商的授权服务器发送 TS.43 EAP-AKA 请求。
Android 的请求标头
Accept:application/vnd.gsma.eap-relay.v1.0+json- 这是一种特定于 GSMA 的 JSON 格式,而不仅仅是
application/json
- 这是一种特定于 GSMA 的 JSON 格式,而不仅仅是
Android 的请求字段
eap_id:请参阅 RCC.14 附录 C- 即
0<IMSI>@<realm>.mnc<MNC>.mcc<MCC>.3gppnetwork.org
- 即
GID1:仅当授权版本为 12.0 时指定app_name:编码后的 AppName 将具有执行电话验证的用例的 MD5 哈希值:- 所有面向应用的聚合器请求都将具有
Google-OGI的应用名称
- 所有面向应用的聚合器请求都将具有
app:应用 IDap2014表示电话号码信息terminal_vendor/model/sw_version:设置为任意值;Android 不会保证这些字段包含实际的设备信息vers:配置版本;例如 0 或 1entitlement_version:Google 会根据运营商的要求配置发送给运营商的授权版本- 通常,
entitlement_version为 10.0 或 12.0
- 通常,
EAP-AKA #1 - 来自 ECS 的响应
ECS 响应标头
Content-Type:Android 希望响应类型与请求的 Accept 标头相匹配- 即
application/vnd.gsma.eap-relay.v1.0+json
- 即
ECS 响应字段
eap-relay-packet:包含符合 RCC.14 - 第 C.2 部分 的 EAP 软件包
EAP-AKA 第 2 步 - 获取身份验证令牌
EAP-AKA #2 - 向 ECS 发送 POST 请求
然后,Android Telephony 模块会将收到的 eap-relay-packet 发送回同一端点。
Android 的请求标头
Accept:Android 将设置两个 Accept 标头:application/vnd.gsma.eap-relay.v1.0+json:指如果设备需要再次发送 EAP-AKA 请求,则返回 JSON 的载波text/vnd.wap.connectivity-xml:指 Android 希望运营商返回的 EAP-AKA 身份验证令牌的实际格式
Content-Type:application/vnd.gsma.eap-relay.v1.0+json
Android 的请求字段
eap-relay-packet:包含之前的 EAP-AKA 响应的 eap-relay-packet,但采用符合 RFC 4817 - 第 9.2 节的 EAP-Response/AKA-Challenge 格式。
EAP-AKA #2 - 来自 ECS 的响应
EAP-AKA 身份验证成功后,运营商会返回身份验证令牌。
ECS 响应标头
Content-Type:Android 希望与请求的 Accept 标头匹配的响应- 也就是说,Android 期望包含身份验证令牌的响应具有
text/vnd.wap.connectivity-xml类型 - 另一个 Accept 标头
application/vnd.gsma.eap-relay.v1.0+json是指运营商希望 Android 执行另一个 EAP-AKA 请求
- 也就是说,Android 期望包含身份验证令牌的响应具有
ECS 响应字段
TOKEN.token:包含身份验证令牌TOKEN.validity:设备收到响应后,响应有效的秒数- Google 将验证身份验证令牌是否满足技术要求
AcquireTemporary Token
AcquireTempToken - 向 ECS 发出的 GET 请求
Android 客户端使用从 EAP-AKA 收到的身份验证令牌,通过调用运营商的 AcquireTemporaryToken 端点来获取临时令牌。请求
- 示例:TS.43 v12.0 - 第 6.4.6 节 -“AcquireTemporaryToken 请求示例”
- AcquireTempToken 的参数与 EAP-AKA #1 类似,但有以下区别:
- AcquireTempToken 还指定了
IMSI, operation和operation_targets - AcquireTempToken 未指定
EAP_ID
- AcquireTempToken 还指定了
Android 的请求标头
Accept:Android 将设置text/vnd.wap.connectivity-xml
Android 的请求字段
terminal_vendor/model/sw_version:Android 并不保证这些字段包含实际的设备信息operation_targets- FPNV:操作目标为
GetPhoneNumber
- FPNV:操作目标为
AcquireTempToken - 来自 ECS 的响应
示例:TS.43 v12.0 - 第 6.6.6 节 -“AcquireTemporaryToken 响应示例”
ECS 响应标头
Content-Type:Android 希望响应类型与请求的 Accept 标头相匹配- 即
text/vnd.wap.connectivity-xml
- 即
ECS 响应字段
APPLICATION.TemporaryToken:FPNV 服务器随后可将其兑换为电话号码的 TemporaryTokenAPPLICATION.TemporaryTokenExpiry:采用 YYYY-MM-DDThh:mm:ssTZD 格式的到期时间- Google 将验证 TempToken 的过期时间是否满足技术要求
APPLICATION.OperationResult:请参阅 TS.43 v12.0 - 第 6.5.1 部分- 具体而言,如果操作为
SUCCESS,则返回 1
- 具体而言,如果操作为
第 2 步 - 将 TempToken 换成电话号码
选项 1 - Vanilla TS.43
端点:OAuth 和 GetPhoneNumber 端点可以是不同的服务器/端点。这些端点也可能与 EAP-AKA/AcquireTempToken 端点不同。
OAuth
运营商应遵循此 OAuth 指南,并向 Google 提供必要的 OAuth 信息(客户端 ID、客户端密钥、OAuth 服务器网址)
OAuth - 向运营商的身份验证服务器发送 POST 请求
FPNV 请求标头
Authorization:FPNV 将设置Basic $BASE64_ENCODED_CREDENTIALS- base64 编码的凭据是 OAuth
$CLIENT_ID:$CLIENT_SECRET的 base64 编码
- base64 编码的凭据是 OAuth
Content-Type:FPNV 将设置application/x-www-form-urlencodedAccept:FPNV 将设置application/json
FPNV 请求字段
grant_type:client_credentials
POST HTTP/1.1
Host: $OAUTH_ENDPOINT
Authorization: Basic $BASE64_ENCODED_CREDENTIALS
Content-Type: application/x-www-form-urlencoded
Accept: application/json
grant_type=client_credentials
OAuth - 来自运营商身份验证服务器的响应
运营商的响应标头
Content-Type:FPNV 希望响应类型与请求的 Accept 标头相匹配- 即
application/json
- 即
运营商的响应字段
access_token:OAuth 访问令牌token_type:bearerexpires_in:OAuth 访问令牌的过期时间(以秒为单位)- Google 将验证 OAuth 令牌的过期时间是否满足技术要求
200 OK
Content-Type: application/json
{
"access_token": $ACCESS_TOKEN,
"token_type": "bearer",
"expires_in": $EXPIRATION_IN_SECS,
}
GetPhoneNumber
GetPhoneNumber - 向 ECS 发送 POST 请求
Google 验证服务器使用 GetPhoneNumber 操作提取电话号码。
- 示例:TS.43 v12.0 - 第 6.4.7 节 -“GetPhoneNumber 请求示例”
FPNV 的请求标头
Accept:application/jsonContent-Type:application/json
FPNV 的请求字段
requestor_id:标识调用 GetPhoneNumber TS.43 操作的服务- Firebase 电话号码验证 UUID:
191fd7cc-f7cd-4bb4-a5d2-455ae1fb9a19
- Firebase 电话号码验证 UUID:
temporary_token:来自 AcquireTempToken 的 TemporaryTokenaccess_token:Google 用于向运营商进行身份验证的 OAuth 令牌terminal_vendor/model/sw_version:FPNV 将使用任意值填充这些字段entitlement_version:Google 会根据运营商的要求配置发送给运营商的授权版本- 通常,
entitlement_version为 10.0 或 12.0
- 通常,
app:FPNV 将设置ap2014app_name:FPNV 将为所有 FPNV 请求设置firebase- 注意:无论使用哪个聚合器,AcquireTempToken 的
app_name都将为Google-OGI
- 注意:无论使用哪个聚合器,AcquireTempToken 的
operation:FPNV 将设置GetPhoneNumber
GetPhoneNumber - 来自 ECS 的响应
示例:TS.43 v12.0 - 第 6.6.7 节 -“GetPhoneNumber 响应示例”
运营商的响应标头
Content-Type:FPNV 希望响应类型与请求的 Accept 标头相匹配- 即
application/json
- 即
运营商的响应字段
ap2014.MSISDN:FPNV 希望以 E164 格式返回电话号码- JSON 区分大小写,因此 MSISDN 必须大写
TemporaryToken 错误代码
TS.43 v12.0 第 2.8.6 节中的参考内容。
下表详细介绍了 ECS 针对 GetPhoneNumber 请求应向 Google 验证服务器返回的失败响应:
场景 |
来自 ECS 的 GET/POST 响应代码 |
第三方服务器操作 |
请求中的参数无效、缺失或格式错误 |
400 请求错误 |
在下次用户调用时 / 客户端重启后重试 |
请求中的临时令牌无效或已过期 |
401 未经授权 |
如果可能,触发设备从 ECS 获取(新的)有效临时令牌 |
与临时令牌结合使用的操作无效 |
403 禁止访问 |
在下次用户调用时 / 客户端重启后重试 |
找不到所请求的资源 |
404 未找到 |
在下次用户调用时 / 客户端重启后重试 |
ECS 在处理请求时遇到内部错误 |
500 Internal Server Error |
在下次用户调用时 / 客户端重启后重试 |
选项 2 - CAMARA
端点:检索 CAMARA 访问令牌和检索电话号码可以是不同的服务器/端点。这些端点也可能与 EAP-AKA / AcquireTempToken 端点不同。
OAuth - 检索 CAMARA 访问令牌
Google 将仅支持 CAMARA 的 JWT 令牌流,不支持 CIBA 流。
CAMARA 访问令牌 - 向运营商发送 POST 请求
Google 将创建包含以下字段的 JWT。
iss:JWT 的颁发者(也称为客户端 ID)- 即
firebase(实际 FPNV 集成)或fpnv-carrier-tester-app(运营商测试应用)
- 即
sub:JWT 的主题- 即
operatortoken:$TEMP_TOKEN
- 即
aud:受众群体;JWT 的目标接收者- 令牌端点网址(即授权服务器的网址)
exp:到期时间(以秒为单位)- Google 将发送与 CAMARA 访问令牌的有效时长相匹配的过期时长(请参阅技术要求)
iat:以秒为单位的发布时间jti:用于避免重放攻击的唯一标识符- 例如,随机生成的 UUID
scope:请求的用途- 即
dpv:FraudPreventionAndDetection number-verification:device-phone-number:read
- 即
{
"iss": "firebase",
"sub": "operatortoken:ey...",
"aud": $OAUTH_ENDPOINT,
"exp": $EXPIRATION_TIME_IN_SECS,
"iat": $ISSUED_AT_TIME_IN_SECS,
"jti": $RANDOMLY_GENERATED_UUID,
"scope": "dpv:FraudPreventionAndDetection number-verification:device-phone-number:read"
}
FPNV 将使用自己的私钥对 JWT 进行签名,运营商可以使用相应的公钥验证 JWT。FPNV 将使用 JWKS 端点提供公钥。运营商应定期轮询此 JWKS 端点以获取公钥(例如每天一次),因为 FPNV 会定期轮换公钥(例如每 30 天一次)。
FPNV 的请求标头
Content-Type:application/x-www-form-urlencodedAccept:application/json
FPNV 的请求字段
grant_type:urn:ietf:params:oauth:grant-type:jwt-bearerassertion:上面创建的 JWT,已使用 FPNV 的私钥签名- 值得注意的是,此 JWT 包含 TempToken
POST /token.oauth2 HTTP/1.1
Host: as.example.com
Content-Type: application/x-www-form-urlencoded
Accept: application/json
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
&assertion=$JWT
CAMARA 访问令牌 - 运营商的响应
运营商的响应标头
Content-Type:FPNV 希望响应类型与请求的 Accept 标头相匹配- 即
application/json
- 即
运营商的响应字段
access_token:CAMARA 访问令牌,稍后可将其换成电话号码token_type:bearerexpires_in:OAuth 访问令牌的过期时间(以秒为单位)- Google 将验证 OAuth 令牌的过期时间是否满足技术要求
scope:请求的用途- 即
dpv:FraudPreventionAndDetection number-verification:device-phone-number:read
- 即
200 OK
Content-Type: application/json
{
"access_token": $CAMARA_ACCESS_TOKEN,
"token_type": "bearer",
"expires_in": $EXPIRATION_IN_SECS,
"scope": "dpv:FraudPreventionAndDetection number-verification:device-phone-number:read"
}
CAMARA NumberVerification API v2
然后,Google 会通过向运营商的 /device-phone-number 端点发送 GET 请求来交换该 CAMARA 访问令牌。
CAMARA NumberVerification - 向运营商发送的 GET 请求
FPNV 的请求标头
Authorization:Bearer $CAMARA_ACCESS_TOKENAccept:application/json
GET /device-phone-number
Authorization: Bearer $CAMARA_ACCESS_TOKEN
Accept: application/json
Content-Type: application/json
CAMARA NumberVerification - 来自运营商的响应
运营商的响应标头
Content-Type:FPNV 希望响应类型与请求的 Accept 标头相匹配- 即
application/json
- 即
运营商的响应字段
devicePhoneNumber:以 E164 格式返回电话号码
200 OK
Content-Type: application/json
{
"devicePhoneNumber": $PHONE_NUMBER
}