/// <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) { JsonWebToken token; string value = parameters.Account.GetProperty(MgmtAccountPropertyType.AccessToken); token = new JsonWebToken(value); ServiceClientTracing.Information($"[AccessTokenAuthenticator] The specified access token expires at {token.ValidTo}"); if (DateTimeOffset.UtcNow > token.ValidTo) { throw new MgmtPowerShellException("The access token has expired. Generate a new one and try again.", MgmtPowerShellErrorCategory.TokenExpired); } await Task.CompletedTask.ConfigureAwait(false); ServiceClientTracing.Information("[AccessTokenAuthenticator] Constructing the authentication result based on the specified access token"); return(new AuthenticationResult( value, false, null, token.ValidTo, token.ValidTo, token.GetClaim("tid").Value, GetAccount(token), null, parameters.Scopes, Guid.Empty)); }
private static void LogMessages(string message) { if (ServiceClientTracing.IsEnabled) { ServiceClientTracing.Information(message); } }
internal static void LogInfo(string message, params object[] parameters) { if (shouldTrace) { ServiceClientTracing.Information(message, parameters); } }
private static void Respond(Func <Uri, MessageAndHttpCode> responseProducer, HttpListenerContext context) { MessageAndHttpCode messageAndCode = responseProducer(context.Request.Url); ServiceClientTracing.Information($"[HttpListenerInterceptor] Processing a response message to the browser. HttpStatus: {messageAndCode.HttpCode}"); switch (messageAndCode.HttpCode) { case HttpStatusCode.Found: context.Response.StatusCode = (int)HttpStatusCode.Found; context.Response.RedirectLocation = messageAndCode.Message; break; case HttpStatusCode.OK: byte[] buffer = System.Text.Encoding.UTF8.GetBytes(messageAndCode.Message); context.Response.ContentLength64 = buffer.Length; context.Response.OutputStream.Write(buffer, 0, buffer.Length); break; default: throw new NotImplementedException("HttpCode not supported" + messageAndCode.HttpCode); } context.Response.OutputStream.Close(); }
/// <summary> /// Creates a public client used for generating tokens. /// </summary> /// <param name="cloudInstance">The cloud instance used for authentication.</param> /// <param name="clientId">Identifier of the client requesting the token.</param> /// <param name="redirectUri">The redirect URI for the client.</param> /// <param name="tenantId">Identifier of the tenant requesting the token.</param> /// <returns>An aptly configured public client.</returns> private static IPublicClientApplication CreatePublicClient( AzureCloudInstance cloudInstance, string clientId = null, string redirectUri = null, string tenantId = null) { PublicClientApplicationBuilder builder = PublicClientApplicationBuilder.Create(clientId); builder = builder.WithAuthority(cloudInstance, tenantId); if (!string.IsNullOrEmpty(redirectUri)) { builder = builder.WithRedirectUri(redirectUri); } if (!string.IsNullOrEmpty(tenantId)) { builder = builder.WithTenantId(tenantId); } IPublicClientApplication client = builder.WithLogging((level, message, pii) => { MgmtSession.Instance.DebugMessages.Enqueue($"[MSAL] {level} {message}"); }).Build(); if (MgmtSession.Instance.TryGetComponent(ComponentKey.TokenCache, out IMgmtTokenCache tokenCache)) { ServiceClientTracing.Information($"[MSAL] Registering the token cache for client {clientId}"); tokenCache.RegisterCache(client); } return(client); }
/// <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); }
/// <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="AuthenticationToken" /> that represents the access token generated as result of a successful authenication. /// </returns> public override async Task <AuthenticationResult> AuthenticateAsync(AuthenticationParameters parameters, CancellationToken cancellationToken = default) { IPublicClientApplication app = GetClient(parameters.Account, parameters.Environment).AsPublicClient(); ServiceClientTracing.Information($"[DeviceCodeAuthenticator] Calling AcquireTokenWithDeviceCode - Scopes: '{string.Join(", ", parameters.Scopes)}'"); return(await app.AcquireTokenWithDeviceCode(parameters.Scopes, deviceCodeResult => { WriteWarning(deviceCodeResult.Message); return Task.CompletedTask; }).ExecuteAsync(cancellationToken).ConfigureAwait(false)); }
/// <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) { IClientApplicationBase app = GetClient(parameters.Account, parameters.Environment); ServiceClientTracing.Information("[SilentAuthenticator] Calling GetAccountsAsync"); IEnumerable <IAccount> accounts = await app.AsPublicClient().GetAccountsAsync().ConfigureAwait(false); ServiceClientTracing.Information($"[SilentAuthenticator] Calling AcquireTokenSilent - Scopes: '{string.Join(",", parameters.Scopes)}', UserId: '{((SilentParameters)parameters).UserId}', Number of accounts: '{accounts.Count()}'"); AuthenticationResult authResult = await app.AsPublicClient().AcquireTokenSilent( parameters.Scopes, accounts.FirstOrDefault(a => a.HomeAccountId.ObjectId.Equals(((SilentParameters)parameters).UserId, StringComparison.InvariantCultureIgnoreCase))) .ExecuteAsync(cancellationToken).ConfigureAwait(false); return(authResult); }
/// <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) { IClientApplicationBase app = GetClient(parameters.Account, parameters.Environment); ServiceClientTracing.Information("[RefreshTokenAuthenticator] Calling GetAccountsAysnc"); IAccount account = await app.GetAccountAsync(parameters.Account.Identifier).ConfigureAwait(false); if (account != null) { ServiceClientTracing.Information($"[RefreshTokenAuthenticator] Calling AcquireTokenSilent - Scopes: '{string.Join(", ", parameters.Scopes)}'"); return(await app.AcquireTokenSilent(parameters.Scopes, account).ExecuteAsync(cancellationToken).ConfigureAwait(false)); } ServiceClientTracing.Information($"[RefreshTokenAuthenticator] Calling AcquireTokenByRefreshToken - Scopes: '{string.Join(", ", parameters.Scopes)}'"); return(await app.AsRefreshTokenClient().AcquireTokenByRefreshToken( parameters.Scopes, parameters.Account.GetProperty(PartnerAccountPropertyType.RefreshToken)).ExecuteAsync(cancellationToken).ConfigureAwait(false)); }
/// <summary> /// Listens for a single request and responds. /// </summary> /// <param name="port">The port where to listen for requests.</param> /// <param name="responseProducer">The function responsible for responding.</param> /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param> /// <returns></returns> public async Task<Uri> ListenToSingleRequestAndRespondAsync(int port, Func<Uri, MessageAndHttpCode> responseProducer, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); HttpListener httpListener = null; try { string urlToListenTo = $"http://localhost:{port}/"; httpListener = new HttpListener(); httpListener.Prefixes.Add(urlToListenTo); httpListener.Start(); ServiceClientTracing.Information($"[HttpListenerInterceptor] Listening for authorization code on {urlToListenTo}"); using (cancellationToken.Register(() => { TryStopListening(httpListener); })) { HttpListenerContext context = await httpListener.GetContextAsync().ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); Respond(responseProducer, context); ServiceClientTracing.Information($"[HttpListenerInterceptor] Received a message on {urlToListenTo}"); // the request URL should now contain the auth code and pkce return context.Request.Url; } } catch (ObjectDisposedException) { cancellationToken.ThrowIfCancellationRequested(); throw; } finally { TryStopListening(httpListener); } }
private IAccount GetAccount(JsonWebToken token) { token.AssertNotNull(nameof(token)); token.TryGetClaim("upn", out Claim claim); if (claim != null) { ServiceClientTracing.Information($"[AccessTokenAuthenticator] The UPN claim value is {claim.Value}"); ServiceClientTracing.Information($"[AccessTokenAuthenticator] Constructing the resource account value based on specified access token"); return(new ResourceAccount( "login.microsoftonline.com", $"{token.GetClaim("oid").Value}.{token.GetClaim("tid")}", token.GetClaim("oid").Value, token.GetClaim("tid").Value, claim.Value)); } ServiceClientTracing.Information("[AccessTokenAuthenticator] The UPN claim is not present in the access token."); return(null); }
/// <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) { JsonWebToken token; string value; if (parameters.Scopes.Contains($"{parameters.Environment.PartnerCenterEndpoint}/user_impersonation")) { value = parameters.Account.GetProperty(PartnerAccountPropertyType.AccessToken); } else { throw new PSInvalidOperationException("This operation is not supported when you connect using an access token. Please connect interactively or using a refresh token."); } token = new JsonWebToken(value); ServiceClientTracing.Information($"[AccessTokenAuthenticator] The specified access token expires at {token.ValidTo}"); if (DateTimeOffset.UtcNow > token.ValidTo) { throw new PartnerException("The access token has expired. Generate a new one and try again."); } await Task.CompletedTask; ServiceClientTracing.Information("[AccessTokenAuthenticator] Constructing the authentication result based on the specified access token"); return(new AuthenticationResult( value, false, null, token.ValidTo, token.ValidTo, token.GetClaim("tid").Value, GetAccount(token), null, parameters.Scopes, Guid.Empty)); }
/// <summary> /// Get The value from the cache /// </summary> /// <param name="requestUri"></param> /// <param name="value"></param> /// <returns></returns> protected virtual bool TryGetFromCache(string requestUri, out T value) { bool result = false; value = null; ICacheable cacheValue; ServiceClientTracing.Information(string.Format(Resources.CacheCheck, requestUri)); if (_cache != null && _cache.TryGetValue(requestUri, out cacheValue)) { ServiceClientTracing.Information(Resources.CacheHit); if (cacheValue.IsExpired()) { _cache.TryRemove(requestUri, out cacheValue); } else { result = true; value = cacheValue as T; } } return(result); }
public IHttpOperations <T> WithHeader(string name, IEnumerable <string> value) { ServiceClientTracing.Information(Resources.HttpClientAddingHeader, name); _headers.TryAdd(name, value); return(this); }
protected async override Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (LogLevel == Level.None) { return(await base.SendAsync(request, cancellationToken)); } ServiceClientTracing.Information("Request: {0} {1}", request.Method, request.RequestUri); if (LogLevel == Level.BodyAndHeaders || LogLevel == Level.Headers) { ServiceClientTracing.Information(" headers:"); foreach (var header in request.Headers) { ServiceClientTracing.Information(" {0} : {1}", header.Key, string.Join(" ", header.Value)); } } if (LogLevel == Level.Body || LogLevel == Level.BodyAndHeaders) { if (request.Content != null) { string content = await request.Content.ReadAsStringAsync(); ServiceClientTracing.Information(" body: " + content); } // TODO: Checking for binary request contents and omitting them } HttpResponseMessage response = await base.SendAsync(request, cancellationToken); if (LogLevel == Level.BodyAndHeaders || LogLevel == Level.Headers) { ServiceClientTracing.Information("Response:"); ServiceClientTracing.Information(" headers:"); foreach (var header in response.Headers) { ServiceClientTracing.Information(" {0} : {1}", header.Key, string.Join(" ", header.Value)); } } if (LogLevel == Level.Body || LogLevel == Level.BodyAndHeaders) { bool isEncoded = IsHeaderPresent(response.Content.Headers, "Content-Encoding"); if (!isEncoded && response.Content != null) { string content = await response.Content.ReadAsStringAsync(); string contentType = GetHeader(response.Content.Headers, "Content-Type"); if (contentType != null && contentType.Contains("application/json")) { try { dynamic parsedJson = JsonConvert.DeserializeObject(content); content = JsonConvert.SerializeObject(parsedJson, Formatting.Indented); } catch (Exception) { /*ignore and print below as it is*/ } } ServiceClientTracing.Information(content); } // TODO: Checking for binary response content and omitting them if (isEncoded) { ServiceClientTracing.Information(" body: <encoded body omitted>"); } } return(response); }
/// <summary> /// Registers the token cache with client application. /// </summary> /// <param name="client">The client application to be used when registering the token cache.</param> public override void RegisterCache(IClientApplicationBase client) { ServiceClientTracing.Information("Registering the persistent token cache."); base.RegisterCache(client); }