private async Task <AuthorizationResult> AcquireAuthCodeAsync( string redirectUri = TestRedirectUri, string requestUri = TestAuthorizationRequestUri, string responseUriString = TestAuthorizationResponseUri) { // Arrange var requestContext = new RequestContext(TestCommon.CreateDefaultServiceBundle(), Guid.NewGuid()); var responseUri = new Uri(responseUriString); IWebUI webUI = new DefaultOsBrowserWebUi(_platformProxy, _logger, _tcpInterceptor); _tcpInterceptor.ListenToSingleRequestAndRespondAsync( TestPort, Arg.Any <Func <Uri, string> >(), CancellationToken.None) .Returns(Task.FromResult(responseUri)); // Act AuthorizationResult authorizationResult = await webUI.AcquireAuthorizationAsync( new Uri(requestUri), new Uri(redirectUri), requestContext, CancellationToken.None).ConfigureAwait(false); // Assert that we opened the browser await _platformProxy.Received(1).StartDefaultOsBrowserAsync(requestUri) .ConfigureAwait(false); return(authorizationResult); }
private static IPublicClientApplication CreatePca() { IPublicClientApplication pca = PublicClientApplicationBuilder .Create(s_clientIdForPublicApp) .WithAuthority(GetAuthority()) .WithLogging(Log, LogLevel.Verbose, true) .WithRedirectUri(DefaultOsBrowserWebUi.FindFreeLocalhostRedirectUri()) // required for DefaultOsBrowser .Build(); pca.UserTokenCache.SetBeforeAccess(notificationArgs => { notificationArgs.TokenCache.DeserializeMsalV3(File.Exists(CacheFilePath) ? File.ReadAllBytes(CacheFilePath) : null); }); pca.UserTokenCache.SetAfterAccess(notificationArgs => { // if the access operation resulted in a cache update if (notificationArgs.HasStateChanged) { // reflect changes in the persistent store File.WriteAllBytes(CacheFilePath, notificationArgs.TokenCache.SerializeMsalV3()); } }); return(pca); }
/// <summary> /// Apply this authenticator to the given authentication parameters. /// </summary> /// <param name="parameters">The complex object containing authentication specific information.</param> /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param> /// <returns> /// An instance of <see cref="AuthenticationResult" /> that represents the access token generated as result of a successful authenication. /// </returns> public override async Task <AuthenticationResult> AuthenticateAsync(AuthenticationParameters parameters, CancellationToken cancellationToken = default) { AuthenticationResult authResult; IClientApplicationBase app; InteractiveParameters interactiveParameters = parameters as InteractiveParameters; TcpListener listener = null; string redirectUri = null; int port = 8399; while (++port < 9000) { try { listener = new TcpListener(IPAddress.Loopback, port); listener.Start(); redirectUri = $"http://localhost:{port}/"; listener.Stop(); break; } catch (Exception ex) { WriteWarning($"Port {port} is taken with exception '{ex.Message}'; trying to connect to the next port."); listener?.Stop(); } } app = GetClient(parameters.Account, parameters.Environment, redirectUri); if (app is IConfidentialClientApplication) { ICustomWebUi customWebUi = new DefaultOsBrowserWebUi(interactiveParameters.Message); ServiceClientTracing.Information($"[InteractiveUserAuthenticator] Calling AcquireAuthorizationCodeAsync - Scopes: '{string.Join(",", parameters.Scopes)}'"); Uri authCodeUrl = await customWebUi.AcquireAuthorizationCodeAsync( await app.AsConfidentialClient().GetAuthorizationRequestUrl(parameters.Scopes).ExecuteAsync(cancellationToken).ConfigureAwait(false), new Uri(redirectUri), cancellationToken).ConfigureAwait(false); NameValueCollection queryStringParameters = HttpUtility.ParseQueryString(authCodeUrl.Query); ServiceClientTracing.Information($"[InteractiveUserAuthenticator] Calling AcquireTokenByAuthorizationCode - Scopes: '{string.Join(",", parameters.Scopes)}'"); authResult = await app.AsConfidentialClient().AcquireTokenByAuthorizationCode( parameters.Scopes, queryStringParameters["code"]).ExecuteAsync(cancellationToken).ConfigureAwait(false); } else { ServiceClientTracing.Information(string.Format(CultureInfo.InvariantCulture, "[InteractiveUserAuthenticator] Calling AcquireTokenInteractive - Scopes: '{0}'", string.Join(",", parameters.Scopes))); authResult = await app.AsPublicClient().AcquireTokenInteractive(parameters.Scopes) .WithCustomWebUi(new DefaultOsBrowserWebUi(interactiveParameters.Message)) .WithPrompt(Prompt.ForceLogin) .ExecuteAsync(cancellationToken).ConfigureAwait(false); } return(authResult); }
public void ValidateRedirectUri() { // Arrange var tcpInterceptor = Substitute.For <ITcpInterceptor>(); IWebUI webUi = new DefaultOsBrowserWebUi(_platformProxy, _logger, tcpInterceptor); // Act webUi.ValidateRedirectUri(new Uri("http://localhost:12345")); webUi.ValidateRedirectUri(new Uri("http://127.0.0.1:54321")); AssertInvalidRedirectUri(webUi, "http://localhost"); // no port AssertInvalidRedirectUri(webUi, "http://localhost:0"); // no port AssertInvalidRedirectUri(webUi, "http://localhost:80"); // default port AssertInvalidRedirectUri(webUi, "http://www.bing.com:1234"); // not localhost }
/// <summary> /// Acquires the Azure management token asynchronously. /// </summary> /// <param name="tenantId">The tenant identifier.</param> /// <param name="usePrompt">The prompt behavior. If true, raises a prompt, otherwise performs silent login.</param> /// <param name="userIdentifier">Optional user for which token is to be fetched.</param> /// <returns>AAD authentication result.</returns> private async Task <UserAuthenticationDetails> AcquireAzureManagementTokenAsync( string tenantId, bool usePrompt, string userIdentifier = null) { Guid correlationId = Guid.NewGuid(); this.logger.LogInformation($"Acquiring token with correlation Id set to '{correlationId}'"); try { #if USEADAL AuthenticationContext authenticationContext = new AuthenticationContext( $"{UserAuthenticator.AADLoginAuthority}{tenantId}", validateAuthority: false, TokenCache.DefaultShared, new AzureAdHttpClientFactory()) { CorrelationId = correlationId, }; AuthenticationResult authenticationResult = null; if (userIdentifier == null) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Uri redirectUri = DefaultOsBrowserWebUi.UpdateRedirectUri(new Uri("http://localhost")); authenticationResult = await authenticationContext.AcquireTokenAsync( UserAuthenticator.AzureAADResource, UserAuthenticator.PSClientId, redirectUri, new PlatformParameters( usePrompt ? PromptBehavior.SelectAccount : PromptBehavior.Never, new DefaultOsBrowserWebUi(this.logger))).ConfigureAwait(false); } else { DeviceCodeResult deviceCodeResult = await authenticationContext.AcquireDeviceCodeAsync(UserAuthenticator.AzureAADResource, UserAuthenticator.PSClientId).ConfigureAwait(false); Console.WriteLine($"Interactive login required. Please enter {deviceCodeResult.UserCode} when asked for in the browser window. If a browser windows does not open or is not supported, follow the instructions below (these can be followed on any device) {Environment.NewLine}{deviceCodeResult.Message}"); await Task.Delay(1000).ConfigureAwait(false); authenticationResult = await authenticationContext.AcquireTokenByDeviceCodeAsync(deviceCodeResult).ConfigureAwait(false); } } else { authenticationResult = await authenticationContext.AcquireTokenSilentAsync( UserAuthenticator.AzureAADResource, UserAuthenticator.PSClientId).ConfigureAwait(false); } return(new UserAuthenticationDetails { AccessToken = authenticationResult.AccessToken, TenantId = authenticationResult.TenantId, Username = authenticationResult.UserInfo.DisplayableId, }); #else AuthenticationResult authenticationResult = null; if (userIdentifier == null) { if (this.publicClientApplication.IsSystemWebViewAvailable) { authenticationResult = await this.publicClientApplication .AcquireTokenInteractive(new List <string>() { AzureAADResource + "/.default" }) .WithAuthority($"{UserAuthenticator.AADLoginAuthority}{tenantId}", validateAuthority: false) .WithCorrelationId(correlationId) .WithPrompt(usePrompt ? Prompt.SelectAccount : Prompt.NoPrompt) .ExecuteAsync().ConfigureAwait(false); } else { authenticationResult = await this.publicClientApplication .AcquireTokenWithDeviceCode( new List <string>() { AzureAADResource + "/.default" }, async (deviceCodeResult) => { Console.WriteLine($"Interactive login required. Please enter {deviceCodeResult.UserCode} when asked for in the browser window. If a browser windows does not open or is not supported, follow the instructions below (these can be followed on any device) {Environment.NewLine}{deviceCodeResult.Message}"); await Task.Delay(1000).ConfigureAwait(false); try { // Open the browser with the url. ProcessStartInfo processStartInfo = new ProcessStartInfo { FileName = deviceCodeResult.VerificationUrl, UseShellExecute = true, }; Process.Start(processStartInfo); } catch (Exception) { Console.WriteLine($"Could not open the url for verification on this machine. Please open {deviceCodeResult.VerificationUrl} from any machine and enter code {deviceCodeResult.UserCode} when asked to do so to continue. The process will continue automatically once the login verification is done."); } }) .WithAuthority($"{UserAuthenticator.AADLoginAuthority}{tenantId}", validateAuthority: false) .WithCorrelationId(correlationId) .ExecuteAsync().ConfigureAwait(false); } } else { authenticationResult = await this.publicClientApplication .AcquireTokenSilent(new List <string>() { AzureAADResource + "/.default" }, userIdentifier) .WithAuthority($"{UserAuthenticator.AADLoginAuthority}{tenantId}", validateAuthority: false) .WithCorrelationId(correlationId) .ExecuteAsync().ConfigureAwait(false); } return(new UserAuthenticationDetails { AccessToken = authenticationResult.AccessToken, Username = authenticationResult.Account.Username, TenantId = authenticationResult.TenantId, }); #endif } catch (Exception ex) { this.logger.LogError(ex, $"Failed to acquire token. Correlation Id '{correlationId}'"); throw; } }