private void LoadSession(out string authCode, out LiveAuthException error) { authCode = null; error = null; // only load session once. if (this.loginStatus == null) { if (this.webContext != null) { // Reads current login status from session cookie. this.loginStatus = HttpContextUtility.GetUserLoginStatus(this.webContext); HttpContextUtility.ReadAuthCodeRequest( webContext, out authCode, out this.appRequestState, out this.appRequestTs, out error); if (this.loginStatus.Status == LiveConnectSessionStatus.Unknown && error != null && error.ErrorCode == AuthErrorCodes.AccessDenied) { this.loginStatus = new LiveLoginResult(LiveConnectSessionStatus.NotConnected, null); } } else { this.loginStatus = new LiveLoginResult(LiveConnectSessionStatus.Unknown, null); } this.publicAuthClient.Session = this.loginStatus.Session; } }
private void OnAuthFlowEnded(string resultData) { if (this.rootVisual != null) { this.rootVisual.Navigating -= OnRootNavigating; } if (this.rootPage != null) { this.rootPage.BackKeyPress -= OnBackKeyPress; } if (this.callback != null) { if (!string.IsNullOrEmpty(resultData)) { this.callback(resultData, null); } else { var error = new LiveAuthException( AuthErrorCodes.AccessDenied, ResourceHelper.GetString("ConsentNotGranted")); this.callback(null, error); } } }
/// <summary> /// Reads authorization code from current session. /// </summary> public static bool ReadAuthCodeRequest( HttpContextBase webContext, out string code, out string state, out string requestTs, out LiveAuthException error) { Debug.Assert(webContext != null); NameValueCollection queryString = webContext.Request.QueryString; code = queryString[AuthConstants.Code]; bool isCodeRequest = !string.IsNullOrEmpty(code); string clientState = queryString[AuthConstants.ClientState]; IDictionary <string, string> states = LiveAuthUtility.DecodeAppRequestStates(clientState); state = states.ContainsKey(AuthConstants.AppState) ? states[AuthConstants.AppState] : null; requestTs = states.ContainsKey(AuthConstants.ClientRequestTs) ? states[AuthConstants.ClientRequestTs] : null; string errorCode = queryString[AuthConstants.Error]; string errorDescription = queryString[AuthConstants.ErrorDescription]; error = string.IsNullOrEmpty(errorCode) ? null : new LiveAuthException(errorCode, errorDescription, state); return(isCodeRequest); }
/// <summary> /// Reads authorization code from current session. /// </summary> public static bool ReadAuthCodeRequest( HttpContextBase webContext, out string code, out string state, out string requestTs, out LiveAuthException error) { Debug.Assert(webContext != null); NameValueCollection queryString = webContext.Request.QueryString; code = queryString[AuthConstants.Code]; bool isCodeRequest = !string.IsNullOrEmpty(code); string clientState = queryString[AuthConstants.ClientState]; IDictionary<string, string> states = LiveAuthUtility.DecodeAppRequestStates(clientState); state = states.ContainsKey(AuthConstants.AppState) ? states[AuthConstants.AppState] : null; requestTs = states.ContainsKey(AuthConstants.ClientRequestTs) ? states[AuthConstants.ClientRequestTs] : null; string errorCode = queryString[AuthConstants.Error]; string errorDescription = queryString[AuthConstants.ErrorDescription]; error = string.IsNullOrEmpty(errorCode) ? null : new LiveAuthException(errorCode, errorDescription, state); return isCodeRequest; }
/// <summary> /// Decrypt an authentication token and parse into a JsonWebToken object. /// </summary> public static bool ReadUserIdFromAuthenticationToken( string authenticationToken, object clientSecrets, out string userId, out LiveAuthException error) { userId = null; JsonWebToken jwt; bool succeeded = false; if (DecodeAuthenticationToken(authenticationToken, clientSecrets, out jwt, out error)) { userId = jwt.Claims.UserId; if (jwt.IsExpired) { error = new LiveAuthException(AuthErrorCodes.SessionExpired, ErrorText.SessionExpired); } else { succeeded = true; } } return succeeded; }
/// <summary> /// Decrypt an authentication token and parse into a JsonWebToken object. /// </summary> public static bool ReadUserIdFromAuthenticationToken( string authenticationToken, object clientSecrets, out string userId, out LiveAuthException error) { userId = null; JsonWebToken jwt; bool succeeded = false; if (DecodeAuthenticationToken(authenticationToken, clientSecrets, out jwt, out error)) { userId = jwt.Claims.UserId; if (jwt.IsExpired) { error = new LiveAuthException(AuthErrorCodes.SessionExpired, ErrorText.SessionExpired); } else { succeeded = true; } } return(succeeded); }
/// <summary> /// Decrypt an authentication token and parse into a JsonWebToken object. /// </summary> public static bool DecodeAuthenticationToken( string authenticationToken, string clientSecret, out JsonWebToken token, out LiveAuthException error) { Debug.Assert(!string.IsNullOrEmpty(authenticationToken)); Debug.Assert(!string.IsNullOrEmpty(clientSecret)); token = null; error = null; Dictionary<int, string> keys = new Dictionary<int, string>(); keys.Add(0, clientSecret); try { token = new JsonWebToken(authenticationToken, keys); return true; } catch (Exception ex) { error = new LiveAuthException(AuthErrorCodes.ClientError, ex.Message); return false; } }
/// <summary> /// Decrypt an authentication token and parse into a JsonWebToken object. /// </summary> public static bool DecodeAuthenticationToken( string authenticationToken, string clientSecret, out JsonWebToken token, out LiveAuthException error) { Debug.Assert(!string.IsNullOrEmpty(authenticationToken)); Debug.Assert(!string.IsNullOrEmpty(clientSecret)); token = null; error = null; Dictionary <int, string> keys = new Dictionary <int, string>(); keys.Add(0, clientSecret); try { token = new JsonWebToken(authenticationToken, keys); return(true); } catch (Exception ex) { error = new LiveAuthException(AuthErrorCodes.ClientError, ex.Message); return(false); } }
/// <summary> /// Decrypts the user ID from a given authentication token. /// </summary> public bool GetUserId(string authenticationToken, out string userId, out LiveAuthException error) { Debug.Assert(!string.IsNullOrEmpty(authenticationToken)); return(LiveAuthWebUtility.ReadUserIdFromAuthenticationToken( authenticationToken, this.clientSecret, out userId, out error)); }
/// <summary> /// Decrypts the user ID from a given authentication token. /// </summary> public bool GetUserId(string authenticationToken, out string userId, out LiveAuthException error) { Debug.Assert(!string.IsNullOrEmpty(authenticationToken)); return LiveAuthWebUtility.ReadUserIdFromAuthenticationToken( authenticationToken, this.clientSecret, out userId, out error); }
/// <summary> /// Initializes the LiveAuthClient instance for the current user. /// If an authorization code is present, it will send a request to the auth server to exchange the token. /// If there is an auth session in current context, it will retrieve the current auth session. /// If the current session is expired or the current request url indicates (refresh=1) to get a new token, /// it will try to request a new access token using refresh token that will need to be provided through /// IRefreshTokenHandler.RetrieveRefreshTokenAsync() method. /// Any updated session state will be saved in the auth session cookie. /// </summary> public Task <LiveLoginResult> ExchangeAuthCodeAsync(string redirectUrl, HttpContextBase webContext) { Debug.Assert(webContext != null); Debug.Assert(!string.IsNullOrEmpty(redirectUrl)); this.ValidateConflictAuthTask(); this.webContext = webContext; var taskSource = new TaskCompletionSource <LiveLoginResult>(); this.currentTask = taskSource; string authorizationCode; LiveAuthException error; this.LoadSession(out authorizationCode, out error); // If this page receives an authorization code, then exchange the code // with the auth server to get the tokens. if (!string.IsNullOrEmpty(authorizationCode)) { // We intentionally move auth actions into a wrapping asynchronous process to work around an issue where // invoking Task Async methods will trigger an error to be thrown on an Async Asp.Net page (.aspx) code. Task.Factory.StartNew(() => { this.ExchangeCodeForToken( redirectUrl, authorizationCode); }); } else if (error != null) { // We received error from the auth server response. if (error.ErrorCode == AuthErrorCodes.AccessDenied) { // Access_denied should be treated as NotConnected. LiveLoginResult result = new LiveLoginResult(LiveConnectSessionStatus.NotConnected, null); this.OnAuthTaskCompleted(result); } else { // We received some other error, then throw it. this.OnAuthTaskCompleted(new LiveLoginResult(error)); } } else { // This is exchange auth code only action, but there is neither code nor error return. // The app developer may invoke this at wrong location. error = new LiveAuthException(AuthErrorCodes.ClientError, ErrorText.AuthServerResponseNotAvailable); this.OnAuthTaskCompleted(new LiveLoginResult(error)); } return(taskSource.Task); }
private void OnAuthTaskCompleted( LiveLoginResult loginResult) { if (loginResult != null) { if (loginResult.Session != null) { LiveAuthException error = this.ValidateSession(loginResult.Session); if (error != null) { loginResult = new LiveLoginResult(error); } else { // We have a new session, update the LiveAuthClient.Session this.loginStatus = loginResult; if (this.refreshTokenHandler != null && !string.IsNullOrEmpty(loginResult.Session.RefreshToken)) { string userId; if (this.GetUserId(loginResult.Session.AuthenticationToken, out userId, out error)) { RefreshTokenInfo refreshInfo = new RefreshTokenInfo(loginResult.Session.RefreshToken, userId); Task saveTokenTask = this.refreshTokenHandler.SaveRefreshTokenAsync(refreshInfo); saveTokenTask.ContinueWith((tk) => { this.CompleteAuthTask(loginResult); }); return; } else { loginResult = new LiveLoginResult(error); } } } } } else { // We should return the existing status for cases like already initialized or can't refresh ticket. loginResult = this.loginStatus; } this.CompleteAuthTask(loginResult); }
/// <summary> /// Validate if the user Id from the received session matches the one from the refresh token and current session. /// </summary> private LiveAuthException ValidateSession(LiveConnectSession session) { Debug.Assert(session != null); string currentUserId = null; string userId; LiveAuthException error = null; LiveConnectSession currentSession = (this.loginStatus == null) ? null : this.loginStatus.Session; // Read current session user Id, if available. if (currentSession != null) { LiveAuthException currentSessionError; LiveAuthWebUtility.ReadUserIdFromAuthenticationToken( currentSession.AuthenticationToken, this.clientSecret, out currentUserId, out currentSessionError); } // Read user Id from the new session received from the auth server. LiveAuthWebUtility.ReadUserIdFromAuthenticationToken(session.AuthenticationToken, this.clientSecret, out userId, out error); if (error == null) { if (!string.IsNullOrEmpty(currentUserId) && string.Compare(userId, currentUserId, StringComparison.InvariantCultureIgnoreCase) != 0) { // The user Id should match current session user Id error = new LiveAuthException(AuthErrorCodes.InvalidRequest, ErrorText.NewSessionDoesNotMatchCurrentUserId); } else if (this.refreshTokenInfo != null && string.Compare(userId, this.refreshTokenInfo.UserId, StringComparison.InvariantCultureIgnoreCase) != 0) { // The user Id should match the uesr Id from the one in the refresh token if available. error = new LiveAuthException(AuthErrorCodes.InvalidRequest, ErrorText.RefereshTokenNotMatchUserId); } } return(error); }
private void CompleteAuthTask(LiveLoginResult loginResult) { Debug.Assert(loginResult != null); loginResult = this.ValidateSessionInitScopes(loginResult); HttpContextUtility.UpdateUserSession(this.webContext, loginResult, this.appRequestTs); if (loginResult.Session != null) { // Only update Session property if there is a new session. this.publicAuthClient.Session = loginResult.Session; } this.publicAuthClient.FirePendingPropertyChangedEvents(); TaskCompletionSource <LiveLoginResult> taskSource = this.currentTask; if (taskSource != null) { this.currentTask = null; if (loginResult.Error != null) { var error = loginResult.Error as LiveAuthException; if (error == null) { error = new LiveAuthException(AuthErrorCodes.ClientError, error.Message, loginResult.Error); } error.State = this.appRequestState; taskSource.SetException(loginResult.Error); } else { loginResult.State = this.appRequestState; taskSource.SetResult(loginResult); } } }
/// <summary> /// Decrypt an authentication token and parse into a JsonWebToken object. /// </summary> public static bool DecodeAuthenticationToken( string authenticationToken, object clientSecrets, out JsonWebToken token, out LiveAuthException error) { Debug.Assert(!string.IsNullOrEmpty(authenticationToken)); Debug.Assert(clientSecrets != null); token = null; error = null; try { token = new JsonWebToken(authenticationToken, clientSecrets); return(true); } catch (Exception ex) { error = new LiveAuthException(AuthErrorCodes.ClientError, ex.Message); return(false); } }
/// <summary> /// Decrypt an authentication token and parse into a JsonWebToken object. /// </summary> public static bool DecodeAuthenticationToken( string authenticationToken, object clientSecrets, out JsonWebToken token, out LiveAuthException error) { Debug.Assert(!string.IsNullOrEmpty(authenticationToken)); Debug.Assert(clientSecrets != null); token = null; error = null; try { token = new JsonWebToken(authenticationToken, clientSecrets); return true; } catch (Exception ex) { error = new LiveAuthException(AuthErrorCodes.ClientError, ex.Message); return false; } }
private bool CheckRefreshTokenRequest(out IEnumerable <string> scopes, out LiveAuthException error) { string clientIdFromRequestUrl; error = null; bool isTokenRequest = HttpContextUtility.ReadRefreshTokenRequest(this.webContext, out clientIdFromRequestUrl, out scopes); if (isTokenRequest) { if (string.Compare(clientIdFromRequestUrl, this.clientId, StringComparison.InvariantCultureIgnoreCase) != 0) { // The request client Id does not match current client Id. error = new LiveAuthException(AuthErrorCodes.ClientError, ErrorText.RefreshRequestClientIdNotMatch); } if (this.refreshTokenHandler == null) { // The web client is requesting requesting refresh token, however, the server has not implemented this logic. error = new LiveAuthException(AuthErrorCodes.ClientError, ErrorText.IRefreshTokenHandlerNotProvided); } } return(isTokenRequest); }
/// <summary> /// Authenticate the user. Ask user for consent if neccessary. /// </summary> public async Task <LiveLoginResult> AuthenticateAsync(string scopes, bool silent) { Exception error = null; string accessToken = null; string authenticationToken = null; LiveLoginResult result = null; try { accessToken = await this.GetAccessToken(scopes, silent); LiveConnectSession session = new LiveConnectSession(this.authClient); session.AccessToken = accessToken; if (!string.IsNullOrEmpty(this.authClient.RedirectUrl) && !this.authClient.RedirectUrl.StartsWith(Win8AppIdPrefix, StringComparison.OrdinalIgnoreCase)) { authenticationToken = await this.GetAuthenticationToken(this.authClient.RedirectUrl, silent); session.AuthenticationToken = authenticationToken; } result = new LiveLoginResult(LiveConnectSessionStatus.Connected, session); } catch (TaskCanceledException) { result = new LiveLoginResult(LiveConnectSessionStatus.NotConnected, null); } catch (Exception comExp) { switch (comExp.HResult) { case TailoredAuthClient.UserNotFoundLoginExceptionHResult: result = new LiveLoginResult(LiveConnectSessionStatus.Unknown, null); break; case TailoredAuthClient.ConsentNotGrantedExceptionHResult: result = new LiveLoginResult(LiveConnectSessionStatus.NotConnected, null); break; case TailoredAuthClient.InvalidClientExceptionHResult: case TailoredAuthClient.InvalidAuthTargetExceptionHResult: error = new LiveAuthException(AuthErrorCodes.InvalidRequest, ResourceHelper.GetString("InvalidAuthClient"), comExp); break; default: error = new LiveAuthException(AuthErrorCodes.ServerError, ResourceHelper.GetString("ServerError"), comExp); break; } } if (result == null) { Debug.Assert(error != null); result = new LiveLoginResult(error); } return(result); }
public void OnAuthError(LiveAuthException exception, Object userState) { source.TrySetException(exception); }
/// <summary> /// Authenticate the user. Ask user for consent if neccessary. /// </summary> public async Task<LiveLoginResult> AuthenticateAsync(string scopes, bool silent) { Exception error = null; string accessToken = null; string authenticationToken = null; LiveLoginResult result = null; try { accessToken = await this.GetAccessToken(scopes, silent); LiveConnectSession session = new LiveConnectSession(this.authClient); session.AccessToken = accessToken; if (!string.IsNullOrEmpty(this.authClient.RedirectUrl) && !this.authClient.RedirectUrl.Equals(Win8ReturnUriScheme, StringComparison.OrdinalIgnoreCase)) { authenticationToken = await this.GetAuthenticationToken(this.authClient.RedirectUrl, silent); session.AuthenticationToken = authenticationToken; } result = new LiveLoginResult(LiveConnectSessionStatus.Connected, session); } catch (TaskCanceledException) { result = new LiveLoginResult(LiveConnectSessionStatus.NotConnected, null); } catch (Exception comExp) { switch (comExp.HResult) { case TailoredAuthClient.UserNotFoundLoginExceptionHResult: result = new LiveLoginResult(LiveConnectSessionStatus.Unknown, null); break; case TailoredAuthClient.ConsentNotGrantedExceptionHResult: result = new LiveLoginResult(LiveConnectSessionStatus.NotConnected, null); break; case TailoredAuthClient.InvalidClientExceptionHResult: case TailoredAuthClient.InvalidAuthTargetExceptionHResult: error = new LiveAuthException(AuthErrorCodes.InvalidRequest, ResourceHelper.GetString("InvalidAuthClient"), comExp); break; default: error = new LiveAuthException(AuthErrorCodes.ServerError, ResourceHelper.GetString("ServerError"), comExp); break; } } if (result == null) { Debug.Assert(error != null); result = new LiveLoginResult(error); } return result; }
/// <summary> /// Writes the user current session. /// </summary> public static void UpdateUserSession(HttpContextBase context, LiveLoginResult loginResult, string requestTs) { if (context == null) { return; } Debug.Assert(loginResult != null); Dictionary <string, string> cookieValues = new Dictionary <string, string>(); HttpCookie cookie = context.Request.Cookies[AuthCookie]; HttpCookie newCookie = new HttpCookie(AuthCookie); newCookie.Path = "/"; string host = context.Request.Headers["Host"]; newCookie.Domain = host.Split(':')[0]; if (cookie != null && cookie.Values != null) { foreach (string key in cookie.Values.AllKeys) { newCookie.Values[key] = cookie[key]; } } LiveConnectSession session = loginResult.Session; if (session != null) { newCookie.Values[AuthConstants.AccessToken] = Uri.EscapeDataString(session.AccessToken); newCookie.Values[AuthConstants.AuthenticationToken] = Uri.EscapeDataString(session.AuthenticationToken); newCookie.Values[AuthConstants.Scope] = Uri.EscapeDataString(LiveAuthUtility.BuildScopeString(session.Scopes)); newCookie.Values[AuthConstants.ExpiresIn] = Uri.EscapeDataString(LiveAuthWebUtility.GetExpiresInString(session.Expires)); newCookie.Values[AuthConstants.Expires] = Uri.EscapeDataString(LiveAuthWebUtility.GetExpiresString(session.Expires)); } LiveConnectSessionStatus status; if (!string.IsNullOrEmpty(newCookie[AuthConstants.AccessToken])) { // We have an access token, so it is connected, regardless expired or not // since it is handled after loading the session in both Asp.Net and JS library. status = LiveConnectSessionStatus.Connected; } else { status = loginResult.Status; if (loginResult.Status == LiveConnectSessionStatus.Unknown) { // If we recorded NotConnected previously, keep it. LiveConnectSessionStatus statusFromCookie; if (Enum.TryParse <LiveConnectSessionStatus>( newCookie[AuthConstants.Status], true /*ignore case*/, out statusFromCookie)) { if (statusFromCookie == LiveConnectSessionStatus.NotConnected) { status = statusFromCookie; } } } } newCookie.Values[AuthConstants.Status] = GetStatusString(status); // Needs to write error to inform the JS library. LiveAuthException authError = loginResult.Error as LiveAuthException; if (authError != null) { newCookie.Values[AuthConstants.Error] = Uri.EscapeDataString(authError.ErrorCode); newCookie.Values[AuthConstants.ErrorDescription] = HttpUtility.UrlPathEncode(authError.Message); } else if (status != LiveConnectSessionStatus.Connected) { newCookie.Values[AuthConstants.Error] = Uri.EscapeDataString(AuthErrorCodes.AccessDenied); newCookie.Values[AuthConstants.ErrorDescription] = HttpUtility.UrlPathEncode("Cannot retrieve access token."); } if (!string.IsNullOrEmpty(requestTs)) { newCookie.Values[AuthConstants.ClientRequestTs] = requestTs; } context.Response.Cookies.Add(newCookie); }
private void TryRefreshToken(string redirectUrl) { Debug.Assert(this.loginStatus != null); IEnumerable <string> scopes; LiveAuthException error; bool isTokenRequest = this.CheckRefreshTokenRequest(out scopes, out error); if (error != null) { this.OnAuthTaskCompleted(new LiveLoginResult(error)); return; } // Try to refresh a token if // i) there is a token request or // ii) we don't have a token or // iii) the current token is expired. LiveLoginResult result = null; LiveConnectSession session = this.loginStatus.Session; bool hasValidToken = session != null && session.IsValid; bool shouldRefresh = (this.refreshTokenHandler != null) && (isTokenRequest || !hasValidToken); if (!shouldRefresh) { this.OnAuthTaskCompleted(null); return; } if (this.initScopes == null) { // We don't have initScopes, then use the scopes received from Url. this.initScopes = scopes; } this.refreshTokenHandler.RetrieveRefreshTokenAsync().ContinueWith(t => { try { this.refreshTokenInfo = t.Result; if (this.refreshTokenInfo != null) { string currentUserId = this.publicAuthClient.CurrentUserId; if (currentUserId != null && this.refreshTokenInfo.UserId != currentUserId) { // There is a user Id available in current session. We need to ensure the token provided matches it. result = new LiveLoginResult(new LiveAuthException( AuthErrorCodes.InvalidRequest, ErrorText.RefereshTokenNotMatchUserId)); } else { LiveAuthRequestUtility.RefreshTokenAsync( this.clientId, this.clientSecret, redirectUrl, this.refreshTokenInfo.RefreshToken, null/*scopes - We intentially specify null scopes and validate the initScopes after we have the session * result. With this approach, we can return notConnected if initScopes is not satisfied, and surface * the error if there is one. */ ).ContinueWith((Task <LiveLoginResult> rt) => { result = rt.Result; this.OnAuthTaskCompleted(result); }); return; } } } catch (Exception ex) { error = new LiveAuthException(AuthErrorCodes.ClientError, ErrorText.RetrieveRefreshTokenError, ex); result = new LiveLoginResult(error); } this.OnAuthTaskCompleted(result); }); }
/// <summary> /// Validate if the user Id from the received session matches the one from the refresh token and current session. /// </summary> private LiveAuthException ValidateSession(LiveConnectSession session) { Debug.Assert(session != null); string currentUserId = null; string userId; LiveAuthException error = null; LiveConnectSession currentSession = (this.loginStatus == null) ? null : this.loginStatus.Session; // Read current session user Id, if available. if (currentSession != null) { LiveAuthException currentSessionError; LiveAuthWebUtility.ReadUserIdFromAuthenticationToken( currentSession.AuthenticationToken, this.clientSecrets, out currentUserId, out currentSessionError); } // Read user Id from the new session received from the auth server. LiveAuthWebUtility.ReadUserIdFromAuthenticationToken(session.AuthenticationToken, this.clientSecrets, out userId, out error); if (error == null) { if (!string.IsNullOrEmpty(currentUserId) && string.Compare(userId, currentUserId, StringComparison.InvariantCultureIgnoreCase) != 0) { // The user Id should match current session user Id error = new LiveAuthException(AuthErrorCodes.InvalidRequest, ErrorText.NewSessionDoesNotMatchCurrentUserId); } else if (this.refreshTokenInfo != null && string.Compare(userId, this.refreshTokenInfo.UserId, StringComparison.InvariantCultureIgnoreCase) != 0) { // The user Id should match the uesr Id from the one in the refresh token if available. error = new LiveAuthException(AuthErrorCodes.InvalidRequest, ErrorText.RefereshTokenNotMatchUserId); } } return error; }
private bool CheckRefreshTokenRequest(out IEnumerable<string> scopes, out LiveAuthException error) { string clientIdFromRequestUrl; error = null; bool isTokenRequest = HttpContextUtility.ReadRefreshTokenRequest(this.webContext, out clientIdFromRequestUrl, out scopes); if (isTokenRequest) { if (string.Compare(clientIdFromRequestUrl, this.clientId, StringComparison.InvariantCultureIgnoreCase) != 0) { // The request client Id does not match current client Id. error = new LiveAuthException(AuthErrorCodes.ClientError, ErrorText.RefreshRequestClientIdNotMatch); } if (this.refreshTokenHandler == null) { // The web client is requesting requesting refresh token, however, the server has not implemented this logic. error = new LiveAuthException(AuthErrorCodes.ClientError, ErrorText.IRefreshTokenHandlerNotProvided); } } return isTokenRequest; }
private void TryRefreshToken(string redirectUrl) { Debug.Assert(this.loginStatus != null); IEnumerable<string> scopes; LiveAuthException error; bool isTokenRequest = this.CheckRefreshTokenRequest(out scopes, out error); if (error != null) { this.OnAuthTaskCompleted(new LiveLoginResult(error)); return; } // Try to refresh a token if // i) there is a token request or // ii) we don't have a token or // iii) the current token is expired. LiveLoginResult result = null; LiveConnectSession session = this.loginStatus.Session; bool hasValidToken = session != null && session.IsValid; bool shouldRefresh = (this.refreshTokenHandler != null) && (isTokenRequest || !hasValidToken); if (!shouldRefresh) { this.OnAuthTaskCompleted(null); return; } if (this.initScopes == null) { // We don't have initScopes, then use the scopes received from Url. this.initScopes = scopes; } this.refreshTokenHandler.RetrieveRefreshTokenAsync().ContinueWith(t => { try { this.refreshTokenInfo = t.Result; if (this.refreshTokenInfo != null) { string currentUserId = this.publicAuthClient.CurrentUserId; if (currentUserId != null && this.refreshTokenInfo.UserId != currentUserId) { // There is a user Id available in current session. We need to ensure the token provided matches it. result = new LiveLoginResult(new LiveAuthException( AuthErrorCodes.InvalidRequest, ErrorText.RefereshTokenNotMatchUserId)); } else { LiveAuthRequestUtility.RefreshTokenAsync( this.clientId, this.clientSecret, redirectUrl, this.refreshTokenInfo.RefreshToken, null/*scopes - We intentially specify null scopes and validate the initScopes after we have the session * result. With this approach, we can return notConnected if initScopes is not satisfied, and surface * the error if there is one. */ ).ContinueWith((Task<LiveLoginResult> rt) => { result = rt.Result; this.OnAuthTaskCompleted(result); }); return; } } } catch (Exception ex) { error = new LiveAuthException(AuthErrorCodes.ClientError, ErrorText.RetrieveRefreshTokenError, ex); result = new LiveLoginResult(error); } this.OnAuthTaskCompleted(result); }); }
private void CompleteAuthTask(LiveLoginResult loginResult) { Debug.Assert(loginResult != null); loginResult = this.ValidateSessionInitScopes(loginResult); HttpContextUtility.UpdateUserSession(this.webContext, loginResult, this.appRequestTs); if (loginResult.Session != null) { // Only update Session property if there is a new session. this.publicAuthClient.Session = loginResult.Session; } this.publicAuthClient.FirePendingPropertyChangedEvents(); TaskCompletionSource<LiveLoginResult> taskSource = this.currentTask; if (taskSource != null) { this.currentTask = null; if (loginResult.Error != null) { var error = loginResult.Error as LiveAuthException; if (error == null) { error = new LiveAuthException(AuthErrorCodes.ClientError, error.Message, loginResult.Error); } error.State = this.appRequestState; taskSource.SetException(loginResult.Error); } else { loginResult.State = this.appRequestState; taskSource.SetResult(loginResult); } } }
/// <summary> /// Initializes the LiveAuthClient instance for the current user. /// If an authorization code is present, it will send a request to the auth server to exchange the token. /// If there is an auth session in current context, it will retrieve the current auth session. /// If the current session is expired or the current request url indicates (refresh=1) to get a new token, /// it will try to request a new access token using refresh token that will need to be provided through /// IRefreshTokenHandler.RetrieveRefreshTokenAsync() method. /// Any updated session state will be saved in the auth session cookie. /// </summary> public Task<LiveLoginResult> ExchangeAuthCodeAsync(string redirectUrl, HttpContextBase webContext) { Debug.Assert(webContext != null); Debug.Assert(!string.IsNullOrEmpty(redirectUrl)); this.ValidateConflictAuthTask(); this.webContext = webContext; var taskSource = new TaskCompletionSource<LiveLoginResult>(); this.currentTask = taskSource; string authorizationCode; LiveAuthException error; this.LoadSession(out authorizationCode, out error); // If this page receives an authorization code, then exchange the code // with the auth server to get the tokens. if (!string.IsNullOrEmpty(authorizationCode)) { // We intentionally move auth actions into a wrapping asynchronous process to work around an issue where // invoking Task Async methods will trigger an error to be thrown on an Async Asp.Net page (.aspx) code. Task.Factory.StartNew(() => { this.ExchangeCodeForToken( redirectUrl, authorizationCode); }); } else if (error != null) { // We received error from the auth server response. if (error.ErrorCode == AuthErrorCodes.AccessDenied) { // Access_denied should be treated as NotConnected. LiveLoginResult result = new LiveLoginResult(LiveConnectSessionStatus.NotConnected, null); this.OnAuthTaskCompleted(result); } else { // We received some other error, then throw it. this.OnAuthTaskCompleted(new LiveLoginResult(error)); } } else { // This is exchange auth code only action, but there is neither code nor error return. // The app developer may invoke this at wrong location. error = new LiveAuthException(AuthErrorCodes.ClientError, ErrorText.AuthServerResponseNotAvailable); this.OnAuthTaskCompleted(new LiveLoginResult(error)); } return taskSource.Task; }