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);
}
}
}
}