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

1311 lines
55 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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 {
/// <summary>
/// Represents a user for a LeanCloud application.
/// </summary>
[AVClassName("_User")]
public class AVUser : AVObject {
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 {
return AVPlugins.Instance.UserController;
}
}
internal static IAVCurrentUserController CurrentUserController {
get {
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) {
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) {
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 => {
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 => {
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") {
throw new ArgumentException("Cannot remove the username key.");
}
base.Remove(key);
}
protected override bool IsKeyMutable(string key) {
return !readOnlyKeys.Contains(key);
}
internal override void HandleSave(IObjectState serverState) {
base.HandleSave(serverState);
SynchronizeAllAuthData();
CleanupAuthData();
MutateState(mutableClone => {
mutableClone.ServerData.Remove("password");
});
}
/// <summary>
/// authenticated token.
/// </summary>
public string SessionToken {
get {
if (State.ContainsKey("sessionToken")) {
return State["sessionToken"] as string;
}
return null;
}
}
internal static string CurrentSessionToken {
get {
Task<string> sessionTokenTask = GetCurrentSessionTokenAsync();
sessionTokenTask.Wait();
return sessionTokenTask.Result;
}
}
internal static Task<string> GetCurrentSessionTokenAsync(CancellationToken cancellationToken = default) {
return CurrentUserController.GetCurrentSessionTokenAsync(cancellationToken);
}
internal Task SetSessionTokenAsync(string newSessionToken) {
return SetSessionTokenAsync(newSessionToken, CancellationToken.None);
}
internal Task SetSessionTokenAsync(string newSessionToken, CancellationToken cancellationToken) {
MutateState(mutableClone => {
mutableClone.ServerData["sessionToken"] = newSessionToken;
});
return SaveCurrentUserAsync(this);
}
/// <summary>
/// Gets or sets the username.
/// </summary>
[AVFieldName("username")]
public string Username {
get { return GetProperty<string>(null, "Username"); }
set { SetProperty(value, "Username"); }
}
/// <summary>
/// Sets the password.
/// </summary>
[AVFieldName("password")]
public string Password {
private get { return GetProperty<string>(null, "Password"); }
set { SetProperty(value, "Password"); }
}
/// <summary>
/// Sets the email address.
/// </summary>
[AVFieldName("email")]
public string Email {
get { return GetProperty<string>(null, "Email"); }
set { SetProperty(value, "Email"); }
}
/// <summary>
/// 用户手机号。
/// </summary>
[AVFieldName("mobilePhoneNumber")]
public string MobilePhoneNumber {
get {
return GetProperty<string>(null, "MobilePhoneNumber");
}
set {
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 {
return GetProperty<bool>(false, "MobilePhoneVerified");
}
set {
SetProperty<bool>(value, "MobilePhoneVerified");
}
}
/// <summary>
/// 判断用户是否为匿名用户
/// </summary>
public bool IsAnonymous {
get {
bool rtn = false;
if (this.AuthData != null) {
rtn = this.AuthData.Keys.Contains("anonymous");
}
return rtn;
}
}
internal Task SignUpAsync(Task toAwait, CancellationToken cancellationToken) {
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() {
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) {
return taskQueue.Enqueue(toAwait => SignUpAsync(toAwait, cancellationToken),
cancellationToken);
}
#region 事件流系统相关 API
/// <summary>
/// 关注某个用户
/// </summary>
/// <param name="userObjectId">被关注的用户</param>
/// <returns></returns>
public Task<bool> FollowAsync(string userObjectId) {
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) {
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 => {
return AVClient.IsSuccessStatusCode(t.Result.Item1);
});
}
/// <summary>
/// 取关某一个用户
/// </summary>
/// <param name="userObjectId"></param>
/// <returns></returns>
public Task<bool> UnfollowAsync(string userObjectId) {
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 => {
return AVClient.IsSuccessStatusCode(t.Result.Item1);
});
}
/// <summary>
/// 获取当前用户的关注者的查询
/// </summary>
/// <returns></returns>
public AVQuery<AVUser> GetFollowerQuery() {
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() {
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() {
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() {
return this.GetFollowerQuery().FindAsync();
}
/// <summary>
/// 获取当前用户所关注的用户
/// </summary>
/// <returns></returns>
public Task<IEnumerable<AVUser>> GetFolloweesAsync() {
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) {
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 => {
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) {
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 => {
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) {
throw new InvalidOperationException("You must call SignUpAsync before calling SaveAsync.");
}
return base.SaveAsync(toAwait, cancellationToken).OnSuccess(_ => {
if (!CurrentUserController.IsCurrent(this)) {
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();
}
/// <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() {
// 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() {
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 => {
LogOutWithProviders();
AVUser user = t.Result;
if (user == null) {
return Task.FromResult(0);
}
return user.taskQueue.Enqueue(toAwait => user.LogOutAsync(toAwait, cancellationToken), cancellationToken);
}).Unwrap();
}
internal Task LogOutAsync(Task toAwait, CancellationToken cancellationToken) {
string oldSessionToken = SessionToken;
if (oldSessionToken == null) {
return Task.FromResult(0);
}
// Cleanup in-memory session.
MutateState(mutableClone => {
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) {
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 {
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() {
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() {
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) {
return CurrentUserController.GetAsync(cancellationToken);
}
private static Task SaveCurrentUserAsync(AVUser user) {
return SaveCurrentUserAsync(user, CancellationToken.None);
}
private static Task SaveCurrentUserAsync(AVUser user, CancellationToken cancellationToken) {
return CurrentUserController.SetAsync(user, cancellationToken);
}
internal static void ClearInMemoryUser() {
CurrentUserController.ClearFromMemory();
}
/// <summary>
/// Constructs a <see cref="AVQuery{AVUser}"/> for AVUsers.
/// </summary>
public static AVQuery<AVUser> Query {
get {
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() {
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) {
isRevocableSessionEnabled = true;
}
return GetCurrentUserAsync(cancellationToken).OnSuccess(t => {
var user = t.Result;
return user.UpgradeToRevocableSessionAsync(cancellationToken);
});
}
internal static void DisableRevocableSession() {
lock (isRevocableSessionEnabledMutex) {
isRevocableSessionEnabled = false;
}
}
internal static bool IsRevocableSessionEnabled {
get {
lock (isRevocableSessionEnabledMutex) {
return isRevocableSessionEnabled;
}
}
}
internal Task UpgradeToRevocableSessionAsync() {
return UpgradeToRevocableSessionAsync(CancellationToken.None);
}
public Task UpgradeToRevocableSessionAsync(CancellationToken cancellationToken) {
return taskQueue.Enqueue(toAwait => UpgradeToRevocableSessionAsync(toAwait, cancellationToken),
cancellationToken);
}
internal Task UpgradeToRevocableSessionAsync(Task toAwait, CancellationToken cancellationToken) {
string sessionToken = SessionToken;
return toAwait.OnSuccess(_ => {
return AVSession.UpgradeToRevocableSessionAsync(sessionToken, cancellationToken);
}).Unwrap().OnSuccess(t => {
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) {
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) {
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) {
return UserController.UpdatePasswordAsync(ObjectId, SessionToken, oldPassword, newPassword, cancellationToken);
}
/// <summary>
/// Gets the authData for this user.
/// </summary>
internal IDictionary<string, IDictionary<string, object>> AuthData {
get {
IDictionary<string, IDictionary<string, object>> authData;
if (this.TryGetValue<IDictionary<string, IDictionary<string, object>>>(
"authData", out authData)) {
return authData;
}
return null;
}
private set {
this["authData"] = value;
}
}
private static IAVAuthenticationProvider GetProvider(string providerName) {
IAVAuthenticationProvider provider;
if (authProviders.TryGetValue(providerName, out provider)) {
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)) {
return;
}
var authData = AuthData;
if (authData == null) {
return;
}
foreach (var pair in new Dictionary<string, IDictionary<string, object>>(authData)) {
if (pair.Value == null) {
authData.Remove(pair.Key);
}
}
}
}
/// <summary>
/// Synchronizes authData for all providers.
/// </summary>
private void SynchronizeAllAuthData() {
lock (mutex) {
var authData = AuthData;
if (authData == null) {
return;
}
foreach (var pair in authData) {
SynchronizeAuthData(GetProvider(pair.Key));
}
}
}
private void SynchronizeAuthData(IAVAuthenticationProvider provider) {
bool restorationSuccess = false;
lock (mutex) {
var authData = AuthData;
if (authData == null || provider == null) {
return;
}
IDictionary<string, object> data;
if (authData.TryGetValue(provider.AuthType, out data)) {
restorationSuccess = provider.RestoreAuthentication(data);
}
}
if (!restorationSuccess) {
this.UnlinkFromAsync(provider.AuthType, CancellationToken.None);
}
}
public Task LinkWithAsync(string authType, IDictionary<string, object> data, CancellationToken cancellationToken) {
return taskQueue.Enqueue(toAwait => {
AuthData = new Dictionary<string, IDictionary<string, object>>();
AuthData[authType] = data;
return SaveAsync(cancellationToken);
}, cancellationToken);
}
public Task LinkWithAsync(string authType, CancellationToken cancellationToken) {
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) {
return LinkWithAsync(authType, null, cancellationToken);
}
/// <summary>
/// Checks whether a user is linked to a service.
/// </summary>
internal bool IsLinked(string authType) {
lock (mutex) {
return AuthData != null && AuthData.ContainsKey(authType) && AuthData[authType] != null;
}
}
internal static Task<AVUser> LogInWithAsync(string authType,
IDictionary<string, object> data,
bool failOnNotExist,
CancellationToken cancellationToken) {
AVUser user = null;
return UserController.LogInAsync(authType, data, failOnNotExist, cancellationToken).OnSuccess(t => {
user = AVObject.FromState<AVUser>(t.Result, "_User");
lock (user.mutex) {
if (user.AuthData == null) {
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) {
var provider = GetProvider(authType);
return provider.AuthenticateAsync(cancellationToken)
.OnSuccess(authData => LogInWithAsync(authType, authData.Result, false, cancellationToken))
.Unwrap();
}
internal static void RegisterProvider(IAVAuthenticationProvider provider) {
authProviders[provider.AuthType] = provider;
if (CurrentUser != null) {
CurrentUser.SynchronizeAuthData(provider);
}
}
#region 手机号登录
internal static Task<AVUser> LogInWithParametersAsync(Dictionary<string, object> strs, CancellationToken cancellationToken) {
AVUser avUser = CreateWithoutData<AVUser>(null);
return UserController.LogInWithParametersAsync("login", strs, cancellationToken).OnSuccess(t => {
var user = CreateWithoutData<AVUser>(null);
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);
}
/// <summary>
/// 用邮箱作和密码匹配登录
/// </summary>
/// <param name="email">邮箱</param>
/// <param name="password">密码</param>
/// <returns></returns>
public static Task<AVUser> LogInByEmailAsync(string email, string password, CancellationToken cancellationToken = default) {
return UserController.LogInAsync(null, email, password, cancellationToken).OnSuccess(t => {
AVUser user = FromState<AVUser>(t.Result, "_User");
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) {
Dictionary<string, object> strs = new Dictionary<string, object>()
{
{ "mobilePhoneNumber", mobilePhoneNumber },
{ "password", password }
};
return LogInWithParametersAsync(strs, cancellationToken);
}
/// <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) {
Dictionary<string, object> strs = new Dictionary<string, object>()
{
{ "mobilePhoneNumber", mobilePhoneNumber },
{ "smsCode", smsCode }
};
return LogInWithParametersAsync(strs, cancellationToken);
}
/// <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);
}
/// <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);
}
/// <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) {
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> {
{ "mobilePhoneNumber", mobilePhoneNumber },
};
if (string.IsNullOrEmpty(validateToken)) {
strs.Add("validate_token", validateToken);
}
var command = new AVCommand("requestLoginSmsCode", "POST", CurrentSessionToken, data: strs);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => {
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) {
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);
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);
}
#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) {
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);
}
/// <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);
}
/// <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);
}
#endregion
#endregion
#region 重置密码
/// <summary>
/// 请求重置密码,需要传入注册时使用的手机号。
/// </summary>
/// <param name="mobilePhoneNumber">注册时使用的手机号</param>
/// <returns></returns>
public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber) {
return RequestPasswordResetBySmsCode(mobilePhoneNumber, null, CancellationToken.None);
}
/// <summary>
/// 请求重置密码,需要传入注册时使用的手机号。
/// </summary>
/// <param name="mobilePhoneNumber">注册时使用的手机号</param>
/// <param name="cancellationToken">cancellationToken</param>
/// <returns></returns>
public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber, CancellationToken cancellationToken) {
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);
}
/// <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) {
string currentSessionToken = AVUser.CurrentSessionToken;
Dictionary<string, object> strs = new Dictionary<string, object>()
{
{ "mobilePhoneNumber", mobilePhoneNumber },
};
if (string.IsNullOrEmpty(validateToken)) {
strs.Add("validate_token", validateToken);
}
var command = new AVCommand("requestPasswordResetBySmsCode", "POST", currentSessionToken, data: strs);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => {
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) {
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) {
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 => {
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);
}
/// <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);
}
/// <summary>
/// 发送认证码到需要认证的手机上
/// </summary>
/// <param name="mobilePhoneNumber">手机号</param>
/// <param name="cancellationToken">CancellationToken</param>
/// <returns></returns>
public static Task<bool> RequestMobilePhoneVerifyAsync(string mobilePhoneNumber, CancellationToken cancellationToken) {
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;
Dictionary<string, object> strs = new Dictionary<string, object>()
{
{ "mobilePhoneNumber", mobilePhoneNumber }
};
if (string.IsNullOrEmpty(validateToken)) {
strs.Add("validate_token", validateToken);
}
var command = new AVCommand("requestMobilePhoneVerify", "POST", currentSessionToken, data: strs);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => {
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) {
var command = new AVCommand("verifyMobilePhone/" + code.Trim() + "?mobilePhoneNumber=" + mobilePhoneNumber.Trim(),
"POST",
null,
data: null);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => {
return AVClient.IsSuccessStatusCode(t.Result.Item1);
});
}
/// <summary>
/// 验证手机验证码是否为有效值
/// </summary>
/// <param name="code">手机收到的验证码</param>
/// <returns></returns>
public static Task<bool> VerifyMobilePhoneAsync(string code) {
var command = new AVCommand("verifyMobilePhone/" + code.Trim(),
"POST",
null,
data: null);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => {
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);
}
#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> {
{ "email", email }
};
var command = new AVCommand("requestEmailVerify",
"POST",
null,
data: strs);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => {
return AVClient.IsSuccessStatusCode(t.Result.Item1);
});
}
#endregion
#region in no-local-storage enviroment
internal Task Create() {
return Create(CancellationToken.None);
}
internal Task Create(CancellationToken cancellationToken) {
return taskQueue.Enqueue(toAwait => Create(toAwait, cancellationToken),
cancellationToken);
}
internal Task Create(Task toAwait, CancellationToken cancellationToken) {
if (AuthData == null) {
// TODO (hallucinogen): make an Extension of Task to create Task with exception/canceled.
if (string.IsNullOrEmpty(Username)) {
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)) {
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)) {
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(_ => {
return UserController.SignUpAsync(State, currentOperations, cancellationToken);
}).Unwrap().ContinueWith(t => {
if (t.IsFaulted || t.IsCanceled) {
HandleFailedSave(currentOperations);
} else {
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) {
var sessionTokenTask = Task.FromResult(sesstionToken);
if (sesstionToken == null)
sessionTokenTask = AVUser.GetCurrentAsync().OnSuccess(u => {
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) {
if (options == null) {
options = new AVUserAuthDataLogInOption();
}
return LogInWithAsync(platform, data, options.FailOnNotExist, cancellationToken);
}
public static Task<AVUser> LogInWithAuthDataAndUnionIdAsync(
IDictionary<string, object> authData,
string platform,
string unionId,
AVUserAuthDataLogInOption options = null,
CancellationToken cancellationToken = default) {
if (options == null) {
options = new AVUserAuthDataLogInOption();
}
MergeAuthData(authData, unionId, options);
return LogInWithAsync(platform, authData, options.FailOnNotExist, cancellationToken);
}
public static Task<AVUser> LogInAnonymouslyAsync(CancellationToken cancellationToken = default) {
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);
}
/// <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) {
return LinkWithAsync(platform, data, cancellationToken);
}
public Task AssociateAuthDataAndUnionIdAsync(
IDictionary<string, object> authData,
string platform,
string unionId,
AVUserAuthDataLogInOption options = null,
CancellationToken cancellationToken = default) {
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) {
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
}
}