/// <summary> /// Utility method for converting an ADAL <see cref="MsiAuthResult"/> into an <see cref="SdkAuthResult"/>. /// </summary> /// <param name="authenticationResult">The <see cref="MsiAuthResult"/></param> /// <returns>An <see cref="SdkAuthResult"/>.</returns> private static SdkAuthResult ToSdkAuthResult(this MsiAuthResult authenticationResult) { // Use a temporary variable to manipulate the access token string accessToken = authenticationResult.AccessToken; // Pad the access token with '=' until it's length is divisible by 4 accessToken += Enumerable.Repeat('=', 4 - (accessToken.Length % 4)); // Replace invalid characters accessToken = accessToken .Replace('-', '+') .Replace('_', '/'); // Convert from base64 to a string string decodedAccessToken = Encoding.ASCII.GetString(Convert.FromBase64String(accessToken)); // Convert to an object so we can extract values JToken deserializedAccessToken = JToken.Parse(decodedAccessToken); // Extract the user data string uniqueUserId = deserializedAccessToken["unique_name"]?.Value <string>(); string tenantId = deserializedAccessToken["tid"]?.Value <string>(); // TODO: Validate access token by checking its signature // Build and return the result return(new SdkAuthResult( accessTokenType: authenticationResult.AccessTokenType, accessToken: authenticationResult.AccessToken, userId: null, expiresOn: authenticationResult.ExpiresOn, psUserDisplayableInformation: new { UPN = uniqueUserId, TenantId = tenantId, })); }
/// <summary> /// Refreshes the access token using MSI auth if required, otherwise returns the most recent still-valid refresh token. /// </summary> /// <returns>A valid access token.</returns> /// <exception cref="MsiAuthException">If the login attempt fails</exception> internal static SdkAuthResult RefreshMsiAuth() { // Get the environment parameters EnvironmentParameters environmentParameters = AuthUtils.CurrentEnvironmentParameters; // Create auth context that we will use to connect to the AAD endpoint AuthenticationContext authContext = new AuthenticationContext(environmentParameters.AuthUrl); SdkAuthResult authResult = AuthUtils.LatestMsiAuthResult; if (authResult == null || authResult.IsExpired) { // Create the HTTP client using (HttpClient client = new HttpClient()) { // Add the "Metadata:true" header client.DefaultRequestHeaders.Add("Metadata", "true"); // Make the request for the desired resource HttpResponseMessage response = client .GetAsync($"{AuthUtils.ManagedServiceIdentityEndpoint}?resource={environmentParameters.ResourceId}") .GetAwaiter().GetResult(); // Make sure we successfully retrieved the result if (!response.IsSuccessStatusCode) { // Extract the error code and error message from the failure response string errorCode; string errorMessage; try { // Get the content of the failure response string errorJsonString = response.Content.ReadAsStringAsync() .GetAwaiter().GetResult(); JToken errorJson = JToken.Parse(errorJsonString)["error"]?.Value <JToken>(); // Extract error code and error message errorCode = errorJson["code"].Value <string>() ?? string.Empty; errorMessage = errorJson["message"].Value <string>() ?? string.Empty; } catch (Exception ex) { throw new MsiAuthException("Unable to authenticate using MSI", ex); } // Build the exception message string exceptionMessage; if (!string.IsNullOrWhiteSpace(errorMessage)) { exceptionMessage = errorMessage; } else { exceptionMessage = "Unknown MSI authentication error"; } if (!string.IsNullOrWhiteSpace(errorCode)) { exceptionMessage = $"{errorCode}: {exceptionMessage}"; } // Throw the exception throw new MsiAuthException(exceptionMessage); } // Convert the response from JSON into an object MsiAuthResult msiResult = JsonConvert.DeserializeObject <MsiAuthResult>(response.Content.ReadAsStringAsync() .GetAwaiter().GetResult()); // Convert the MSI auth result to our own type authResult = msiResult.ToSdkAuthResult(); } } return(authResult); }