csharp-sdk-upm/Storage/Source/Public/AVUser.cs

1311 lines
55 KiB
C#
Raw Normal View History

2019-07-19 15:01:34 +08:00
using LeanCloud.Storage.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace LeanCloud {
2019-07-19 15:01:34 +08:00
/// <summary>
/// Represents a user for a LeanCloud application.
/// </summary>
[AVClassName("_User")]
public class AVUser : AVObject {
2019-07-19 15:01:34 +08:00
private static readonly IDictionary<string, IAVAuthenticationProvider> authProviders =
new Dictionary<string, IAVAuthenticationProvider>();
private static readonly HashSet<string> readOnlyKeys = new HashSet<string> {
"sessionToken", "isNew"
};
internal static IAVUserController UserController {
get {
2019-07-19 15:01:34 +08:00
return AVPlugins.Instance.UserController;
}
}
internal static IAVCurrentUserController CurrentUserController {
get {
2019-07-19 15:01:34 +08:00
return AVPlugins.Instance.CurrentUserController;
}
}
/// <summary>
/// Whether the AVUser has been authenticated on this device. Only an authenticated
/// AVUser can be saved and deleted.
/// </summary>
[Obsolete("This property is deprecated, please use IsAuthenticatedAsync instead.")]
public bool IsAuthenticated {
get {
lock (mutex) {
2019-07-19 15:01:34 +08:00
return SessionToken != null &&
CurrentUser != null &&
CurrentUser.ObjectId == ObjectId;
}
}
}
/// <summary>
/// Whether the AVUser has been authenticated on this device, and the AVUser's session token is expired.
/// Only an authenticated AVUser can be saved and deleted.
/// </summary>
public Task<bool> IsAuthenticatedAsync() {
lock (mutex) {
if (SessionToken == null || CurrentUser == null || CurrentUser.ObjectId != ObjectId) {
2019-07-19 15:01:34 +08:00
return Task.FromResult(false);
}
}
var command = new AVCommand(String.Format("users/me?session_token={0}", CurrentSessionToken),
method: "GET",
data: null);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => {
2019-07-19 15:01:34 +08:00
return AVClient.IsSuccessStatusCode(t.Result.Item1);
});
}
/// <summary>
/// Refresh this user's session token, and current session token will be invalid.
/// </summary>
public Task RefreshSessionTokenAsync(CancellationToken cancellationToken) {
return UserController.RefreshSessionTokenAsync(ObjectId, SessionToken, cancellationToken).OnSuccess(t => {
2019-07-19 15:01:34 +08:00
var serverState = t.Result;
HandleSave(serverState);
});
}
/// <summary>
/// Removes a key from the object's data if it exists.
/// </summary>
/// <param name="key">The key to remove.</param>
/// <exception cref="System.ArgumentException">Cannot remove the username key.</exception>
public override void Remove(string key) {
if (key == "username") {
2019-07-19 15:01:34 +08:00
throw new ArgumentException("Cannot remove the username key.");
}
base.Remove(key);
}
protected override bool IsKeyMutable(string key) {
2019-07-19 15:01:34 +08:00
return !readOnlyKeys.Contains(key);
}
internal override void HandleSave(IObjectState serverState) {
2019-07-19 15:01:34 +08:00
base.HandleSave(serverState);
SynchronizeAllAuthData();
CleanupAuthData();
MutateState(mutableClone => {
2019-07-19 15:01:34 +08:00
mutableClone.ServerData.Remove("password");
});
}
/// <summary>
/// authenticated token.
/// </summary>
public string SessionToken {
get {
if (State.ContainsKey("sessionToken")) {
2019-07-19 15:01:34 +08:00
return State["sessionToken"] as string;
}
return null;
}
}
internal static string CurrentSessionToken {
get {
2019-07-19 15:01:34 +08:00
Task<string> sessionTokenTask = GetCurrentSessionTokenAsync();
sessionTokenTask.Wait();
return sessionTokenTask.Result;
}
}
internal static Task<string> GetCurrentSessionTokenAsync(CancellationToken cancellationToken = default) {
2019-07-19 15:01:34 +08:00
return CurrentUserController.GetCurrentSessionTokenAsync(cancellationToken);
}
internal Task SetSessionTokenAsync(string newSessionToken) {
2019-07-19 15:01:34 +08:00
return SetSessionTokenAsync(newSessionToken, CancellationToken.None);
}
internal Task SetSessionTokenAsync(string newSessionToken, CancellationToken cancellationToken) {
MutateState(mutableClone => {
2019-07-19 15:01:34 +08:00
mutableClone.ServerData["sessionToken"] = newSessionToken;
});
return SaveCurrentUserAsync(this);
}
/// <summary>
/// Gets or sets the username.
/// </summary>
[AVFieldName("username")]
public string Username {
2019-07-19 15:01:34 +08:00
get { return GetProperty<string>(null, "Username"); }
set { SetProperty(value, "Username"); }
}
/// <summary>
/// Sets the password.
/// </summary>
[AVFieldName("password")]
public string Password {
2019-07-19 15:01:34 +08:00
private get { return GetProperty<string>(null, "Password"); }
set { SetProperty(value, "Password"); }
}
/// <summary>
/// Sets the email address.
/// </summary>
[AVFieldName("email")]
public string Email {
2019-07-19 15:01:34 +08:00
get { return GetProperty<string>(null, "Email"); }
set { SetProperty(value, "Email"); }
}
/// <summary>
/// 用户手机号。
/// </summary>
[AVFieldName("mobilePhoneNumber")]
public string MobilePhoneNumber {
get {
2019-07-19 15:01:34 +08:00
return GetProperty<string>(null, "MobilePhoneNumber");
}
set {
2019-07-19 15:01:34 +08:00
SetProperty<string>(value, "MobilePhoneNumber");
}
}
/// <summary>
/// 用户手机号是否已经验证
/// </summary>
/// <value><c>true</c> if mobile phone verified; otherwise, <c>false</c>.</value>
[AVFieldName("mobilePhoneVerified")]
public bool MobilePhoneVerified {
get {
2019-07-19 15:01:34 +08:00
return GetProperty<bool>(false, "MobilePhoneVerified");
}
set {
2019-07-19 15:01:34 +08:00
SetProperty<bool>(value, "MobilePhoneVerified");
}
}
/// <summary>
/// 判断用户是否为匿名用户
/// </summary>
public bool IsAnonymous {
get {
2019-07-19 15:01:34 +08:00
bool rtn = false;
if (this.AuthData != null) {
2019-07-19 15:01:34 +08:00
rtn = this.AuthData.Keys.Contains("anonymous");
}
return rtn;
}
}
internal Task SignUpAsync(Task toAwait, CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
return this.Create(toAwait, cancellationToken).OnSuccess(_ => SaveCurrentUserAsync(this)).Unwrap();
}
/// <summary>
/// Signs up a new user. This will create a new AVUser on the server and will also persist the
/// session on disk so that you can access the user using <see cref="CurrentUser"/>. A username and
/// password must be set before calling SignUpAsync.
/// </summary>
public Task SignUpAsync() {
2019-07-19 15:01:34 +08:00
return SignUpAsync(CancellationToken.None);
}
/// <summary>
/// Signs up a new user. This will create a new AVUser on the server and will also persist the
/// session on disk so that you can access the user using <see cref="CurrentUser"/>. A username and
/// password must be set before calling SignUpAsync.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
public Task SignUpAsync(CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
return taskQueue.Enqueue(toAwait => SignUpAsync(toAwait, cancellationToken),
cancellationToken);
}
#region 事件流系统相关 API
/// <summary>
/// 关注某个用户
/// </summary>
/// <param name="userObjectId">被关注的用户</param>
/// <returns></returns>
public Task<bool> FollowAsync(string userObjectId) {
2019-07-19 15:01:34 +08:00
return this.FollowAsync(userObjectId, null);
}
/// <summary>
/// 关注某个用户
/// </summary>
/// <param name="userObjectId">被关注的用户Id</param>
/// <param name="data">关注的时候附加属性</param>
/// <returns></returns>
public Task<bool> FollowAsync(string userObjectId, IDictionary<string, object> data) {
if (data != null) {
2019-07-19 15:01:34 +08:00
data = this.EncodeForSaving(data);
}
var command = new AVCommand(string.Format("users/{0}/friendship/{1}", this.ObjectId, userObjectId),
method: "POST",
sessionToken: CurrentSessionToken,
data: data);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => {
2019-07-19 15:01:34 +08:00
return AVClient.IsSuccessStatusCode(t.Result.Item1);
});
}
/// <summary>
/// 取关某一个用户
/// </summary>
/// <param name="userObjectId"></param>
/// <returns></returns>
public Task<bool> UnfollowAsync(string userObjectId) {
2019-07-19 15:01:34 +08:00
var command = new AVCommand(string.Format("users/{0}/friendship/{1}", this.ObjectId, userObjectId),
method: "DELETE",
sessionToken: CurrentSessionToken,
data: null);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => {
2019-07-19 15:01:34 +08:00
return AVClient.IsSuccessStatusCode(t.Result.Item1);
});
}
/// <summary>
/// 获取当前用户的关注者的查询
/// </summary>
/// <returns></returns>
public AVQuery<AVUser> GetFollowerQuery() {
2019-07-19 15:01:34 +08:00
AVQuery<AVUser> query = new AVQuery<AVUser>();
query.RelativeUri = string.Format("users/{0}/followers", this.ObjectId);
return query;
}
/// <summary>
/// 获取当前用户所关注的用户的查询
/// </summary>
/// <returns></returns>
public AVQuery<AVUser> GetFolloweeQuery() {
2019-07-19 15:01:34 +08:00
AVQuery<AVUser> query = new AVQuery<AVUser>();
query.RelativeUri = string.Format("users/{0}/followees", this.ObjectId);
return query;
}
/// <summary>
/// 同时查询关注了当前用户的关注者和当前用户所关注的用户
/// </summary>
/// <returns></returns>
public AVQuery<AVUser> GetFollowersAndFolloweesQuery() {
2019-07-19 15:01:34 +08:00
AVQuery<AVUser> query = new AVQuery<AVUser>();
query.RelativeUri = string.Format("users/{0}/followersAndFollowees", this.ObjectId);
return query;
}
/// <summary>
/// 获取当前用户的关注者
/// </summary>
/// <returns></returns>
public Task<IEnumerable<AVUser>> GetFollowersAsync() {
2019-07-19 15:01:34 +08:00
return this.GetFollowerQuery().FindAsync();
}
/// <summary>
/// 获取当前用户所关注的用户
/// </summary>
/// <returns></returns>
public Task<IEnumerable<AVUser>> GetFolloweesAsync() {
2019-07-19 15:01:34 +08:00
return this.GetFolloweeQuery().FindAsync();
}
//public Task<AVStatus> SendStatusAsync()
//{
//}
#endregion
/// <summary>
/// Logs in a user with a username and password. On success, this saves the session to disk so you
/// can retrieve the currently logged in user using <see cref="CurrentUser"/>.
/// </summary>
/// <param name="username">The username to log in with.</param>
/// <param name="password">The password to log in with.</param>
/// <returns>The newly logged-in user.</returns>
public static Task<AVUser> LogInAsync(string username, string password) {
2019-07-19 15:01:34 +08:00
return LogInAsync(username, password, CancellationToken.None);
}
/// <summary>
/// Logs in a user with a username and password. On success, this saves the session to disk so you
/// can retrieve the currently logged in user using <see cref="CurrentUser"/>.
/// </summary>
/// <param name="username">The username to log in with.</param>
/// <param name="password">The password to log in with.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The newly logged-in user.</returns>
public static Task<AVUser> LogInAsync(string username,
string password,
CancellationToken cancellationToken) {
return UserController.LogInAsync(username, null, password, cancellationToken).OnSuccess(t => {
2019-07-19 15:01:34 +08:00
AVUser user = AVObject.FromState<AVUser>(t.Result, "_User");
return SaveCurrentUserAsync(user).OnSuccess(_ => user);
}).Unwrap();
}
/// <summary>
/// Logs in a user with a username and password. On success, this saves the session to disk so you
/// can retrieve the currently logged in user using <see cref="CurrentUser"/>.
/// </summary>
/// <param name="sessionToken">The session token to authorize with</param>
/// <returns>The user if authorization was successful</returns>
public static Task<AVUser> BecomeAsync(string sessionToken) {
2019-07-19 15:01:34 +08:00
return BecomeAsync(sessionToken, CancellationToken.None);
}
/// <summary>
/// Logs in a user with a username and password. On success, this saves the session to disk so you
/// can retrieve the currently logged in user using <see cref="CurrentUser"/>.
/// </summary>
/// <param name="sessionToken">The session token to authorize with</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The user if authorization was successful</returns>
public static Task<AVUser> BecomeAsync(string sessionToken, CancellationToken cancellationToken) {
return UserController.GetUserAsync(sessionToken, cancellationToken).OnSuccess(t => {
2019-07-19 15:01:34 +08:00
AVUser user = AVObject.FromState<AVUser>(t.Result, "_User");
return SaveCurrentUserAsync(user).OnSuccess(_ => user);
}).Unwrap();
}
protected override Task SaveAsync(Task toAwait, CancellationToken cancellationToken) {
lock (mutex) {
if (ObjectId == null) {
2019-07-19 15:01:34 +08:00
throw new InvalidOperationException("You must call SignUpAsync before calling SaveAsync.");
}
return base.SaveAsync(toAwait, cancellationToken).OnSuccess(_ => {
if (!CurrentUserController.IsCurrent(this)) {
2019-07-19 15:01:34 +08:00
return Task.FromResult(0);
}
return SaveCurrentUserAsync(this);
}).Unwrap();
}
}
internal override Task<AVObject> FetchAsyncInternal(Task toAwait, IDictionary<string, object> queryString, CancellationToken cancellationToken) {
return base.FetchAsyncInternal(toAwait, queryString, cancellationToken).OnSuccess(t => {
if (!CurrentUserController.IsCurrent(this)) {
return Task<AVObject>.FromResult(t.Result);
}
// If this is already the current user, refresh its state on disk.
return SaveCurrentUserAsync(this).OnSuccess(_ => t.Result);
}).Unwrap();
2019-07-19 15:01:34 +08:00
}
/// <summary>
/// Logs out the currently logged in user session. This will remove the session from disk, log out of
/// linked services, and future calls to <see cref="CurrentUser"/> will return <c>null</c>.
/// </summary>
/// <remarks>
/// Typically, you should use <see cref="LogOutAsync()"/>, unless you are managing your own threading.
/// </remarks>
public static void LogOut() {
2019-07-19 15:01:34 +08:00
// TODO (hallucinogen): this will without a doubt fail in Unity. But what else can we do?
LogOutAsync().Wait();
}
/// <summary>
/// Logs out the currently logged in user session. This will remove the session from disk, log out of
/// linked services, and future calls to <see cref="CurrentUser"/> will return <c>null</c>.
/// </summary>
/// <remarks>
/// This is preferable to using <see cref="LogOut()"/>, unless your code is already running from a
/// background thread.
/// </remarks>
public static Task LogOutAsync() {
2019-07-19 15:01:34 +08:00
return LogOutAsync(CancellationToken.None);
}
/// <summary>
/// Logs out the currently logged in user session. This will remove the session from disk, log out of
/// linked services, and future calls to <see cref="CurrentUser"/> will return <c>null</c>.
///
/// This is preferable to using <see cref="LogOut()"/>, unless your code is already running from a
/// background thread.
/// </summary>
public static Task LogOutAsync(CancellationToken cancellationToken) {
return GetCurrentUserAsync().OnSuccess(t => {
2019-07-19 15:01:34 +08:00
LogOutWithProviders();
AVUser user = t.Result;
if (user == null) {
2019-07-19 15:01:34 +08:00
return Task.FromResult(0);
}
return user.taskQueue.Enqueue(toAwait => user.LogOutAsync(toAwait, cancellationToken), cancellationToken);
}).Unwrap();
}
internal Task LogOutAsync(Task toAwait, CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
string oldSessionToken = SessionToken;
if (oldSessionToken == null) {
2019-07-19 15:01:34 +08:00
return Task.FromResult(0);
}
// Cleanup in-memory session.
MutateState(mutableClone => {
2019-07-19 15:01:34 +08:00
mutableClone.ServerData.Remove("sessionToken");
});
var revokeSessionTask = AVSession.RevokeAsync(oldSessionToken, cancellationToken);
return Task.WhenAll(revokeSessionTask, CurrentUserController.LogOutAsync(cancellationToken));
}
private static void LogOutWithProviders() {
foreach (var provider in authProviders.Values) {
2019-07-19 15:01:34 +08:00
provider.Deauthenticate();
}
}
/// <summary>
/// Gets the currently logged in AVUser with a valid session, either from memory or disk
/// if necessary.
/// </summary>
public static AVUser CurrentUser {
get {
2019-07-19 15:01:34 +08:00
var userTask = GetCurrentUserAsync();
// TODO (hallucinogen): this will without a doubt fail in Unity. How should we fix it?
userTask.Wait();
return userTask.Result;
}
}
public static Task<AVUser> GetCurrentAsync() {
2019-07-19 15:01:34 +08:00
var userTask = GetCurrentUserAsync();
return userTask;
}
/// <summary>
/// Gets the currently logged in AVUser with a valid session, either from memory or disk
/// if necessary, asynchronously.
/// </summary>
public static Task<AVUser> GetCurrentUserAsync() {
2019-07-19 15:01:34 +08:00
return GetCurrentUserAsync(CancellationToken.None);
}
/// <summary>
/// Gets the currently logged in AVUser with a valid session, either from memory or disk
/// if necessary, asynchronously.
/// </summary>
internal static Task<AVUser> GetCurrentUserAsync(CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
return CurrentUserController.GetAsync(cancellationToken);
}
private static Task SaveCurrentUserAsync(AVUser user) {
2019-07-19 15:01:34 +08:00
return SaveCurrentUserAsync(user, CancellationToken.None);
}
private static Task SaveCurrentUserAsync(AVUser user, CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
return CurrentUserController.SetAsync(user, cancellationToken);
}
internal static void ClearInMemoryUser() {
2019-07-19 15:01:34 +08:00
CurrentUserController.ClearFromMemory();
}
/// <summary>
/// Constructs a <see cref="AVQuery{AVUser}"/> for AVUsers.
/// </summary>
public static AVQuery<AVUser> Query {
get {
2019-07-19 15:01:34 +08:00
return new AVQuery<AVUser>();
}
}
#region Legacy / Revocable Session Tokens
private static readonly object isRevocableSessionEnabledMutex = new object();
private static bool isRevocableSessionEnabled;
/// <summary>
/// Tells server to use revocable session on LogIn and SignUp, even when App's Settings
/// has "Require Revocable Session" turned off. Issues network request in background to
/// migrate the sessionToken on disk to revocable session.
/// </summary>
/// <returns>The Task that upgrades the session.</returns>
public static Task EnableRevocableSessionAsync() {
2019-07-19 15:01:34 +08:00
return EnableRevocableSessionAsync(CancellationToken.None);
}
/// <summary>
/// Tells server to use revocable session on LogIn and SignUp, even when App's Settings
/// has "Require Revocable Session" turned off. Issues network request in background to
/// migrate the sessionToken on disk to revocable session.
/// </summary>
/// <returns>The Task that upgrades the session.</returns>
public static Task EnableRevocableSessionAsync(CancellationToken cancellationToken) {
lock (isRevocableSessionEnabledMutex) {
2019-07-19 15:01:34 +08:00
isRevocableSessionEnabled = true;
}
return GetCurrentUserAsync(cancellationToken).OnSuccess(t => {
2019-07-19 15:01:34 +08:00
var user = t.Result;
return user.UpgradeToRevocableSessionAsync(cancellationToken);
});
}
internal static void DisableRevocableSession() {
lock (isRevocableSessionEnabledMutex) {
2019-07-19 15:01:34 +08:00
isRevocableSessionEnabled = false;
}
}
internal static bool IsRevocableSessionEnabled {
get {
lock (isRevocableSessionEnabledMutex) {
2019-07-19 15:01:34 +08:00
return isRevocableSessionEnabled;
}
}
}
internal Task UpgradeToRevocableSessionAsync() {
2019-07-19 15:01:34 +08:00
return UpgradeToRevocableSessionAsync(CancellationToken.None);
}
public Task UpgradeToRevocableSessionAsync(CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
return taskQueue.Enqueue(toAwait => UpgradeToRevocableSessionAsync(toAwait, cancellationToken),
cancellationToken);
}
internal Task UpgradeToRevocableSessionAsync(Task toAwait, CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
string sessionToken = SessionToken;
return toAwait.OnSuccess(_ => {
2019-07-19 15:01:34 +08:00
return AVSession.UpgradeToRevocableSessionAsync(sessionToken, cancellationToken);
}).Unwrap().OnSuccess(t => {
2019-07-19 15:01:34 +08:00
return SetSessionTokenAsync(t.Result);
}).Unwrap();
}
#endregion
/// <summary>
/// Requests a password reset email to be sent to the specified email address associated with the
/// user account. This email allows the user to securely reset their password on the LeanCloud site.
/// </summary>
/// <param name="email">The email address associated with the user that forgot their password.</param>
public static Task RequestPasswordResetAsync(string email) {
2019-07-19 15:01:34 +08:00
return RequestPasswordResetAsync(email, CancellationToken.None);
}
/// <summary>
/// Requests a password reset email to be sent to the specified email address associated with the
/// user account. This email allows the user to securely reset their password on the LeanCloud site.
/// </summary>
/// <param name="email">The email address associated with the user that forgot their password.</param>
/// <param name="cancellationToken">The cancellation token.</param>
public static Task RequestPasswordResetAsync(string email,
CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
return UserController.RequestPasswordResetAsync(email, cancellationToken);
}
/// <summary>
/// Updates current user's password. Need the user's old password,
/// </summary>
/// <returns>The password.</returns>
/// <param name="oldPassword">Old password.</param>
/// <param name="newPassword">New password.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public Task UpdatePassword(string oldPassword, string newPassword, CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
return UserController.UpdatePasswordAsync(ObjectId, SessionToken, oldPassword, newPassword, cancellationToken);
}
/// <summary>
/// Gets the authData for this user.
/// </summary>
internal IDictionary<string, IDictionary<string, object>> AuthData {
get {
2019-07-19 15:01:34 +08:00
IDictionary<string, IDictionary<string, object>> authData;
if (this.TryGetValue<IDictionary<string, IDictionary<string, object>>>(
"authData", out authData)) {
2019-07-19 15:01:34 +08:00
return authData;
}
return null;
}
private set {
2019-07-19 15:01:34 +08:00
this["authData"] = value;
}
}
private static IAVAuthenticationProvider GetProvider(string providerName) {
2019-07-19 15:01:34 +08:00
IAVAuthenticationProvider provider;
if (authProviders.TryGetValue(providerName, out provider)) {
2019-07-19 15:01:34 +08:00
return provider;
}
return null;
}
/// <summary>
/// Removes null values from authData (which exist temporarily for unlinking)
/// </summary>
private void CleanupAuthData() {
lock (mutex) {
if (!CurrentUserController.IsCurrent(this)) {
2019-07-19 15:01:34 +08:00
return;
}
var authData = AuthData;
if (authData == null) {
2019-07-19 15:01:34 +08:00
return;
}
foreach (var pair in new Dictionary<string, IDictionary<string, object>>(authData)) {
if (pair.Value == null) {
2019-07-19 15:01:34 +08:00
authData.Remove(pair.Key);
}
}
}
}
/// <summary>
/// Synchronizes authData for all providers.
/// </summary>
private void SynchronizeAllAuthData() {
lock (mutex) {
2019-07-19 15:01:34 +08:00
var authData = AuthData;
if (authData == null) {
2019-07-19 15:01:34 +08:00
return;
}
foreach (var pair in authData) {
2019-07-19 15:01:34 +08:00
SynchronizeAuthData(GetProvider(pair.Key));
}
}
}
private void SynchronizeAuthData(IAVAuthenticationProvider provider) {
2019-07-19 15:01:34 +08:00
bool restorationSuccess = false;
lock (mutex) {
2019-07-19 15:01:34 +08:00
var authData = AuthData;
if (authData == null || provider == null) {
2019-07-19 15:01:34 +08:00
return;
}
IDictionary<string, object> data;
if (authData.TryGetValue(provider.AuthType, out data)) {
2019-07-19 15:01:34 +08:00
restorationSuccess = provider.RestoreAuthentication(data);
}
}
if (!restorationSuccess) {
2019-07-19 15:01:34 +08:00
this.UnlinkFromAsync(provider.AuthType, CancellationToken.None);
}
}
public Task LinkWithAsync(string authType, IDictionary<string, object> data, CancellationToken cancellationToken) {
return taskQueue.Enqueue(toAwait => {
2019-07-19 15:01:34 +08:00
AuthData = new Dictionary<string, IDictionary<string, object>>();
AuthData[authType] = data;
return SaveAsync(cancellationToken);
}, cancellationToken);
}
public Task LinkWithAsync(string authType, CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
var provider = GetProvider(authType);
return provider.AuthenticateAsync(cancellationToken)
.OnSuccess(t => LinkWithAsync(authType, t.Result, cancellationToken))
.Unwrap();
}
/// <summary>
/// Unlinks a user from a service.
/// </summary>
public Task UnlinkFromAsync(string authType, CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
return LinkWithAsync(authType, null, cancellationToken);
}
/// <summary>
/// Checks whether a user is linked to a service.
/// </summary>
internal bool IsLinked(string authType) {
lock (mutex) {
2019-07-19 15:01:34 +08:00
return AuthData != null && AuthData.ContainsKey(authType) && AuthData[authType] != null;
}
}
internal static Task<AVUser> LogInWithAsync(string authType,
IDictionary<string, object> data,
bool failOnNotExist,
CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
AVUser user = null;
return UserController.LogInAsync(authType, data, failOnNotExist, cancellationToken).OnSuccess(t => {
2019-07-19 15:01:34 +08:00
user = AVObject.FromState<AVUser>(t.Result, "_User");
lock (user.mutex) {
if (user.AuthData == null) {
2019-07-19 15:01:34 +08:00
user.AuthData = new Dictionary<string, IDictionary<string, object>>();
}
user.AuthData[authType] = data;
user.SynchronizeAllAuthData();
}
return SaveCurrentUserAsync(user);
}).Unwrap().OnSuccess(t => user);
}
internal static Task<AVUser> LogInWithAsync(string authType,
CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
var provider = GetProvider(authType);
return provider.AuthenticateAsync(cancellationToken)
.OnSuccess(authData => LogInWithAsync(authType, authData.Result, false, cancellationToken))
.Unwrap();
}
internal static void RegisterProvider(IAVAuthenticationProvider provider) {
2019-07-19 15:01:34 +08:00
authProviders[provider.AuthType] = provider;
if (CurrentUser != null) {
CurrentUser.SynchronizeAuthData(provider);
2019-07-19 15:01:34 +08:00
}
}
#region 手机号登录
internal static Task<AVUser> LogInWithParametersAsync(Dictionary<string, object> strs, CancellationToken cancellationToken) {
AVUser avUser = CreateWithoutData<AVUser>(null);
2019-07-19 15:01:34 +08:00
return UserController.LogInWithParametersAsync("login", strs, cancellationToken).OnSuccess(t => {
var user = CreateWithoutData<AVUser>(null);
2019-07-19 15:01:34 +08:00
user.HandleFetchResult(t.Result);
return SaveCurrentUserAsync(user).OnSuccess(_ => user);
}).Unwrap();
}
/// <summary>
/// 以手机号和密码实现登陆。
/// </summary>
/// <param name="mobilePhoneNumber">手机号</param>
/// <param name="password">密码</param>
/// <returns></returns>
public static Task<AVUser> LogInByMobilePhoneNumberAsync(string mobilePhoneNumber, string password) {
return LogInByMobilePhoneNumberAsync(mobilePhoneNumber, password, CancellationToken.None);
2019-07-19 15:01:34 +08:00
}
/// <summary>
/// 用邮箱作和密码匹配登录
/// </summary>
/// <param name="email">邮箱</param>
/// <param name="password">密码</param>
/// <returns></returns>
public static Task<AVUser> LogInByEmailAsync(string email, string password, CancellationToken cancellationToken = default) {
2019-07-19 15:01:34 +08:00
return UserController.LogInAsync(null, email, password, cancellationToken).OnSuccess(t => {
AVUser user = FromState<AVUser>(t.Result, "_User");
2019-07-19 15:01:34 +08:00
return SaveCurrentUserAsync(user).OnSuccess(_ => user);
}).Unwrap();
}
/// <summary>
/// 以手机号和密码匹配登陆
/// </summary>
/// <param name="mobilePhoneNumber">手机号</param>
/// <param name="password">密码</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static Task<AVUser> LogInByMobilePhoneNumberAsync(string mobilePhoneNumber, string password, CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
Dictionary<string, object> strs = new Dictionary<string, object>()
{
{ "mobilePhoneNumber", mobilePhoneNumber },
{ "password", password }
};
return LogInWithParametersAsync(strs, cancellationToken);
2019-07-19 15:01:34 +08:00
}
/// <summary>
/// 以手机号和验证码登陆
/// </summary>
/// <param name="mobilePhoneNumber">手机号</param>
/// <param name="smsCode">短信验证码</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static Task<AVUser> LogInBySmsCodeAsync(string mobilePhoneNumber, string smsCode, CancellationToken cancellationToken = default) {
2019-07-19 15:01:34 +08:00
Dictionary<string, object> strs = new Dictionary<string, object>()
{
{ "mobilePhoneNumber", mobilePhoneNumber },
{ "smsCode", smsCode }
};
return LogInWithParametersAsync(strs, cancellationToken);
2019-07-19 15:01:34 +08:00
}
/// <summary>
/// Requests the login SMS code asynchronous.
/// </summary>
/// <param name="mobilePhoneNumber">The mobile phone number.</param>
/// <returns></returns>
public static Task<bool> RequestLogInSmsCodeAsync(string mobilePhoneNumber) {
return RequestLogInSmsCodeAsync(mobilePhoneNumber, CancellationToken.None);
2019-07-19 15:01:34 +08:00
}
/// <summary>
/// Requests the login SMS code asynchronous.
/// </summary>
/// <param name="mobilePhoneNumber">The mobile phone number.</param>
/// <param name="validateToken">Validate token.</param>
/// <returns></returns>
public static Task<bool> RequestLogInSmsCodeAsync(string mobilePhoneNumber, string validateToken) {
return RequestLogInSmsCodeAsync(mobilePhoneNumber, null, CancellationToken.None);
2019-07-19 15:01:34 +08:00
}
/// <summary>
/// Requests the login SMS code asynchronous.
/// </summary>
/// <param name="mobilePhoneNumber">The mobile phone number.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
public static Task<bool> RequestLogInSmsCodeAsync(string mobilePhoneNumber, CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
return RequestLogInSmsCodeAsync(mobilePhoneNumber, null, cancellationToken);
}
/// <summary>
/// Requests the login SMS code asynchronous.
/// </summary>
/// <param name="mobilePhoneNumber">The mobile phone number.</param>
/// <param name="validateToken">Validate token.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
public static Task<bool> RequestLogInSmsCodeAsync(string mobilePhoneNumber, string validateToken, CancellationToken cancellationToken) {
Dictionary<string, object> strs = new Dictionary<string, object> {
2019-07-19 15:01:34 +08:00
{ "mobilePhoneNumber", mobilePhoneNumber },
};
if (string.IsNullOrEmpty(validateToken)) {
2019-07-19 15:01:34 +08:00
strs.Add("validate_token", validateToken);
}
var command = new AVCommand("requestLoginSmsCode", "POST", CurrentSessionToken, data: strs);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => {
2019-07-19 15:01:34 +08:00
return AVClient.IsSuccessStatusCode(t.Result.Item1);
});
}
/// <summary>
/// 手机号一键登录
/// </summary>
/// <param name="mobilePhoneNumber">手机号</param>
/// <param name="smsCode">短信验证码</param>
/// <returns></returns>
public static Task<AVUser> SignUpOrLogInByMobilePhoneAsync(string mobilePhoneNumber, string smsCode, CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
Dictionary<string, object> strs = new Dictionary<string, object>()
{
{ "mobilePhoneNumber", mobilePhoneNumber },
{ "smsCode", smsCode }
};
return UserController.LogInWithParametersAsync("usersByMobilePhone", strs, cancellationToken).OnSuccess(t => {
var user = CreateWithoutData<AVUser>(null);
2019-07-19 15:01:34 +08:00
user.HandleFetchResult(t.Result);
return SaveCurrentUserAsync(user).OnSuccess(_ => user);
}).Unwrap();
}
/// <summary>
/// 手机号一键登录
/// </summary>
/// <returns>signup or login by mobile phone async.</returns>
/// <param name="mobilePhoneNumber">手机号</param>
/// <param name="smsCode">短信验证码</param>
public static Task<AVUser> SignUpOrLogInByMobilePhoneAsync(string mobilePhoneNumber, string smsCode) {
return SignUpOrLogInByMobilePhoneAsync(mobilePhoneNumber, smsCode, CancellationToken.None);
2019-07-19 15:01:34 +08:00
}
#region mobile sms shortcode sign up & log in.
/// <summary>
/// Send sign up sms code async.
/// </summary>
/// <returns>The sign up sms code async.</returns>
/// <param name="mobilePhoneNumber">Mobile phone number.</param>
public static Task SendSignUpSmsCodeAsync(string mobilePhoneNumber) {
2019-07-19 15:01:34 +08:00
return AVCloud.RequestSMSCodeAsync(mobilePhoneNumber);
}
/// <summary>
/// Sign up by mobile phone async.
/// </summary>
/// <returns>The up by mobile phone async.</returns>
/// <param name="mobilePhoneNumber">Mobile phone number.</param>
/// <param name="smsCode">Sms code.</param>
public static Task<AVUser> SignUpByMobilePhoneAsync(string mobilePhoneNumber, string smsCode) {
return SignUpOrLogInByMobilePhoneAsync(mobilePhoneNumber, smsCode);
2019-07-19 15:01:34 +08:00
}
/// <summary>
/// Send log in sms code async.
/// </summary>
/// <returns>The log in sms code async.</returns>
/// <param name="mobilePhoneNumber">Mobile phone number.</param>
public static Task SendLogInSmsCodeAsync(string mobilePhoneNumber) {
return RequestLogInSmsCodeAsync(mobilePhoneNumber);
2019-07-19 15:01:34 +08:00
}
/// <summary>
/// Log in by mobile phone async.
/// </summary>
/// <returns>The in by mobile phone async.</returns>
/// <param name="mobilePhoneNumber">Mobile phone number.</param>
/// <param name="smsCode">Sms code.</param>
public static Task<AVUser> LogInByMobilePhoneAsync(string mobilePhoneNumber, string smsCode) {
return LogInBySmsCodeAsync(mobilePhoneNumber, smsCode);
2019-07-19 15:01:34 +08:00
}
#endregion
#endregion
#region 重置密码
/// <summary>
/// 请求重置密码,需要传入注册时使用的手机号。
/// </summary>
/// <param name="mobilePhoneNumber">注册时使用的手机号</param>
/// <returns></returns>
public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber) {
return RequestPasswordResetBySmsCode(mobilePhoneNumber, null, CancellationToken.None);
2019-07-19 15:01:34 +08:00
}
/// <summary>
/// 请求重置密码,需要传入注册时使用的手机号。
/// </summary>
/// <param name="mobilePhoneNumber">注册时使用的手机号</param>
/// <param name="cancellationToken">cancellationToken</param>
/// <returns></returns>
public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber, CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
return RequestPasswordResetBySmsCode(mobilePhoneNumber, null, cancellationToken);
}
/// <summary>
/// 请求重置密码,需要传入注册时使用的手机号。
/// </summary>
/// <param name="mobilePhoneNumber">注册时使用的手机号</param>
/// <param name="validateToken">Validate token.</param>
/// <returns></returns>
public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber, string validateToken) {
return RequestPasswordResetBySmsCode(mobilePhoneNumber, validateToken, CancellationToken.None);
2019-07-19 15:01:34 +08:00
}
/// <summary>
/// 请求重置密码,需要传入注册时使用的手机号。
/// </summary>
/// <param name="mobilePhoneNumber">注册时使用的手机号</param>
/// <param name="validateToken">Validate token.</param>
/// <param name="cancellationToken">cancellationToken</param>
/// <returns></returns>
public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber, string validateToken, CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
string currentSessionToken = AVUser.CurrentSessionToken;
Dictionary<string, object> strs = new Dictionary<string, object>()
{
{ "mobilePhoneNumber", mobilePhoneNumber },
};
if (string.IsNullOrEmpty(validateToken)) {
2019-07-19 15:01:34 +08:00
strs.Add("validate_token", validateToken);
}
var command = new AVCommand("requestPasswordResetBySmsCode", "POST", currentSessionToken, data: strs);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => {
2019-07-19 15:01:34 +08:00
return AVClient.IsSuccessStatusCode(t.Result.Item1);
});
}
/// <summary>
/// 通过验证码重置密码。
/// </summary>
/// <param name="newPassword">新密码</param>
/// <param name="smsCode">6位数验证码</param>
/// <returns></returns>
public static Task<bool> ResetPasswordBySmsCodeAsync(string newPassword, string smsCode) {
2019-07-19 15:01:34 +08:00
return AVUser.ResetPasswordBySmsCodeAsync(newPassword, smsCode, CancellationToken.None);
}
/// <summary>
/// 通过验证码重置密码。
/// </summary>
/// <param name="newPassword">新密码</param>
/// <param name="smsCode">6位数验证码</param>
/// <param name="cancellationToken">cancellationToken</param>
/// <returns></returns>
public static Task<bool> ResetPasswordBySmsCodeAsync(string newPassword, string smsCode, CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
string currentSessionToken = AVUser.CurrentSessionToken;
Dictionary<string, object> strs = new Dictionary<string, object>()
{
{ "password", newPassword }
};
var command = new AVCommand("resetPasswordBySmsCode/" + smsCode, "PUT", currentSessionToken, data: strs);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => {
2019-07-19 15:01:34 +08:00
return AVClient.IsSuccessStatusCode(t.Result.Item1);
});
}
/// <summary>
/// 发送认证码到需要认证的手机上
/// </summary>
/// <param name="mobilePhoneNumber">手机号</param>
/// <returns></returns>
public static Task<bool> RequestMobilePhoneVerifyAsync(string mobilePhoneNumber) {
return RequestMobilePhoneVerifyAsync(mobilePhoneNumber, null, CancellationToken.None);
2019-07-19 15:01:34 +08:00
}
/// <summary>
/// 发送认证码到需要认证的手机上
/// </summary>
/// <param name="mobilePhoneNumber">手机号</param>
/// <param name="validateToken">Validate token.</param>
/// <returns></returns>
public static Task<bool> RequestMobilePhoneVerifyAsync(string mobilePhoneNumber, string validateToken) {
return RequestMobilePhoneVerifyAsync(mobilePhoneNumber, validateToken, CancellationToken.None);
2019-07-19 15:01:34 +08:00
}
/// <summary>
/// 发送认证码到需要认证的手机上
/// </summary>
/// <param name="mobilePhoneNumber">手机号</param>
/// <param name="cancellationToken">CancellationToken</param>
/// <returns></returns>
public static Task<bool> RequestMobilePhoneVerifyAsync(string mobilePhoneNumber, CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
return RequestMobilePhoneVerifyAsync(mobilePhoneNumber, null, cancellationToken);
}
/// <summary>
/// 发送认证码到需要认证的手机上
/// </summary>
/// <param name="mobilePhoneNumber">手机号</param>
/// <param name="validateToken">Validate token.</param>
/// <param name="cancellationToken">CancellationToken</param>
/// <returns></returns>
public static Task<bool> RequestMobilePhoneVerifyAsync(string mobilePhoneNumber, string validateToken, CancellationToken cancellationToken) {
string currentSessionToken = CurrentSessionToken;
2019-07-19 15:01:34 +08:00
Dictionary<string, object> strs = new Dictionary<string, object>()
{
{ "mobilePhoneNumber", mobilePhoneNumber }
};
if (string.IsNullOrEmpty(validateToken)) {
2019-07-19 15:01:34 +08:00
strs.Add("validate_token", validateToken);
}
var command = new AVCommand("requestMobilePhoneVerify", "POST", currentSessionToken, data: strs);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => {
2019-07-19 15:01:34 +08:00
return AVClient.IsSuccessStatusCode(t.Result.Item1);
});
}
/// <summary>
/// 验证手机验证码是否为有效值
/// </summary>
/// <param name="code">手机收到的验证码</param>
/// <param name="mobilePhoneNumber">手机号,可选</param>
/// <returns></returns>
public static Task<bool> VerifyMobilePhoneAsync(string code, string mobilePhoneNumber) {
2019-07-19 15:01:34 +08:00
var command = new AVCommand("verifyMobilePhone/" + code.Trim() + "?mobilePhoneNumber=" + mobilePhoneNumber.Trim(),
"POST",
null,
2019-07-19 15:01:34 +08:00
data: null);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => {
2019-07-19 15:01:34 +08:00
return AVClient.IsSuccessStatusCode(t.Result.Item1);
});
}
/// <summary>
/// 验证手机验证码是否为有效值
/// </summary>
/// <param name="code">手机收到的验证码</param>
/// <returns></returns>
public static Task<bool> VerifyMobilePhoneAsync(string code) {
2019-07-19 15:01:34 +08:00
var command = new AVCommand("verifyMobilePhone/" + code.Trim(),
"POST",
null,
2019-07-19 15:01:34 +08:00
data: null);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => {
2019-07-19 15:01:34 +08:00
return AVClient.IsSuccessStatusCode(t.Result.Item1);
});
}
/// <summary>
/// 验证手机验证码是否为有效值
/// </summary>
/// <param name="code">手机收到的验证码</param>
/// <param name="cancellationToken">cancellationToken</param>
/// <returns></returns>
public static Task<bool> VerifyMobilePhoneAsync(string code, CancellationToken cancellationToken) {
return VerifyMobilePhoneAsync(code, CancellationToken.None);
2019-07-19 15:01:34 +08:00
}
#endregion
#region 邮箱验证
/// <summary>
/// 申请发送验证邮箱的邮件,一周之内有效
/// 如果该邮箱已经验证通过,会直接返回 True并不会真正发送邮件
/// 注意,不能频繁的调用此接口,一天之内只允许向同一个邮箱发送验证邮件 3 次,超过调用次数,会直接返回错误
/// </summary>
/// <param name="email">邮箱地址</param>
/// <returns></returns>
public static Task<bool> RequestEmailVerifyAsync(string email) {
Dictionary<string, object> strs = new Dictionary<string, object> {
2019-07-19 15:01:34 +08:00
{ "email", email }
};
var command = new AVCommand("requestEmailVerify",
"POST",
null,
2019-07-19 15:01:34 +08:00
data: strs);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => {
2019-07-19 15:01:34 +08:00
return AVClient.IsSuccessStatusCode(t.Result.Item1);
});
}
#endregion
#region in no-local-storage enviroment
internal Task Create() {
return Create(CancellationToken.None);
2019-07-19 15:01:34 +08:00
}
internal Task Create(CancellationToken cancellationToken) {
2019-07-19 15:01:34 +08:00
return taskQueue.Enqueue(toAwait => Create(toAwait, cancellationToken),
cancellationToken);
}
internal Task Create(Task toAwait, CancellationToken cancellationToken) {
if (AuthData == null) {
2019-07-19 15:01:34 +08:00
// TODO (hallucinogen): make an Extension of Task to create Task with exception/canceled.
if (string.IsNullOrEmpty(Username)) {
2019-07-19 15:01:34 +08:00
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
tcs.TrySetException(new InvalidOperationException("Cannot sign up user with an empty name."));
return tcs.Task;
}
if (string.IsNullOrEmpty(Password)) {
2019-07-19 15:01:34 +08:00
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
tcs.TrySetException(new InvalidOperationException("Cannot sign up user with an empty password."));
return tcs.Task;
}
}
if (!string.IsNullOrEmpty(ObjectId)) {
2019-07-19 15:01:34 +08:00
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
tcs.TrySetException(new InvalidOperationException("Cannot sign up a user that already exists."));
return tcs.Task;
}
IDictionary<string, IAVFieldOperation> currentOperations = StartSave();
return toAwait.OnSuccess(_ => {
2019-07-19 15:01:34 +08:00
return UserController.SignUpAsync(State, currentOperations, cancellationToken);
}).Unwrap().ContinueWith(t => {
if (t.IsFaulted || t.IsCanceled) {
2019-07-19 15:01:34 +08:00
HandleFailedSave(currentOperations);
} else {
2019-07-19 15:01:34 +08:00
var serverState = t.Result;
HandleSave(serverState);
}
return t;
}).Unwrap();
}
#endregion
#region task session token for http request
internal static Task<string> TakeSessionToken(string sesstionToken = null) {
2019-07-19 15:01:34 +08:00
var sessionTokenTask = Task.FromResult(sesstionToken);
if (sesstionToken == null)
sessionTokenTask = AVUser.GetCurrentAsync().OnSuccess(u => {
2019-07-19 15:01:34 +08:00
if (u.Result != null)
return u.Result.SessionToken;
return null;
});
return sessionTokenTask;
}
#endregion
#region AVUser Extension
public IDictionary<string, IDictionary<string, object>> GetAuthData() {
return AuthData;
}
/// <summary>
/// use 3rd auth data to sign up or log in.if user with the same auth data exits,it will transfer as log in.
/// </summary>
/// <param name="data">OAuth data, like {"accessToken":"xxxxxx"}</param>
/// <param name="platform">auth platform,maybe "facebook"/"twiiter"/"weibo"/"weixin" .etc</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static Task<AVUser> LogInWithAuthDataAsync(IDictionary<string, object> data,
string platform,
AVUserAuthDataLogInOption options = null,
CancellationToken cancellationToken = default) {
2019-07-19 15:01:34 +08:00
if (options == null) {
options = new AVUserAuthDataLogInOption();
}
return LogInWithAsync(platform, data, options.FailOnNotExist, cancellationToken);
2019-07-19 15:01:34 +08:00
}
public static Task<AVUser> LogInWithAuthDataAndUnionIdAsync(
IDictionary<string, object> authData,
string platform,
string unionId,
AVUserAuthDataLogInOption options = null,
CancellationToken cancellationToken = default) {
2019-07-19 15:01:34 +08:00
if (options == null) {
options = new AVUserAuthDataLogInOption();
}
MergeAuthData(authData, unionId, options);
return LogInWithAsync(platform, authData, options.FailOnNotExist, cancellationToken);
2019-07-19 15:01:34 +08:00
}
public static Task<AVUser> LogInAnonymouslyAsync(CancellationToken cancellationToken = default) {
2019-07-19 15:01:34 +08:00
var data = new Dictionary<string, object> {
{ "id", Guid.NewGuid().ToString() }
};
var options = new AVUserAuthDataLogInOption();
return LogInWithAuthDataAsync(data, "anonymous", options, cancellationToken);
}
[Obsolete("please use LogInWithAuthDataAsync instead.")]
public static Task<AVUser> LogInWithAsync(string authType, IDictionary<string, object> data, CancellationToken cancellationToken) {
return LogInWithAsync(authType, data, false, cancellationToken);
2019-07-19 15:01:34 +08:00
}
/// <summary>
/// link a 3rd auth account to the user.
/// </summary>
/// <param name="data">OAuth data, like {"accessToken":"xxxxxx"}</param>
/// <param name="platform">auth platform,maybe "facebook"/"twiiter"/"weibo"/"weixin" .etc</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task AssociateAuthDataAsync(IDictionary<string, object> data, string platform, CancellationToken cancellationToken = default) {
2019-07-19 15:01:34 +08:00
return LinkWithAsync(platform, data, cancellationToken);
}
public Task AssociateAuthDataAndUnionIdAsync(
IDictionary<string, object> authData,
string platform,
string unionId,
AVUserAuthDataLogInOption options = null,
CancellationToken cancellationToken = default) {
2019-07-19 15:01:34 +08:00
if (options == null) {
options = new AVUserAuthDataLogInOption();
}
MergeAuthData(authData, unionId, options);
return LinkWithAsync(platform, authData, cancellationToken);
}
/// <summary>
/// unlink a 3rd auth account from the user.
/// </summary>
/// <param name="platform">auth platform,maybe "facebook"/"twiiter"/"weibo"/"weixin" .etc</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task DisassociateWithAuthDataAsync(string platform, CancellationToken cancellationToken = default) {
2019-07-19 15:01:34 +08:00
return UnlinkFromAsync(platform, cancellationToken);
}
/// 合并为支持 AuthData 的格式
static void MergeAuthData(IDictionary<string, object> authData, string unionId, AVUserAuthDataLogInOption options) {
authData["platform"] = options.UnionIdPlatform;
authData["main_account"] = options.AsMainAccount;
authData["unionid"] = unionId;
}
#endregion
}
}