private async Task RefreshAccessTokenAsync()

in RESTProxy/Models/Endpoint.cs [434:494]


        private async Task RefreshAccessTokenAsync()
        {
            // These define the needed OAUTH2 URI and request body format for retrieving the AccessToken.
            const string TokenUrlFormat = "https://login.windows.net/{0}/oauth2/token";
            const string AuthBodyFormat = "grant_type=client_credentials&client_id={0}&client_secret={1}&resource={2}";

            // We set ValidThru to "Now" *here* because we'll be adding the "expires_in" value to
            // this (which is in seconds from the time the response was generated).  If we did
            // DateTime.Now after we get the response, it could potentially be multiple seconds
            // after the server generated that response.  It's safer to indicate that it expires
            // sooner than it really does and force an earlier Refresh than to incorrectly use an
            // AccessToken that has expired and get an undesirable API request failure.
            this.ValidThru = DateTime.Now;

            string uri = string.Format(TokenUrlFormat, this.TenantId);
            string body = string.Format(
                AuthBodyFormat,
                System.Web.HttpUtility.UrlEncode(this.ClientId),
                System.Web.HttpUtility.UrlEncode(this.ClientSecret),
                this.BaseUri);

            WebRequest request = HttpWebRequest.Create(uri);
            request.Method = "POST";

            using (Stream requestStream = await request.GetRequestStreamAsync())
            {
                byte[] bytes = System.Text.Encoding.UTF8.GetBytes(body);
                requestStream.Write(bytes, 0, bytes.Length);
            }

            using (WebResponse response = await request.GetResponseAsync())
            {
                using (Stream stream = response.GetResponseStream())
                {
                    using (StreamReader reader = new StreamReader(stream))
                    {
                        string responseString = reader.ReadToEnd();

                        JObject jsonResponse = JObject.Parse(responseString);
                        this.AccessToken = (string)jsonResponse["access_token"];

                        // We'll intentionally expire the AccessToken a bit earlier since there is
                        // a non-zero amount of time that will be spent between when we check the
                        // expiration time of the token and when the REST request makes it to the
                        // real endpoint for authentication.
                        const int ExpirationBufferSeconds = 90;

                        // We intentionally aren't using
                        // DateTimeOffset.FromUnixTimeSeconds((long)jsonResponse["expires_on"]).UtcDateTime.
                        // The problem with that is the DateTime on this server may not be in sync
                        // with the remote server, and thus the relative time might be off by a
                        // matter of minutes (actually seen in practice).  Instead, we'll always
                        // operate off of our own system's time and use "expires_in" instead which
                        // is just a relative number of seconds from when the request was returned
                        // to us.
                        long expiresIn = (long)jsonResponse["expires_in"] - ExpirationBufferSeconds;
                        this.ValidThru = this.ValidThru.AddSeconds(expiresIn);
                    }
                }
            }
        }