Firebase删除使用apple正确签名的用户

xsuvu9jc  于 2023-10-22  发布在  其他
关注(0)|答案(4)|浏览(144)

我用Firebase实现了Sign-In-With-Apple。我也有删除用户的功能。这就是我的工作

static Future<bool> deleteUser(BuildContext context) async {
    try {
      await BackendService().deleteUser(
        context,
      );

      await currentUser!.delete(); // <-- this actually deleting the user from Auth

      Provider.of<DataProvider>(context, listen: false).reset();

      return true;
    } on FirebaseException catch (error) {
      print(error.message);
      AlertService.showSnackBar(
        title: 'Fehler',
        description: error.message ?? 'Unbekannter Fehler',
        isSuccess: false,
      );
      return false;
    }
  }

正如你所看到的,我删除了所有用户的数据,最后从auth删除了用户本人。
但苹果仍然认为我在使用App。我可以在我的设置中看到它:

此外,当试图再次登录与苹果,它的行为就像我已经有一个帐户。但是我刚刚删除了它,Firebase里面没有任何东西说我仍然有这个帐户?如何从Firebase中完全删除Apple用户?我错过了什么?

deikduxw

deikduxw1#

Apple和其他一些第三方身份提供商通常不提供API来执行此操作。
访问这些数据可能会导致隐私问题,例如,恶意应用程序可以在访问用户配置文件后删除授权信息。
但是,如果你想做一个“优雅”的注销,你可以要求你的用户从iOS设置注销,并听取服务器到服务器的通知撤销。

2w2cym1i

2w2cym1i2#

虽然用户的帐户已被删除的firebase它还没有被删除从苹果的系统。在为Apple编写firebase SDK时,仍在开发此功能git hub issue(计划于2022年第4季度或2023年第1季度),由于flutter和react native可能依赖于base SDK,因此需要自定义实现,直到可用为止。
根据Apple的说法,要完全删除用户的Apple帐户,您应该使用generate_tokens API获取Apple的刷新令牌,然后使用revoke_tokens API撤销它。
高级描述:
1.客户端(应用程序):获取Apple授权码。
1.将授权码发送到您的服务器。
1.服务器端:使用苹果p8密钥创建jwt令牌。Jwt令牌将用于对Apple API的请求进行身份验证
1.服务器端:refresh_token的交易授权代码(参见上面的第一个链接)
1.服务器端:撤销refresh_token(参见上面的第二个链接)
详细说明:https://stackoverflow.com/a/72656672/6357154
.NET的服务器端进程的植入。假设条件:

  • _client是一个在DI contrainer中注册的HttpClient,其基本URL来自上面发布的Apple文档
  • AppleClientOptions包含用于firebase上的Apple设置的相同值。
/// <summary>
/// Gets apple refresh token
/// SEE MORE: https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens
/// </summary>
/// <param name="jwtToken"></param>
/// <param name="authorizationCode"></param>
/// <returns></returns>
public async Task<string> GetTokenFromApple(string jwtToken, string authorizationCode)
{
    IEnumerable<KeyValuePair<string, string>> content = new[]
    {
        new KeyValuePair<string, string>("client_id", _appleClientOptions.ClientId),
        new KeyValuePair<string, string>("client_secret", jwtToken),
        new KeyValuePair<string, string>("code", authorizationCode),
        new KeyValuePair<string, string>("grant_type", "authorization_code"),
    };
    var encodedContent = new FormUrlEncodedContent(content);
    var response = await _client.PostAsync("auth/token", encodedContent);
    var responseAsString = await response.Content.ReadAsStringAsync();
    if (response.IsSuccessStatusCode)
    {
        var appleTokenResponse = JsonConvert.DeserializeObject<AppleTokenResponse>(responseAsString);
        return appleTokenResponse.refresh_token;
    }
    _logger.LogError($"GetTokenFromApple failed: {responseAsString}");
    return null;
}

/// <summary>
/// Revokes apple refresh token
/// SEE MORE: https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens
/// </summary>
/// <param name="jwtToken"></param>
/// <param name="refreshToken"></param>
/// <returns></returns>
public async Task<bool> RevokeToken(string jwtToken, string refreshToken)
{
    IEnumerable<KeyValuePair<string, string>> content = new[]
    {
        new KeyValuePair<string, string>("client_id", _appleClientOptions.ClientId),
        new KeyValuePair<string, string>("client_secret", jwtToken),
        new KeyValuePair<string, string>("token", refreshToken),
        new KeyValuePair<string, string>("token_type_hint", "refresh_token"),
    };
    var response = await _client.PostAsync("auth/revoke", new FormUrlEncodedContent(content));
    return response.IsSuccessStatusCode;
}

private string GenerateAppleJwtTokenLinux()
{
    var epochNow = (int) DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
    var (payload, extraHeaders) = CreateJwtPayload(
        epochNow, 
        _appleClientOptions.TeamId,
        _appleClientOptions.ClientId,
        _appleClientOptions.KeyId);
    
    var privateKeyCleaned = Base64Decode(_appleClientOptions.PrivateKey)
        .Replace("-----BEGIN PRIVATE KEY-----", string.Empty)
        .Replace("-----END PRIVATE KEY-----", string.Empty)
        .Replace("\r\n", string.Empty)
        .Replace("\r\n", string.Empty);
    var bytes = Convert.FromBase64String(privateKeyCleaned);
    
    using var ecDsaKey = ECDsa.Create();
    ecDsaKey!.ImportPkcs8PrivateKey(bytes, out _);
    
    return Jose.JWT.Encode(payload, ecDsaKey, JwsAlgorithm.ES256, extraHeaders);
}

private static (Dictionary<string, object> payload, Dictionary<string, object> extraHeaders) CreateJwtPayload(
    int epochNow,
    string teamId,
    string clientId,
    string keyId)
{
    var payload = new Dictionary<string, object>
    {
        {"iss", teamId},
        {"iat", epochNow},
        {"exp", epochNow + 12000},
        {"aud", "https://appleid.apple.com"},
        {"sub", clientId}
    };
    var extraHeaders = new Dictionary<string, object>
    {
        {"kid", keyId},
        {"alg", "ES256"}
    };
    return (payload, extraHeaders);
}

/// <summary>
/// https://developer.apple.com/documentation/sign_in_with_apple/tokenresponse
/// </summary>
public class AppleTokenResponse
{
    public string access_token { get; set; }
    
    public string expires_in { get; set; }
    
    public string id_token { get; set; }
    
    public string refresh_token { get; set; }
    
    public string token_type { get; set; }
}

public class AppleClientOptions
{
    public string TeamId { get; set; }

    public string ClientId { get; set; }

    public string KeyId { get; set; }
        
    public string PrivateKey { get; set; }
}
public async Task<bool> DeleteUsersAccountAsync(string appleAuthorizationCode)
{
    // Get jwt token:
    var jwtToken = _appleClient.GenerateAppleJwtTokenLinux(); // Apple client is code form above, registered in DI.
    // Get refresh token from authorization code:
    var refreshToken = await _appleClient.GetTokenFromApple(jwtToken, appleAuthorizationCode);
    if (string.IsNullOrEmpty(refreshToken)) return false;

    // Delete token:
    var isRevoked = await _appleClient.RevokeToken(jwtToken, refreshToken);
    _logger.LogInformation("Deleted apple tokens for {UserId}", userId);
    if (!isRevoked) return false;
    return true;
}

其他实现示例:

kkih6yb8

kkih6yb83#

你确实从Firebase中删除了用户,但苹果并不知道这一点。你也应该从苹果删除这些信息。打开iPhone上的“设置”应用,然后点按顶部的您的姓名。然后按“密码和安全”,然后按“Apple ID登录”。所有Apple ID登录都应该在那里列出,并且可以删除。

xn1cxnb4

xn1cxnb44#

Apple不提供这项服务。但对于我的情况,下面的解决方案工作得很好。
我的登录过程:

1.检查用户是否在之前登录

// Create an `OAuthCredential` from the credential returned by Apple.
  final oauthCredential = OAuthProvider("apple.com").credential(
    idToken: appleCredential.identityToken,
    rawNonce: rawNonce,
  );

  // If you can not access the email property in credential,
  // means that user already signed in with his appleId in the application once before
  bool isAlreadyRegistered = appleCredential.email == null;

现在到了关键部分:

2.登录用户并检查uid是否已在Firebase中存在

final UserCredential result =
      await FirebaseAuth.instance.signInWithCredential(
    oauthCredential,
  );

  isAlreadyRegistered = await BackendService.checkIfUserIdExists(
    result.user?.uid ?? '',
  );

checkIfUserIdExists也很简单:

static Future<bool> checkIfUserIdExists(String userId) async {
    try {
      var collectionRef = FirebaseFirestore.instance.collection(
        BackendKeys.users,
      );

      var doc = await collectionRef.doc(userId).get();
      return doc.exists;
    } on FirebaseException catch (e) {
      return false;
    }
  }

相关问题