/// <summary>
        /// TODO: single sign-on is not supported currently.
        /// Implements the logic for verifying and decoding authentication token issued by MSA.
        /// Note that authentication tokens are used for single sign-on only.
        /// </summary>
        /// <param name="userAuthenticationToken">Microsoft authentication token</param>
        /// <param name="appClientID">ClientID issued by Microsoft to the app that requested the token</param>
        /// <param name="appClientSecret">Client secret issued by Microsoft to the app that requested the token</param>
        /// <returns>Tuple:
        ///     boolean: true if the authentication token was valid, false otherwise
        ///     string: the user id in the authentication token. (TODO: return the JWT token instead).
        ///     <c>OAuthException</c>: exception specific to <c>OAuth</c> encountered during token validation and decoding
        /// </returns>
        private Tuple <bool, string, OAuthException> VerifyAndDecodeMicrosoftAuthenticationToken(string userAuthenticationToken, string appClientID, string appClientSecret)
        {
            // Flag representing whether token has been verified
            bool tokenVerified = false;

            // Identity extracted from the token
            string tokenIdentity = null;

            // Exception raised upon verifying and decoding the token
            OAuthException tokenEx = null;

            // MSA authentication token simply gets decoded to a JWT
            JsonWebToken jwtToken = null;

            // Exception raised by LiveID decoding
            LiveAuthException liveEx = null;

            // Decode token. If cannot decode, create appropriate OAuthException.
            // DecodeAuthenticationToken does not appear to throw any errors. Instead, its errors are caught, and it returns false instead.
            if (LiveAuthWebUtility.DecodeAuthenticationToken(userAuthenticationToken, appClientSecret, out jwtToken, out liveEx) == false)
            {
                tokenEx = new OAuthException(OAuthErrors.ServiceUnavailable_503_Microsoft, liveEx /* pass out the LiveException also */);
            }
            else
            {
                //// TOKEN Validation checks

                if (jwtToken.IsExpired == true)
                {
                    // Token expired. Handle this appropriately.
                    tokenEx = new OAuthException(OAuthErrors.Unauthorized_401_1);
                }
                else if (appClientID != jwtToken.Claims.AppId)
                {
                    // Token stolen by different app. Handle this appropriately.
                    tokenEx = new OAuthException(OAuthErrors.Unauthorized_401_3);
                }
                else if (string.IsNullOrEmpty(jwtToken.Claims.UserId))
                {
                    // Token's id doesn't exist. Handle this appropriately.
                    tokenEx = new OAuthException(OAuthErrors.Unauthorized_401_4);
                }
                else
                {
                    // Extract the token's identity
                    tokenIdentity = jwtToken.Claims.UserId;
                    tokenVerified = true;
                }
            }

            // MSA's authentication token contains no information about a user other than it's account id.
            // Note that the account id found in an MSA authentication token is different than the actual account id.
            // Although couldn't find specs, it's likely this is due to privacy reasons. MSA issues a different account
            // id to different application publishers. In this ways, two publishers cannot track a single user accross
            // since the ids found in their auth tokens are different. However, this id is the same across two different
            // apps owned by the same publisher.
            return(new Tuple <bool, string, OAuthException>(tokenVerified, tokenIdentity, tokenEx));
        }
Exemplo n.º 2
0
        /// <summary>
        /// Verifies a Facebook access token by asking for the corresponding debug token (Facebook's famous debug_token) from Facebook.
        /// This is better than checking an access token using implicit flow -- unlike implicit flow, this check ensures that the token
        /// was issued to an our app. (this means that the user typed his password in an window that listed our app name). Implicit flow
        /// only guarantees that a user typed his password in a window that listed some app name (not necessarily ours).
        /// </summary>
        /// <param name="userAccessToken">Facebook user access token</param>
        /// <param name="facebookAppID">AppID issued by Facebook to the app that requested the token</param>
        /// <param name="facebookAppSecret">App secret issued by Facebook to the app that requested the token</param>
        /// <returns>Tuple:
        ///     boolean: true if the access token was valid, false otherwise
        ///     <code>OAuthException</code>: exception specific to <c>OAuth</c> encountered during token validation and decoding
        /// </returns>
        private async Task <Tuple <bool, OAuthException> > FacebookDebugUserAccessToken(string userAccessToken, string facebookAppID, string facebookAppSecret)
        {
            // Flag representing whether token has been verified
            bool tokenVerified = false;

            // Exception raised upon verifying and decoding the token
            OAuthException tokenEx = null;

            try
            {
                // Using the nice Facebook library, we start by creating a FacebookClient with our app's credentials
                FacebookClient client = new FacebookClient(facebookAppID + "|" + facebookAppSecret);

                // Conver the token into a debugToken
                dynamic resultDebug = await client.GetTaskAsync <JsonObject>("debug_token", new { input_token = userAccessToken, });

                // The result is a collection of JsonObjects. According to Facebook, there should never be more than one object
                // in the result of a debug_token
                if (resultDebug == null || resultDebug.Values == null || resultDebug.Values.Count != 1)
                {
                    tokenEx = new OAuthException(OAuthErrors.ServiceUnavailable_503_Facebook);
                }
                else
                {
                    // FB access token simply gets decoded into a debugToken
                    FacebookDebugToken debugToken = JsonConvert.DeserializeObject <FacebookDebugToken>(Enumerable.First(resultDebug.Values).ToString());

                    // Token validation tests

                    // Check whether the token is valid
                    if (debugToken.IsValid == false)
                    {
                        // Token invalid. Handle this appropriately.
                        tokenEx = new OAuthException(OAuthErrors.Unauthorized_401_2);
                    }
                    else if (facebookAppID != debugToken.AppId)
                    {
                        // Token stolen by different app. Handle this appropriately.
                        tokenEx = new OAuthException(OAuthErrors.Unauthorized_401_3);
                    }
                    else if (OAuthUtil.BeginningOfTime.AddSeconds(debugToken.ExpiresAt).ToLocalTime() < DateTime.Now)
                    {
                        // Token expired. Handle this appropriately.
                        tokenEx = new OAuthException(OAuthErrors.Unauthorized_401_1);
                    }
                    else if (string.IsNullOrEmpty(debugToken.UserId))
                    {
                        // Token's id doesn't exist. Handle this appropriately.
                        tokenEx = new OAuthException(OAuthErrors.Unauthorized_401_4);
                    }
                    else
                    {
                        // Token is verified. Yay!
                        tokenVerified = true;
                    }
                }
            }
            catch (Exception ex)
            {
                tokenEx = new OAuthException(OAuthErrors.ServiceUnavailable_503_Facebook, ex /* pass out the FacebookOOAuthException also */);
            }

            return(new Tuple <bool, OAuthException>(tokenVerified, tokenEx));
        }