How do I determine an expired access token?

0

I am interfacing with the Microsoft Health Cloud API and have successfully requested an access token and refresh token. Communication with the RESTful API works as intended, although I am having a hard time figuring out, how to reliably determine an expired access token.

I have the following code in place:

fire_and_forget read_profile()
{
    HttpClient httpClient{};
    httpClient.DefaultRequestHeaders().Authorization({ L"bearer", access_token_ });
    try
    {
        auto const response{ co_await httpClient.GetStringAsync({ L"https://api.microsofthealth.net/v1/me/Profile" }) };
        // Raise event passing the response along.
        // Code left out for brevity.
        co_return;
    }
    catch (hresult_error const& e)
    {
        if (e.code() != 0x80190191) // Magic value for "unauthorized access (401)"
        {
            throw;
        }
        // This is an "unauthorized access (401)" error. Continue with requesting a new
        // access token from the refresh token.
        // Code left out for brevity.
    }

Although it appears to work, it feels wrong for so many reasons. It's not just the magic value, but also the fact, that this particular error code may be used for other error modes.

Is there a more robust way of determining, whether an access token has expired?

Note: I understand, that I could use the expiration interval, and check against the system time. I'd rather not go down that route, as it isn't entirely reliable either, and introduces additional complexity for roaming that information across devices.

oauth
uwp
c++-winrt
asked on Stack Overflow May 4, 2018 by IInspectable

1 Answer

0

I understand, that I could use the expiration interval, and check against the system time.

Microsoft Health Cloud API has provided expires_in field to verify the token is valid. In general, we could check against the system time, and if the system time was artificially modified, it isn't entirely reliable. So we could use NTP server time, rather than use system time.

 public async static Task<DateTime> GetNetworkTime()
 {
     //default Windows time server
     const string ntpServer = "time.windows.com";

     // NTP message size - 16 bytes of the digest (RFC 2030)
     var ntpData = new byte[48];

     //Setting the Leap Indicator, Version Number and Mode values
     ntpData[0] = 0x1B; //LI = 0 (no warning), VN = 3 (IPv4 only), Mode = 3 (Client Mode)

     var addresses = await Dns.GetHostAddressesAsync(ntpServer);
     //The UDP port number assigned to NTP is 123
     var ipEndPoint = new IPEndPoint(addresses[0], 123);
     //NTP uses UDP

     using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
     {
         socket.Connect(ipEndPoint);

         //Stops code hang if NTP is blocked
         socket.ReceiveTimeout = 3000;

         socket.Send(ntpData);
         socket.Receive(ntpData);
         socket.Dispose();
     }

     //Offset to get to the "Transmit Timestamp" field (time at which the reply 
     //departed the server for the client, in 64-bit timestamp format."
     const byte serverReplyTime = 40;

     //Get the seconds part
     ulong intPart = BitConverter.ToUInt32(ntpData, serverReplyTime);

     //Get the seconds fraction
     ulong fractPart = BitConverter.ToUInt32(ntpData, serverReplyTime + 4);

     //Convert From big-endian to little-endian
     intPart = SwapEndianness(intPart);
     fractPart = SwapEndianness(fractPart);

     var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);

     //**UTC** time
     var networkDateTime = (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddMilliseconds((long)milliseconds);

     return networkDateTime.ToLocalTime();
 }

 // stackoverflow.com/a/3294698/162671
 static uint SwapEndianness(ulong x)
 {
     return (uint)(((x & 0x000000ff) << 24) +
                    ((x & 0x0000ff00) << 8) +
                    ((x & 0x00ff0000) >> 8) +
                    ((x & 0xff000000) >> 24));
 }
answered on Stack Overflow May 7, 2018 by Nico Zhu - MSFT

User contributions licensed under CC BY-SA 3.0