DOOFOX BLOG

10月 23 2019 Category:

Sign with in Apple,网站配置 Apple 登录

尤其是 macOS Catalina 版本开始的 Safari 浏览器,从系统层面支持通过 Apple 账户登录。用户只要一个指纹验证即可快速在网站或 App 上通过 Apple 账户 登录

准备

首先你需要准备一个 Apple 企业/个人 开发者账户,并访问 Certificates, Identifiers & Profiles 创建一个 AppId, ServiceId,生成授权私钥以及绑定可以用于和用户邮件沟通的邮件地址。

创建 App ID


Platform

选择 macOS,不可修改

Description

App 描述,可修改

Bundle ID

必须唯一,建议输入项目的域名,例如 domain.com 可以输入 com.domain ,不可修改

勾选 Sign In with Apple 选项

最后确认并注册,即成功创建 App 应用

创建 Services ID

在创建好 App ID 后,你还需要创建一个 Services ID,用于管理和配置 Sign In with Apple 的安全域名、回调地址数据。


Description

服务描述,可修改

Identifier

服务标示,不可修改,建议输入项目接口域名,例如 api.domain.com 可以输入 com.domain.api ,不可修改

勾选 Sign In with Apple 并配置

Primary App ID

选择刚刚创建的 App ID 进行绑定

Web Domain

配置安全接口域名,例如 domain.com ,不需要 https 协议头

在设置完域名后,你可能需要对域名进行所有权验证,上传指定的文件到 https://domain.com/.well-known/apple-developer-domain-association.txt

Return URLs

授权登录后的接口回调地址,可配置多个

创建 Key

在授权登录时,我们需要效验并获取用户 access_token 等信息,这些信息需要通过 JWT 方式进行封装

Configure 中需要绑定刚刚创建的 App ID

在下载完毕后,你将会下载一个 .p8 的文件

配置邮箱

通过 Sign With in Apple 进行登录的用户,可以选择使用匿名的邮箱,如果你需要通过匿名邮箱与用户联系,你需要在这里授权绑定你的认证邮箱地址和域名


注意:邮箱地址必须通过 TLS 加密连接

准备就绪

当你完成上面所有内容后就开始开发了,以下是开发过程中会用到的数据,你可以先准备起来

teamId

十位数的企业/个人账户编号

clientId

刚刚注册的 Services IDIdentifier

scope

所需要获得的用户信息,以逗号分隔,可以有 openid,name,email

redirectURI

授权成功后的回调地址,需要与创建 Services ID 时候填写的地址匹配

privateKey

刚刚下载的 p8 文件

生成授权地址并授权

生成授权地址的方案有 2 种,一种是通过自己拼接授权地址,进行跳转打开,或者通过 Apple 提供的 appleid.auth.js 插件生成调用。

方案 1

地址格式如下

https://appleid.apple.com/auth/authorize?response_type=code&    state=state&client_id=clientId&redirect_uri=redirectURI&scope=scope    

state

自定义随机字符串,用于防止 CSRF 攻击

涉及到的参数需使用 URL Encode 编码进行处理

示例

const options = {
    response_type: code, // 固定内容
    state: +new Date(), // 随机字符串,此处仅做演示
    client_id: clientId,
    redirect_uri: redirectURI,
    scope: 'openid name email'
}

const url = new URL('https://appleid.apple.com/auth/authorize')
const keys = Object.keys(options)
keys.forEach(key => {
    url.searchParams.append(key, options[key])
})

// 输出内容
console.log(url.toString())

参考:Sign in with Apple REST API

方案 2(推荐)

Apple 提供的 Sign In with Apple JS 非常方便,并且你可以快速生成一个登录按钮

在页面引入这个 JS 文件,其中地址中的 en_US 可以更改为 zh-CN,以生成中文形式的按钮

<html>
    <head>
        <meta name="appleid-signin-client-id" content="[CLIENT_ID]">
        <meta name="appleid-signin-scope" content="[SCOPES]">
        <meta name="appleid-signin-redirect-uri" content="[REDIRECT_URI]">
        <meta name="appleid-signin-state" content="[STATE]">
    </head>
    <body>
        <div id="appleid-signin" data-color="black" data-border="true" data-type="sign in"></div>
        <script type="text/javascript" src="https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js"></script>
    </body>
</html>

如果你不想用 Apple 提供的按钮,想自定义按钮或触发方式,你可以直接使用 Sign In with Apple JS 中提供的方法

AppleID.auth.init({
    clientId: 'clientId',
    scope: 'scope',
    redirectURI: 'redirectURI'
})
// 调用这个方法将直接 运行 window.location.href 进行页面跳转
AppleID.auth.signIn()

参考:Sign in with Apple JS

授权登录

如果你使用的是 Safari 浏览器,那么在授权登录时会弹出系统层的授权登录窗口,否则将会跳转到 Apple 授权登录页面,所以它支持任何系统下的任何浏览器。

应用图标:这里的应用图标是灰色的,这是因为 Apple 暂时不支持纯 Web 使用的自定义图标,除非你以上面创建的 App ID 到 App Stroe 提交一个应用,这里将显示应用的图标。

invalid_request:通常这个问题是因为你的回调地址和 Apple 后台配置的地址不相同或还未生效,多尝试几次即可。

验证授权信息

获取信息

在授权后将会以 POST 方式传递授权信息到设置的回调地址中,数据包括 codeid_tokenuser#1

{
    "code": "ccc9fe129dc0a...0k4W0aUqEA",
    "id_token": "eyJraWQiOiJBSURPUEsxIiwi...sb998sb0ZD-E5dJ9IQqXMSfR_5Q",
    "user": "{\"name\":{\"firstName\":\"测\",\"middleName\":\"\",\"lastName\":\"试\"},\"email\":\"javdhnec3z@privaterelay.appleid.com\"}"
}

user:user 信息格式为 JSON,需要序列化后使用

获取 AccessToken

POST https://appleid.apple.com/auth/token

参考 Generate and validate tokens

主要解释一下 client_secret 参数的生成方法,Apple 数据通常都使用 JWT(JSON Web Tokens) 方式加密。使用算法为 ES256

// header
const header = {
    alg: 'ES256',
    typ: 'JWT'
}

// payload
const t = Math.floor(Date.now() / 1000)
const payload = {
    iss: teamId,
    exp: t + 15777000, // 过期时间,六个月(最大)
    sub: clientId,
    aud: 'https://appleid.apple.com',
    iat: t // 精确到秒的时间戳
}

记得使用之前生成的 p8 格式的私钥进行数据加密

其他参数自行参考官方文档

// 回调内容
/**
    access_token: '',
    token_type: 'Bearer',
    expires_in: 3600,
    refresh_token: '',
    id_token: ''
*/

刷新授权 Token

你可以使用 refresh_token 对过期的 access_token 进行刷新#2

刷新 access_token 方式与验证授权方式大致相同,其中的 grant_type 值改为 refresh_token 并传递 refresh_token 参数

// 请求体
{
    client_id,
    client_secret,
    refresh_token,
    grant_type: 'refresh_token'
}

验证数据并获得用户详细信息

接口返回的 id_token 中包含了用户授权的详细信息,需要通过 JWT 进行解密

在解密时,需要获取到 Apple 的 PublicKey,这个 Key 需要通过 GET https://appleid.apple.com/auth/keys 获取,你可以直接打开这个页面查看

const result = jwt.verify(id_token, PublicKey, {
    algorithms: 'RS256'
})

// result
/**
{
    iss: 'https://appleid.apple.com',
    aud: 'com.domain',
    exp: 1571805905,
    iat: 1571805305,
    sub: '000598.189bb....9bb.0805', Apple用户标识符
    at_hash: '55RTKN2v1Vy5K1r5JkWmSg',
    email: 'd5b465sf2x@privaterelay.appleid.com',
    email_verified: 'true',
    is_private_email: 'true',
    auth_time: 1571805303
}
*/

切记验证aud iss 等信息,防止伪造数据

重点关注

经过以上繁琐的步骤后,你现在可以正式上线这个项目了,不过你必须清楚以下信息