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;
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
namespace LeanCloud {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Represents a user for a LeanCloud application.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[AVClassName("_User")]
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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"
|
|
|
|
|
};
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
internal static IAVUserController UserController {
|
|
|
|
|
get {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return AVPlugins.Instance.UserController;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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.")]
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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);
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
protected override bool IsKeyMutable(string key) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return !readOnlyKeys.Contains(key);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
internal override void HandleSave(IObjectState serverState) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
base.HandleSave(serverState);
|
|
|
|
|
|
|
|
|
|
SynchronizeAllAuthData();
|
|
|
|
|
CleanupAuthData();
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
MutateState(mutableClone => {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
mutableClone.ServerData.Remove("password");
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// authenticated token.
|
|
|
|
|
/// </summary>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
public string SessionToken {
|
|
|
|
|
get {
|
|
|
|
|
if (State.ContainsKey("sessionToken")) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return State["sessionToken"] as string;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
internal static string CurrentSessionToken {
|
|
|
|
|
get {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
Task<string> sessionTokenTask = GetCurrentSessionTokenAsync();
|
|
|
|
|
sessionTokenTask.Wait();
|
|
|
|
|
return sessionTokenTask.Result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
internal static Task<string> GetCurrentSessionTokenAsync(CancellationToken cancellationToken = default) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return CurrentUserController.GetCurrentSessionTokenAsync(cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
internal Task SetSessionTokenAsync(string newSessionToken) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return SetSessionTokenAsync(newSessionToken, CancellationToken.None);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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")]
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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")]
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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")]
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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")]
|
2019-08-28 17:00:03 +08:00
|
|
|
|
public string MobilePhoneNumber {
|
|
|
|
|
get {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return GetProperty<string>(null, "MobilePhoneNumber");
|
|
|
|
|
}
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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")]
|
2019-08-28 17:00:03 +08:00
|
|
|
|
public bool MobilePhoneVerified {
|
|
|
|
|
get {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return GetProperty<bool>(false, "MobilePhoneVerified");
|
|
|
|
|
}
|
2019-08-28 17:00:03 +08:00
|
|
|
|
set {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
SetProperty<bool>(value, "MobilePhoneVerified");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 判断用户是否为匿名用户
|
|
|
|
|
/// </summary>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
public bool IsAnonymous {
|
|
|
|
|
get {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
bool rtn = false;
|
2019-08-28 17:00:03 +08:00
|
|
|
|
if (this.AuthData != null) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
rtn = this.AuthData.Keys.Contains("anonymous");
|
|
|
|
|
}
|
|
|
|
|
return rtn;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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);
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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);
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
public Task<IEnumerable<AVUser>> GetFollowersAsync() {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return this.GetFollowerQuery().FindAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 获取当前用户所关注的用户
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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,
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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.");
|
|
|
|
|
}
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
public static Task LogOutAsync(CancellationToken cancellationToken) {
|
|
|
|
|
return GetCurrentUserAsync().OnSuccess(t => {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
LogOutWithProviders();
|
|
|
|
|
|
|
|
|
|
AVUser user = t.Result;
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
internal Task LogOutAsync(Task toAwait, CancellationToken cancellationToken) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
string oldSessionToken = SessionToken;
|
2019-08-28 17:00:03 +08:00
|
|
|
|
if (oldSessionToken == null) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return Task.FromResult(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cleanup in-memory session.
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
internal static Task<AVUser> GetCurrentUserAsync(CancellationToken cancellationToken) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return CurrentUserController.GetAsync(cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
private static Task SaveCurrentUserAsync(AVUser user) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return SaveCurrentUserAsync(user, CancellationToken.None);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
private static Task SaveCurrentUserAsync(AVUser user, CancellationToken cancellationToken) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return CurrentUserController.SetAsync(user, cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
internal static void ClearInMemoryUser() {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
CurrentUserController.ClearFromMemory();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Constructs a <see cref="AVQuery{AVUser}"/> for AVUsers.
|
|
|
|
|
/// </summary>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
public static Task EnableRevocableSessionAsync(CancellationToken cancellationToken) {
|
|
|
|
|
lock (isRevocableSessionEnabledMutex) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
isRevocableSessionEnabled = true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
return GetCurrentUserAsync(cancellationToken).OnSuccess(t => {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
var user = t.Result;
|
|
|
|
|
return user.UpgradeToRevocableSessionAsync(cancellationToken);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
internal static void DisableRevocableSession() {
|
|
|
|
|
lock (isRevocableSessionEnabledMutex) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
isRevocableSessionEnabled = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
internal static bool IsRevocableSessionEnabled {
|
|
|
|
|
get {
|
|
|
|
|
lock (isRevocableSessionEnabledMutex) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return isRevocableSessionEnabled;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
internal Task UpgradeToRevocableSessionAsync() {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return UpgradeToRevocableSessionAsync(CancellationToken.None);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
public Task UpgradeToRevocableSessionAsync(CancellationToken cancellationToken) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return taskQueue.Enqueue(toAwait => UpgradeToRevocableSessionAsync(toAwait, cancellationToken),
|
|
|
|
|
cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
internal Task UpgradeToRevocableSessionAsync(Task toAwait, CancellationToken cancellationToken) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
string sessionToken = SessionToken;
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
return toAwait.OnSuccess(_ => {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return AVSession.UpgradeToRevocableSessionAsync(sessionToken, cancellationToken);
|
2019-08-28 17:00:03 +08:00
|
|
|
|
}).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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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,
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>>>(
|
2019-08-28 17:00:03 +08:00
|
|
|
|
"authData", out authData)) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return authData;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2019-08-28 17:00:03 +08:00
|
|
|
|
private set {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
this["authData"] = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
private static IAVAuthenticationProvider GetProvider(string providerName) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
IAVAuthenticationProvider provider;
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
private void CleanupAuthData() {
|
|
|
|
|
lock (mutex) {
|
|
|
|
|
if (!CurrentUserController.IsCurrent(this)) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
var authData = AuthData;
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
if (authData == null) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
private void SynchronizeAllAuthData() {
|
|
|
|
|
lock (mutex) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
var authData = AuthData;
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
if (authData == null) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
foreach (var pair in authData) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
SynchronizeAuthData(GetProvider(pair.Key));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
private void SynchronizeAuthData(IAVAuthenticationProvider provider) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
bool restorationSuccess = false;
|
2019-08-28 17:00:03 +08:00
|
|
|
|
lock (mutex) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
var authData = AuthData;
|
2019-08-28 17:00:03 +08:00
|
|
|
|
if (authData == null || provider == null) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
IDictionary<string, object> data;
|
2019-08-28 17:00:03 +08:00
|
|
|
|
if (authData.TryGetValue(provider.AuthType, out data)) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
restorationSuccess = provider.RestoreAuthentication(data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
if (!restorationSuccess) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
this.UnlinkFromAsync(provider.AuthType, CancellationToken.None);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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,
|
2019-08-28 17:00:03 +08:00
|
|
|
|
CancellationToken cancellationToken) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
AVUser user = null;
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
return UserController.LogInAsync(authType, data, failOnNotExist, cancellationToken).OnSuccess(t => {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
user = AVObject.FromState<AVUser>(t.Result, "_User");
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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,
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
internal static void RegisterProvider(IAVAuthenticationProvider provider) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
authProviders[provider.AuthType] = provider;
|
2019-08-28 17:00:03 +08:00
|
|
|
|
if (CurrentUser != null) {
|
|
|
|
|
CurrentUser.SynchronizeAuthData(provider);
|
2019-07-19 15:01:34 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region 手机号登录
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
internal static Task<AVUser> LogInWithParametersAsync(Dictionary<string, object> strs, CancellationToken cancellationToken) {
|
|
|
|
|
AVUser avUser = CreateWithoutData<AVUser>(null);
|
2019-07-19 15:01:34 +08:00
|
|
|
|
|
2019-08-28 17:00:03 +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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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 => {
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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 }
|
|
|
|
|
};
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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 }
|
|
|
|
|
};
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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 },
|
|
|
|
|
};
|
2019-08-28 17:00:03 +08:00
|
|
|
|
if (string.IsNullOrEmpty(validateToken)) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
strs.Add("validate_token", validateToken);
|
|
|
|
|
}
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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 }
|
|
|
|
|
};
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
public static Task<AVUser> LogInByMobilePhoneAsync(string mobilePhoneNumber, string smsCode) {
|
2019-08-28 16:32:13 +08:00
|
|
|
|
return LogInBySmsCodeAsync(mobilePhoneNumber, smsCode);
|
2019-07-19 15:01:34 +08:00
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region 重置密码
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 请求重置密码,需要传入注册时使用的手机号。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="mobilePhoneNumber">注册时使用的手机号</param>
|
|
|
|
|
/// <returns></returns>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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 },
|
|
|
|
|
};
|
2019-08-28 17:00:03 +08:00
|
|
|
|
if (string.IsNullOrEmpty(validateToken)) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
strs.Add("validate_token", validateToken);
|
|
|
|
|
}
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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 }
|
|
|
|
|
};
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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 }
|
|
|
|
|
};
|
2019-08-28 17:00:03 +08:00
|
|
|
|
if (string.IsNullOrEmpty(validateToken)) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
strs.Add("validate_token", validateToken);
|
|
|
|
|
}
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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(),
|
2019-08-28 17:00:03 +08:00
|
|
|
|
"POST",
|
|
|
|
|
null,
|
2019-07-19 15:01:34 +08:00
|
|
|
|
data: null);
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
public static Task<bool> VerifyMobilePhoneAsync(string code) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
var command = new AVCommand("verifyMobilePhone/" + code.Trim(),
|
2019-08-28 17:00:03 +08:00
|
|
|
|
"POST",
|
|
|
|
|
null,
|
2019-07-19 15:01:34 +08:00
|
|
|
|
data: null);
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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",
|
2019-08-28 17:00:03 +08:00
|
|
|
|
"POST",
|
|
|
|
|
null,
|
2019-07-19 15:01:34 +08:00
|
|
|
|
data: strs);
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
internal Task Create() {
|
|
|
|
|
return Create(CancellationToken.None);
|
2019-07-19 15:01:34 +08:00
|
|
|
|
}
|
2019-08-28 17:00:03 +08:00
|
|
|
|
|
|
|
|
|
internal Task Create(CancellationToken cancellationToken) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return taskQueue.Enqueue(toAwait => Create(toAwait, cancellationToken),
|
|
|
|
|
cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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.
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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;
|
|
|
|
|
}
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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();
|
|
|
|
|
|
2019-08-28 17:00:03 +08:00
|
|
|
|
return toAwait.OnSuccess(_ => {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
return UserController.SignUpAsync(State, currentOperations, cancellationToken);
|
2019-08-28 17:00:03 +08:00
|
|
|
|
}).Unwrap().ContinueWith(t => {
|
|
|
|
|
if (t.IsFaulted || t.IsCanceled) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
HandleFailedSave(currentOperations);
|
2019-08-28 17:00:03 +08:00
|
|
|
|
} 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
|
2019-08-28 17:00:03 +08:00
|
|
|
|
internal static Task<string> TakeSessionToken(string sesstionToken = null) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
var sessionTokenTask = Task.FromResult(sesstionToken);
|
|
|
|
|
if (sesstionToken == null)
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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();
|
|
|
|
|
}
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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,
|
2019-08-28 17:00:03 +08:00
|
|
|
|
CancellationToken cancellationToken = default) {
|
2019-07-19 15:01:34 +08:00
|
|
|
|
if (options == null) {
|
|
|
|
|
options = new AVUserAuthDataLogInOption();
|
|
|
|
|
}
|
|
|
|
|
MergeAuthData(authData, unionId, options);
|
2019-08-28 17:00:03 +08:00
|
|
|
|
return LogInWithAsync(platform, authData, options.FailOnNotExist, cancellationToken);
|
2019-07-19 15:01:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:00:03 +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) {
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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,
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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>
|
2019-08-28 17:00:03 +08:00
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|