.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
相关产品推荐
相关产品推荐

