依据
specs/017-oauth-login(含 2026-10-08 业务端账号密码绑定增量)整理。 适用端:骑手 App、商家 App(含商家子账号)。用户端 App 的手机号+短信流程不在本文范围。
| 端 | Apple | LINE | |
|---|---|---|---|
| 骑手 | apple_rider |
google_rider |
line_rider |
| 商家(含子账号) | apple_merchant |
google_merchant |
line_merchant |
① App 取三方凭证
Apple :Sign in with Apple SDK → identityToken
Google :Google Sign-In SDK → idToken
LINE :先调 /auth/line/authorize 拿授权地址和 state,打开授权页
② POST /infouser/user/oauthLogin 提交 provider + credential
├─ 已绑定 → 直接返回 { data: 用户信息, token },登录完成
└─ 未绑定 → 返回 { data: { status:"needPhone", tempKey } }
③ 首次绑定:App 弹【账号 + 密码】表单
POST /infouser/user/oauthBindPhone 提交 tempKey + userName + password(RSA密文)
→ 成功返回 { data: 用户信息, token },绑定并登录完成
token 与现有账号密码登录(syslodeing / shanglodeing)签发的是同类会话 token(骑手端 / 商家端各自独立),后续请求照旧放 token 请求头即可,无需其它接入。POST /auth/line/authorize(免登录)业务端 LINE 登录必须先从服务端拿授权地址(内含服务端签发的 state,5 分钟有效、单次消费),不要自行拼接 LINE 授权 URL。
请求体:
{ "provider": "line_rider" } // 商家端传 line_merchant
响应:
{
"code": 200,
"data": {
"authorizationUrl": "https://access.line.me/oauth2/v2.1/authorize?...",
"state": "32位hex"
}
}
App 打开 authorizationUrl(系统浏览器或 WebView),并保存 state 用于回跳比对。
POST /infouser/user/oauthLogin(免登录)请求体:
| 字段 | 必填 | 说明 |
|---|---|---|
provider |
是 | 见第 1 节 |
credential |
是 | Apple=identityToken;Google=idToken;LINE=授权 code |
state |
LINE 直连提交 code 时必填 | /auth/line/authorize 返回的 state;Apple/Google 不传 |
cid cidType deviceToken voIPToken |
否 | 推送字段,与现有登录一致 |
fullName |
否 | 仅 Apple:首次授权时用户共享的姓名,从 ASAuthorization 凭证取;后续登录不再传 |
响应 A(已绑定,直接登录):
{ "code": 200, "msg": "...", "data": { ...用户信息... }, "token": "xxx" }
响应 B(未绑定,首次):
{ "code": 200, "msg": "...", "data": { "status": "needPhone", "tempKey": "xxx" } }
tempKey 5 分钟有效、一次性:过期或任何一步失败,都要重新从 oauthLogin 开始,不能重试旧 tempKey。needPhone 是历史契约(用户端为手机号+短信),业务端拿到该状态后展示的是「账号 + 密码」表单,不走短信。data 不含三方渠道字段(骑手返回账号实体、商家返回 MerchantLoginView,均只有账号资料);登录渠道 App 自己发起时已知,需要展示时按第 7 节查询。失败: code != 200(凭证无效/过期、provider 不支持、账号已停用、state 无效等),直接提示 msg。
POST /infouser/user/oauthBindPhone(免登录)业务端绑定 = 校验业务账号密码后写入三方绑定,成功即登录。
请求体:
| 字段 | 必填 | 说明 |
|---|---|---|
tempKey |
是 | oauthLogin 返回 |
userName |
是 | 业务账号登录名(与 syslodeing / shanglodeing 登录用的账号一致) |
password |
是 | RSA 公钥加密后的密码密文,加密方式与现有账号密码登录完全一致(同一公钥、同一格式,直接复用现有加密函数) |
cid cidType deviceToken voIPToken |
否 | 推送字段 |
业务端不传 phone / code(那是用户端 App 的短信绑定参数)。
响应(成功):
{ "code": 200, "msg": "...", "data": { ...用户信息... }, "token": "xxx" }
绑定成功后即完成登录,无需再调登录接口;该三方账号下次登录走 oauthLogin 直接拿 token。
失败(不写绑定、不签 token):
| 场景 | 处理建议 |
|---|---|
| tempKey 缺失/过期 | 重新发起 oauthLogin |
| 缺 userName / password | 表单校验拦截 |
| 账号不存在 | 提示 msg(账号名或角色不符、已删除均按不存在处理) |
| 密码错误 | 提示 msg,可重输 |
| 账号停用 / 子账号被禁用或归属失效 | 提示 msg,联系平台 |
GET /auth/line/callback(LINE 服务器调用,App 不直接请求)LINE 授权完成后,后端处理回调并 302 跳回对应 App 的 scheme,App 按查询参数分流:
| 回跳参数 | 含义 | App 动作 |
|---|---|---|
?token=...&state=... |
已绑定,登录成功 | 比对 state 后保存 token,进入首页 |
?needPhone=1&tempKey=...&state=... |
未绑定,首次 | 比对 state 后弹账号+密码表单 → 调 oauthBindPhone |
?error=no_code\|user_stopped\|fail&state=... |
失败 | 提示失败,可重试 |
state 与第 3 节保存的发起值,不一致视为异常放弃本次登录。oauthLogin 提交 provider + credential(code) + state。与用户端 App 一致:登录接口响应不带渠道字段,需要展示「本次登录渠道 + 第三方名称」时调用:
GET /infouser/user/getuserinfo(token 请求头;骑手/商家 OAuth 登录签发的 token 均可直接调用)响应 data 中与渠道相关的两个字段(017 2026-09-24 增量,三端共用同一契约):
| 字段 | 说明 |
|---|---|
oauthProvider |
本次会话登录渠道:apple_rider / google_rider / line_rider / apple_merchant / google_merchant / line_merchant;账号密码登录的会话该字段为空 |
providerDisplayName |
该渠道的第三方显示名称(LINE 的 displayName / Apple 首次授权共享的 fullName),可空 |
展示规则(与用户端一致):
nickName 分开显示;providerDisplayName 为空时只展示渠道(如「Apple 登录」),不要用昵称冒充第三方名称。oauthLogin,无需服务端授权地址。/auth/line/authorize 拿授权地址与 state(服务端校验单次消费),state 过期需重新获取。tempKey 一次性、5 分钟 TTL;绑定任何失败都从 oauthLogin 重来。