在Firebase中撤销苹果登录的Token
本文将介绍如何在Firebase环境中撤销使用“苹果登录”方式登录的Token。
根据苹果审核指南,未在2022年6月30日之前采取行动的应用可能会被删除。
翻译人员为本文编写了翻译稿,如果您对这些句子感到奇怪或描述不准确,请原谅。
本文使用Firebase的Functions,如果Firebase将来提供相关函数,我建议使用它。
整个过程如下所示。
- 从用户登录的应用程序中获取 authorizationCode。
- 使用具有过期时间的 authorizationCode 获取无过期时间的刷新令牌。
- 保存刷新令牌后,在用户离开服务时撤销该令牌。
您可以在 https://appleid.apple.com/auth/token 获取刷新令牌,并在 https://appleid.apple.com/auth/revoke 撤销令牌。
开始
如果您已经使用Firebase实现了“苹果登录”,则应该在项目中某个位置有ASAuthorizationAppleIDCredential。
在我的情况下,它写成以下形式。
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
guard let nonce = currentNonce else {
fatalError("Invalid state: A login callback was received, but no login request was sent.")
}
guard let appleIDToken = appleIDCredential.identityToken else {
print("Unable to fetch identity token")
return
}
guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else {
print("Unable to serialize token string from data: \(appleIDToken.debugDescription)")
return
}
let credential = OAuthProvider.credential(withProviderID: "apple.com",
IDToken: idTokenString,
rawNonce: nonce)
Auth.auth().signIn(with: credential) { (authResult, error) in
if error {
print(error.localizedDescription)
return
}
}
}
}
我们需要的是授权码authorizationCode。将以下代码添加到获取idTokenString的 guard 下方即可。
...
guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else {
print("Unable to serialize token string from data: \(appleIDToken.debugDescription)")
return
}
if let authorizationCode = appleIDCredential.authorizationCode,
let codeString = String(data: authorizationCode, encoding: .utf8) {
print(codeString)
}
...
一旦您达到这个阶段,当用户登录时,可以获得授权码authorizationCode。
但是,我们需要通过authorizationCode获取刷新令牌refresh token,此操作需要JWT,因此让我们使用Firebase函数来实现。
暂停Xcode一会儿,并转到Firebase函数中的代码。
如果您从未使用过函数,请参考
https://firebase.google.com/docs/functions。
在Firebase函数中,您可以使用JavaScript或TypeScript,我使用了JavaScript。
首先,让我们声明一个全局创建JWT的函数。使用npm install安装所需的软件包。
有一个地方可以编写您的密钥文件和ID(团队、客户端、密钥)路由,因此请编写自己的信息。
如果您不知道自己的ID信息,请参考相关问题。
https://github.com/jooyoungho/apple-token-revoke-in-firebase/issues/1
function makeJWT() {
const jwt = require('jsonwebtoken')
const fs = require('fs')
let privateKey = fs.readFileSync('AuthKey_XXXXXXXXXX.p8');
let token = jwt.sign({
iss: 'YOUR TEAM ID',
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 120,
aud: 'https://appleid.apple.com',
sub: 'YOUR CLIENT ID'
}, privateKey, {
algorithm: 'ES256',
header: {
alg: 'ES256',
kid: 'YOUR KEY ID',
} });
return token;
}
上述函数是通过使用您的密钥信息创建JWT而返回的。
现在,让我们使用AuthorizationCode获取Refresh Token。
我们将向函数添加名为getRefreshToken的函数。
exports.getRefreshToken = functions.https.onRequest(async (request, response) => {
const axios = require('axios');
const qs = require('qs')
const code = request.query.code;
const client_secret = makeJWT();
let data = {
'code': code,
'client_id': 'YOUR CLIENT ID',
'client_secret': client_secret,
'grant_type': 'authorization_code'
}
return axios.post(`https://appleid.apple.com/auth/token`, qs.stringify(data), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
})
.then(async res => {
const refresh_token = res.data.refresh_token;
response.send(refresh_token);
});
});
当您调用上述函数时,会从查询中获取代码并获得一个refresh_token。
对于code而言,这是我们从应用程序中获取的授权码。
在连接到应用程序之前,让我们也添加一个revoke函数。
exports.revokeToken = functions.https.onRequest( async (request, response) => {
const axios = require('axios');
const qs = require('qs');
const refresh_token = request.query.refresh_token;
const client_secret = makeJWT();
let data = {
'token': refresh_token,
'client_id': 'YOUR CLIENT ID',
'client_secret': client_secret,
'token_type_hint': 'refresh_token'
};
return axios.post(`https://appleid.apple.com/auth/revoke`, qs.stringify(data), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
})
.then(async res => {
console.log(res.data);
});
});
以上函数基于我们获取的refresh_token撤销登录信息。
到目前为止,我们已经配置好了我们的函数。当我们执行“firebase deploy functions”时,我们将在Firebase函数控制台中添加一些内容。
![img](https://github.com/jooyoungho/apple-token-revoke-in-firebase/raw/main/img.png)
现在回到Xcode。
在之前编写用于保存Refresh token的代码中调用Functions地址。
我将其保存在UserDefaults中,您也可以将其保存在Firebase数据库中。
...
if let authorizationCode = appleIDCredential.authorizationCode, let codeString = String(data: authorizationCode, encoding: .utf8) {
let url = URL(string: "https://YOUR-URL.cloudfunctions.net/getRefreshToken?code=\(codeString)".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "https://apple.com")!
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
if let data = data {
let refreshToken = String(data: data, encoding: .utf8) ?? ""
print(refreshToken)
UserDefaults.standard.set(refreshToken, forKey: "refreshToken")
UserDefaults.standard.synchronize()
}
}
task.resume()
}
...
在此时,当用户登录时,其设备将把refresh_token保存为UserDefaults。现在剩下的就是在用户离开服务时撤销。
func removeAccount() {
let token = UserDefaults.standard.string(forKey: "refreshToken")
if let token = token {
let url = URL(string: "https://YOUR-URL.cloudfunctions.net/revokeToken?refresh_token=\(token)".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "https://apple.com")!
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
guard data != nil else { return }
}
task.resume()
}
...
FirebaseAuthentication.shared.signOut()
}
如果我们到目前为止都按照要求进行了操作,那么我们的应用程序应该已从“设置 - 密码与安全性 - 使用 Apple ID 的应用程序”中移除。
谢谢。