FPNV 技术运营商初始配置指南

上次修改日期: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 设备的请求

  1. EAP-AKA 端点:返回身份验证令牌
  2. AcquireTemporaryToken 端点:根据身份验证令牌返回 TempToken

来自 FPNV 服务器的请求

  1. OAuth 2.0 端点 - OAuth 客户端 ID/密钥流程:根据 OAuth 客户端 ID/密钥返回 OAuth 访问令牌
  2. GetPhoneNumber 端点:根据 OAuth 访问令牌和 TempToken 返回相应的电话号码

选项 2 - CAMARA 实现

CAMARA 实现与原始 TS.43 实现类似,只是处理来自 FPNV 服务器的请求的端点不同。

来自 Android 设备的请求

  1. EAP-AKA 端点:返回身份验证令牌
  2. AcquireTemporaryToken 端点:给定身份验证令牌,返回 TempToken

来自 FPNV 服务器的请求

  1. OAuth 2.0 端点 - JWT 不记名令牌流程:给定包含 TempToken 的 JWT,返回 CAMARA 访问令牌
  2. 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 和指纹
    • SANfpnv.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 以从运营商服务器检索临时令牌的过程。
图 1. 设备通过执行 EAP-AKA,然后执行 AcquireTempToken 从运营商服务器检索 TempToken。第 2 步 - 将 TempToken 换成电话号码将详细介绍如何将 TempToken 换成电话号码。

端点: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 的请求标头

  • Acceptapplication/vnd.gsma.eap-relay.v1.0+json
    • 这是一种特定于 GSMA 的 JSON 格式,而不仅仅是 application/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:应用 ID ap2014 表示电话号码信息
  • terminal_vendor/model/sw_version:设置为任意值;Android 不会保证这些字段包含实际的设备信息
  • vers:配置版本;例如 0 或 1
  • entitlement_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-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-Typeapplication/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 请求

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, operationoperation_targets
    • AcquireTempToken 未指定 EAP_ID

Android 的请求标头

  • Accept:Android 将设置 text/vnd.wap.connectivity-xml

Android 的请求字段

  • terminal_vendor/model/sw_version:Android 并不保证这些字段包含实际的设备信息
  • operation_targets
    • FPNV:操作目标为 GetPhoneNumber

AcquireTempToken - 来自 ECS 的响应

示例TS.43 v12.0 - 第 6.6.6 节 -“AcquireTemporaryToken 响应示例”

ECS 响应标头

  • Content-Type:Android 希望响应类型与请求的 Accept 标头相匹配
    • text/vnd.wap.connectivity-xml

ECS 响应字段

  • APPLICATION.TemporaryToken:FPNV 服务器随后可将其兑换为电话号码的 TemporaryToken
  • APPLICATION.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

图表:显示了 Google 服务器使用 Vanilla TS.43 与运营商交换临时令牌以获取已验证的电话号码。
图 2a. 然后,Google 服务器会通过调用 GetPhoneNumber 将 TempToken 传递给运营商,以换取经过验证的电话号码。此图抽象出了第 1 步 - EAP-AKA / AcquireTempToken

端点: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 编码
  • Content-Type:FPNV 将设置 application/x-www-form-urlencoded
  • Accept:FPNV 将设置 application/json

FPNV 请求字段

  • grant_typeclient_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_typebearer
  • expires_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 操作提取电话号码。

FPNV 的请求标头

  • Acceptapplication/json
  • Content-Typeapplication/json

FPNV 的请求字段

  • requestor_id:标识调用 GetPhoneNumber TS.43 操作的服务
    • Firebase 电话号码验证 UUID191fd7cc-f7cd-4bb4-a5d2-455ae1fb9a19
  • temporary_token:来自 AcquireTempToken 的 TemporaryToken
  • access_token:Google 用于向运营商进行身份验证的 OAuth 令牌
  • terminal_vendor/model/sw_version:FPNV 将使用任意值填充这些字段
  • entitlement_version:Google 会根据运营商的要求配置发送给运营商的授权版本
    • 通常,entitlement_version 为 10.0 或 12.0
  • app:FPNV 将设置 ap2014
  • app_name:FPNV 将为所有 FPNV 请求设置 firebase
    • 注意:无论使用哪个聚合器,AcquireTempToken 的 app_name 都将为 Google-OGI
  • 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

图表:显示了 Google 服务器使用 CAMARA 的 JWT 令牌传递流程与运营商交换临时令牌以获取已验证的电话号码。
图 2b. 然后,Google 服务器会执行 CAMARA 的 JWT 载送令牌流程,将 TempToken 传递给运营商,以换取经过验证的电话号码。此图表省略了步骤 1 - EAP-AKA / AcquireTempToken

端点:检索 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-Typeapplication/x-www-form-urlencoded
  • Acceptapplication/json

FPNV 的请求字段

  • grant_typeurn:ietf:params:oauth:grant-type:jwt-bearer
  • assertion:上面创建的 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_typebearer
  • expires_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 的请求标头

  • AuthorizationBearer $CAMARA_ACCESS_TOKEN
  • Acceptapplication/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
}