예제 #1
0
        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;
            }
        }
예제 #2
0
        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);
                }
            }
        }
예제 #3
0
        /// <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;
        }
예제 #6
0
        /// <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);
            }
        }
예제 #9
0
        /// <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);
 }
예제 #11
0
        /// <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);
        }
예제 #12
0
        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);
        }
예제 #13
0
        /// <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);
        }
예제 #14
0
        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);
                }
            }
        }
예제 #15
0
        /// <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;
            }
        }
예제 #17
0
        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);
        }
예제 #19
0
 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;
        }
예제 #21
0
        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);
                }
            }
        }
예제 #22
0
        /// <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);
        }
예제 #23
0
        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);
                }
            }
        }
        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;
            }
        }
        /// <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;
        }