using System; using System.Collections.Generic; using System.Linq; using System.Windows.Controls; using System.Windows.Navigation; using Microsoft.Phone.Controls; namespace VK { /// /// Авторицаия ВКонтакте /// class VKAuth { #region Fields //неверный ответ от сервера public delegate void ServerErrorCallback(); public event ServerErrorCallback ServerError; //успешный logout public delegate void LogoutCompletedCallback(); public event LogoutCompletedCallback LogoutCompleted; //успешный login public delegate void LoginCompletedCallback(string token, string userId); public event LoginCompletedCallback LoginCompleted; //тип текущего действия public enum VKAuthActionType { Login, Logout } private VKAuthActionType _actionType; //контейнер для WebBrowser private Grid _gridContainer; //маркер загрузки public bool IsLoading = false; //стандартный редирект при логине private const string RedirectUrl = "https://oauth.vk.com/blank.html"; private readonly WebBrowser _webBrowser; #endregion #region Constructor public VKAuth() { _webBrowser = new WebBrowser { IsScriptEnabled = true }; _webBrowser.NavigationFailed += WebBrowserNavigationFailed; } #endregion #region NavigationFailed private void WebBrowserNavigationFailed(object sender, NavigationFailedEventArgs e) { IsLoading = false; if (_actionType == VKAuthActionType.Login) { _webBrowser.Visibility = System.Windows.Visibility.Collapsed; if (ServerError != null) ServerError(); } else { if (LogoutCompleted != null) LogoutCompleted(); } } #endregion #region Login public void LoginAsync(Page page, string appId, string scope) { if (IsLoading) return; IsLoading = true; _actionType = VKAuthActionType.Login; _gridContainer = page.Content as Grid; var url = String.Format("https://api.vk.com/oauth/authorize?client_id={0}&scope={1}&display=touch&response_type=token", appId, scope); var loginUrl = new Uri(url, UriKind.Absolute); _webBrowser.Navigated += WebBrowserNavigated; _webBrowser.Navigate(loginUrl); } private void WebBrowserNavigated(object sender, NavigationEventArgs e) { var result = ParseNavigatedUrl(e.Uri.ToString()); if (result.Status != AuthorzationStatus.Unknown) { _webBrowser.Visibility = System.Windows.Visibility.Collapsed; if (result.Status == AuthorzationStatus.Success) { var token = result.Context.AccessToken; var userId = result.Context.CurrentUserId; if (LoginCompleted != null) LoginCompleted(token, userId); } else { if (ServerError != null) ServerError(); } IsLoading = false; } else { if (!_gridContainer.Children.Contains(_webBrowser)) { Grid.SetColumnSpan(_webBrowser, int.MaxValue); Grid.SetRowSpan(_webBrowser, int.MaxValue); _gridContainer.Children.Add(_webBrowser); } } } public void LoginCancel() { if (_actionType == VKAuthActionType.Logout) return; IsLoading = false; _webBrowser.Visibility = System.Windows.Visibility.Collapsed; } #endregion #region Logout public void LogoutAsync() { if (IsLoading) return; IsLoading = true; _actionType = VKAuthActionType.Logout; _webBrowser.LoadCompleted += WebBrowserLogouted; _webBrowser.Navigate(new Uri(@"https://m.vk.com/")); } private void WebBrowserLogouted(object sender, NavigationEventArgs e) { var page = _webBrowser.SaveToString(); if (page.Contains("https://login.vk.com/?act=logout_mobile")) { var hash = page.Substring(page.IndexOf(@"https://login.vk.com/?act=logout_mobile", StringComparison.Ordinal)); hash = hash.Substring(hash.IndexOf("hash=", StringComparison.Ordinal)); hash = hash.Substring(5, hash.IndexOf(@"&", StringComparison.Ordinal) - 5); var logout = @"https://login.vk.com/?act=logout_mobile&hash=" + hash + "&from_host=m.vk.com&from_protocol=http"; _webBrowser.LoadCompleted -= WebBrowserLogouted; _webBrowser.Navigated += WebBrowserLogoutNavigated; _webBrowser.Navigate(new Uri(logout)); } else { IsLoading = false; LogoutCompleted(); } } private void WebBrowserLogoutNavigated(object sender, NavigationEventArgs e) { IsLoading = false; LogoutCompleted(); } #endregion #region Helpers private static AuthorizationResult ParseNavigatedUrl(string url) { if (url.StartsWith(RedirectUrl) && url.Length > RedirectUrl.Length) { var tokenData = GetToken(url); var tokenArray = GetTokenArray(tokenData); var authorizationResult = GetAuthResult(tokenArray); return authorizationResult; } return new AuthorizationResult { Status = AuthorzationStatus.Unknown }; } private static string GetToken(string url) { return url.Substring(RedirectUrl.Length + 1); } private static Dictionary GetTokenArray(string tokenData) { var result = new Dictionary(); var keyValuePairs = tokenData.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries); var splitedKeyValuePairs = keyValuePairs.Select(i => i.Split(new[] { '=' }, StringSplitOptions.RemoveEmptyEntries)).ToList(); splitedKeyValuePairs.ForEach(j => result.Add(j[0], j[1])); return result; } private static AuthorizationResult GetAuthResult(Dictionary tokenArray) { var authorizationResult = new AuthorizationResult(); if (tokenArray.ContainsKey("error")) { authorizationResult.Status = AuthorzationStatus.Error; authorizationResult.Description = tokenArray["error"]; } else { authorizationResult.Status = AuthorzationStatus.Success; authorizationResult.Description = "success"; authorizationResult.Context = new AuthorizationContext { AccessToken = tokenArray["access_token"], CurrentUserId = tokenArray["user_id"], }; } return authorizationResult; } public enum AuthorzationStatus { Unknown, Success, Error } public class AuthorizationContext { public string CurrentUserId { get; set; } public string AccessToken { get; set; } public string ApplicationId { get; set; } } public class AuthorizationResult { public AuthorzationStatus Status { get; set; } public AuthorizationContext Context { get; set; } public string Description { get; set; } } #endregion } }