using System; using System.Collections.Generic; using System.Threading.Tasks; /// /// Brisk 认证模块。 /// public sealed class BriskAuthModule : BriskModuleBase { /// /// 通过稳定的第三方用户 ID 换取 Brisk 登录态。 /// public async Task LoginWithUserIdAsync(string loginProvider, string loginUserId, BriskProfile profile = null) { RequireNotEmpty(loginProvider, nameof(loginProvider)); RequireNotEmpty(loginUserId, nameof(loginUserId)); return await LoginInternalAsync(CreateLoginBody(loginProvider, profile, loginUserId, null), loginProvider, loginUserId); } /// /// 通过第三方返回的 code 换取 Brisk 登录态。 /// public async Task LoginWithCodeAsync(string loginProvider, string code, BriskProfile profile = null) { RequireNotEmpty(loginProvider, nameof(loginProvider)); RequireNotEmpty(code, nameof(code)); return await LoginInternalAsync(CreateLoginBody(loginProvider, profile, null, code), loginProvider, null); } /// /// 登出当前账号并清理本地会话。 /// public async Task LogoutAsync() { var context = GetContext(); try { if (context.Session.HasAccessToken) { await context.HttpClient.PostJsonAsync("/auth/logout", new Dictionary(), true); } } catch (BriskAuthExpiredException) { // Local logout should still succeed even if the remote token has already expired. } catch (BriskBlockingException exception) { Brisk.NotifyBlockingError(exception); throw; } finally { context.Session.Clear(); await context.TokenStore.ClearAsync(); Brisk.NotifyLoggedOut(); } } private async Task LoginInternalAsync(Dictionary body, string requestedLoginProvider, string requestedLoginUserId) { return await ExecutePublicAsync(async context => { var data = await context.HttpClient.PostJsonAsync("/auth/login/exchange", body, false); var result = BriskModelMapper.ToLoginResult(data); if (string.IsNullOrWhiteSpace(result.LoginProvider)) { result.LoginProvider = requestedLoginProvider; } if (string.IsNullOrWhiteSpace(result.LoginUserId)) { result.LoginUserId = requestedLoginUserId; } await UpdateSessionAsync(context, result); Brisk.NotifyLoggedIn(); return result; }); } private Dictionary CreateLoginBody(string loginProvider, BriskProfile profile, string loginUserId, string code) { var context = GetContext(); var body = new Dictionary { { "game_key", context.Options.GameKey }, { "login_provider", loginProvider } }; AddIfNotEmpty(body, "login_user_id", loginUserId); AddIfNotEmpty(body, "code", code); AddIfNotEmpty(body, "device_id", context.Options.DeviceId); AddIfNotEmpty(body, "client_version", context.Options.ClientVersion); if (profile != null) { AddIfNotEmpty(body, "nickname", profile.Nickname); AddIfNotEmpty(body, "avatar_url", profile.AvatarUrl); if (profile.ProfileJson != null) { body["profile_json"] = profile.ProfileJson; } } return body; } private static void AddIfNotEmpty(Dictionary body, string key, string value) { if (!string.IsNullOrWhiteSpace(value)) { body[key] = value; } } private async Task UpdateSessionAsync(BriskContext context, BriskLoginResult result) { var expiresAt = result.ExpiresIn > 0 ? DateTimeOffset.UtcNow.AddSeconds(result.ExpiresIn) : (DateTimeOffset?)null; context.Session.Update( result.AccessToken, expiresAt, result.PlayerId, result.ProjectAccountId, result.LoginProvider, result.LoginUserId); var storedSession = new BriskStoredSession { AccessToken = result.AccessToken, ExpiresAt = expiresAt, PlayerId = result.PlayerId, ProjectAccountId = result.ProjectAccountId, LoginProvider = result.LoginProvider, LoginUserId = result.LoginUserId }; await context.TokenStore.SaveAsync(storedSession); } }