You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET FirebaseAuthLink调用GetFreshAuthAsync刷新认证触发空引用异常

Firebase认证刷新时FirebaseAuthLink抛出空引用异常

问题现象

基于Firebase实现用户认证服务时,执行认证刷新逻辑触发空引用异常:

  • 异常触发点为GetFreshUserAsync方法内的_authLink = await _authLink.GetFreshAuthAsync();语句
  • 调试确认异常触发时_authLink实例本身不为null,serializedAuthLink为null时触发的异常可正常捕获,排除_authLink实例本身为空的可能,初步判断为FirebaseAuthLink依赖的内部属性为null,但无法定位具体属性。

问题复现代码

public class FirebaseAuthenticationService : IAuthenticationService
{
    public UserModel UserModel { get; set; }

    private readonly string FIREBASE_KEY= "<<--MY_KEY_HIDDEN_FOR_STACKOVEFLOW-->>";
    private FirebaseAuthProvider _authProvider;
    private FirebaseAuthLink _authLink;

    public FirebaseAuthenticationService()
    {
        _authProvider = new FirebaseAuthProvider(new FirebaseConfig(FIREBASE_KEY));
    }

    public async Task<ResultModel<UserModel>> Login(UserModel user)
    {
        try
        {
            _authLink = await _authProvider.SignInWithEmailAndPasswordAsync(user.Email, user.Password);

            string serializedAuthLink = JsonConvert.SerializeObject(_authLink);

            await SecureStorage.SetAsync(ConfigConst.FIREBASE_USER_AUTHLINK, serializedAuthLink);

            ResultModel<UserModel> userResult = await GetFreshUserAsync();

            return userResult;
        }
        catch (Exception e)
        {
            return new ResultModel<UserModel>(false) { Message = e.Message };
        }

    }

    public Task<ResultModel<bool>> Logout()
    {
        Preferences.Remove(ConfigConst.FIREBASE_USER_AUTHLINK);

        return Task.FromResult(new ResultModel<bool>(true));
    }

    public async Task<ResultModel<bool>> Register(UserModel user)
    {
        try
        {
            await _authProvider.CreateUserWithEmailAndPasswordAsync(user.Email, user.Password, user.Name);

            return new ResultModel<bool>(true);
        }
        catch (Exception ex)
        {
            return new ResultModel<bool>(false) { Message = ex.Message };
        }
    }

    public async Task<ResultModel<UserModel>> GetFreshUserAsync()
    {
        try
        {
            if (_authLink is null)
            {
                var serializedAuthLink = await SecureStorage.GetAsync(ConfigConst.FIREBASE_USER_AUTHLINK);

                _authLink = JsonConvert.DeserializeObject<FirebaseAuthLink>(serializedAuthLink);
            }

            _authLink = await _authLink.GetFreshAuthAsync();

            User fu = await _authProvider.GetUserAsync(_authLink.FirebaseToken);

            UserModel = new UserModel()
            {
                Id = fu.LocalId,
                Email = fu.Email,
                Name = fu.DisplayName,
                JWT_Token = _authLink.FirebaseToken
            };

            return new ResultModel<UserModel>(true) { Content = UserModel };
        }
        catch (Exception e)
        {
            Preferences.Remove(ConfigConst.FIREBASE_USER_AUTHLINK);
            return new ResultModel<UserModel>(false) { Message = e.Message };
        }
    }
}

问题根因

FirebaseAuthLink 内部持有一个非公开的FirebaseAuthProvider依赖字段,该字段不会被Newtonsoft.Json的序列化/反序列化流程处理:

  • 登录流程中通过_authProvider.SignInWithEmailAndPasswordAsync拿到的_authLink实例,内部已经自动绑定了当前的_authProvider引用,调用刷新方法无异常
  • 从SecureStorage读取序列化字符串后,直接通过JsonConvert.DeserializeObject<FirebaseAuthLink>反序列化得到的实例,内部的FirebaseAuthProvider字段值为null。GetFreshAuthAsync方法执行时需要调用该provider实例发起令牌刷新请求,就会触发空引用异常,和_authLink本身是否为null无关。

修复方案

替换反序列化逻辑,使用FirebaseAuthProvider自带的ParseAuthLink方法从序列化字符串恢复认证凭据,该方法会自动为恢复的FirebaseAuthLink实例绑定内部依赖的provider引用,同时补充刷新后持久化新凭据、异常时同步清空内存状态的逻辑,修复后的GetFreshUserAsync代码如下:

public async Task<ResultModel<UserModel>> GetFreshUserAsync()
{
    try
    {
        if (_authLink is null)
        {
            var serializedAuthLink = await SecureStorage.GetAsync(ConfigConst.FIREBASE_USER_AUTHLINK);
            // 使用SDK自带方法反序列化,自动绑定内部依赖
            _authLink = _authProvider.ParseAuthLink(serializedAuthLink);
        }

        _authLink = await _authLink.GetFreshAuthAsync();

        User fu = await _authProvider.GetUserAsync(_authLink.FirebaseToken);

        UserModel = new UserModel()
        {
            Id = fu.LocalId,
            Email = fu.Email,
            Name = fu.DisplayName,
            JWT_Token = _authLink.FirebaseToken
        };

        // 刷新成功后持久化最新的认证凭据,覆盖旧的过期凭据
        string updatedSerializedLink = JsonConvert.SerializeObject(_authLink);
        await SecureStorage.SetAsync(ConfigConst.FIREBASE_USER_AUTHLINK, updatedSerializedLink);

        return new ResultModel<UserModel>(true) { Content = UserModel };
    }
    catch (Exception e)
    {
        Preferences.Remove(ConfigConst.FIREBASE_USER_AUTHLINK);
        // 同步清空内存中的无效凭据
        _authLink = null;
        return new ResultModel<UserModel>(false) { Message = e.Message };
    }
}

内容的提问来源于stack exchange,提问作者Jiren

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 05:24:16