/// <summary> /// Creates an OAuth client for the bot. /// </summary> /// <param name="turnContext">The context object for the current turn.</param> /// <returns>An OAuth client for the bot.</returns> protected async Task <OAuthClient> CreateOAuthApiClientAsync(ITurnContext turnContext) { var client = turnContext.TurnState.Get <IConnectorClient>() as ConnectorClient; if (client == null) { throw new ArgumentNullException("CreateOAuthApiClient: OAuth requires a valid ConnectorClient instance"); } if (!OAuthClient.EmulateOAuthCards && string.Equals(turnContext.Activity.ChannelId, "emulator", StringComparison.InvariantCultureIgnoreCase) && (await _credentialProvider.IsAuthenticationDisabledAsync().ConfigureAwait(false))) { OAuthClient.EmulateOAuthCards = true; } if (OAuthClient.EmulateOAuthCards) { var oauthClient = new OAuthClient(client, turnContext.Activity.ServiceUrl); // do not await task - we want this to run in the background var task = Task.Run(() => oauthClient.SendEmulateOAuthCardsAsync(OAuthClient.EmulateOAuthCards)); return(oauthClient); } return(new OAuthClient(client, OAuthClient.OAuthEndpoint)); }
/// <summary> /// Authenticates the request and adds the activity's <see cref="Activity.ServiceUrl"/> /// to the set of trusted URLs. /// </summary> /// <param name="activity">The activity.</param> /// <param name="authHeader">The authentication header.</param> /// <param name="credentials">The bot's credential provider.</param> /// <param name="authConfig">The optional authentication configuration.</param> /// <param name="httpClient">The HTTP client.</param> /// <returns>A task that represents the work queued to execute.</returns> /// <remarks>If the task completes successfully, the result contains the claims-based /// identity for the request.</remarks> public static async Task <ClaimsIdentity> AuthenticateRequest(IActivity activity, string authHeader, ICredentialProvider credentials, AuthenticationConfiguration authConfig, HttpClient httpClient = null) { if (authConfig == null) { throw new ArgumentNullException(nameof(authConfig)); } if (string.IsNullOrWhiteSpace(authHeader)) { var isAuthDisabled = await credentials.IsAuthenticationDisabledAsync().ConfigureAwait(false); if (isAuthDisabled) { // In the scenario where Auth is disabled, we still want to have the // IsAuthenticated flag set in the ClaimsIdentity. To do this requires // adding in an empty claim. return(new ClaimsIdentity(new List <Claim>(), "anonymous")); } // No Auth Header. Auth is required. Request is not authorized. throw new UnauthorizedAccessException(); } var claimsIdentity = await ValidateAuthHeader(authHeader, credentials, activity.ChannelId, authConfig, activity.ServiceUrl, httpClient ?? _httpClient).ConfigureAwait(false); MicrosoftAppCredentials.TrustServiceUrl(activity.ServiceUrl); return(claimsIdentity); }
/// <summary> /// Creates an OAuth client for the bot. /// </summary> /// <param name="turnContext">The context object for the current turn.</param> /// <returns>An OAuth client for the bot.</returns> protected virtual async Task <OAuthClient> CreateOAuthApiClientAsync(ITurnContext turnContext) { if (!OAuthClientConfig.EmulateOAuthCards && string.Equals(turnContext.Activity.ChannelId, "emulator", StringComparison.InvariantCultureIgnoreCase) && (await _credentialProvider.IsAuthenticationDisabledAsync().ConfigureAwait(false))) { OAuthClientConfig.EmulateOAuthCards = true; } var connectorClient = turnContext.TurnState.Get <IConnectorClient>(); if (connectorClient == null) { throw new InvalidOperationException("An IConnectorClient is required in TurnState for this operation."); } if (OAuthClientConfig.EmulateOAuthCards) { // do not await task - we want this to run in the background var oauthClient = new OAuthClient(new Uri(turnContext.Activity.ServiceUrl), connectorClient.Credentials); var task = Task.Run(() => OAuthClientConfig.SendEmulateOAuthCardsAsync(oauthClient, OAuthClientConfig.EmulateOAuthCards)); return(oauthClient); } return(new OAuthClient(new Uri(OAuthClientConfig.OAuthEndpoint), connectorClient.Credentials)); }
public static async Task <ClaimsIdentity> ValidateAuthHeader(string authHeader, ICredentialProvider credentials, string serviceUrl, HttpClient httpClient = null) { if (string.IsNullOrWhiteSpace(authHeader)) { bool isAuthDisabled = await credentials.IsAuthenticationDisabledAsync(); if (isAuthDisabled) { // In the scenario where Auth is disabled, we still want to have the // IsAuthenticated flag set in the ClaimsIdentity. To do this requires // adding in an empty claim. ClaimsIdentity anonymousAuthenticatedIdentity = new ClaimsIdentity(new List <Claim>(), "anonymous"); return(anonymousAuthenticatedIdentity); } // No Auth Header. Auth is required. Request is not authorized. throw new UnauthorizedAccessException(); } bool usingEmulator = EmulatorValidation.IsTokenFromEmulator(authHeader); if (usingEmulator) { return(await EmulatorValidation.AuthenticateEmulatorToken(authHeader, credentials, httpClient ?? _httpClient)); } else { return(await ChannelValidation.AuthenticateChannelToken(authHeader, credentials, serviceUrl, httpClient ?? _httpClient)); } }
protected async Task <bool> TrySetEmulatingOAuthCards(ITurnContext turnContext) { if (!_isEmulatingOAuthCards && string.Equals(turnContext.Activity.ChannelId, "emulator", StringComparison.InvariantCultureIgnoreCase) && (await _credentialProvider.IsAuthenticationDisabledAsync())) { _isEmulatingOAuthCards = true; } return(_isEmulatingOAuthCards); }
public async Task SkillPingAsync() { if (_credentialProvider != null && !await _credentialProvider.IsAuthenticationDisabledAsync().ConfigureAwait(false)) { await _authenticator.AuthenticateAsync(Request, Response).ConfigureAwait(false); } else { Response.StatusCode = (int)HttpStatusCode.OK; } }
public async override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { if (!await Options.IsAuthenticationDisabledAsync()) { var activities = GetActivities(context); foreach (var activity in activities) { MicrosoftAppCredentials.TrustServiceUrl(activity.ServiceUrl); } } await next(); }
/// <summary> /// Validates the security tokens required by the Bot Framework Protocol. Throws on any exceptions. /// </summary> /// <param name="activity">The incoming Activity from the Bot Framework or the Emulator</param> /// <param name="authHeader">The Bearer token included as part of the request</param> /// <param name="credentials">The set of valid credentials, such as the Bot Application ID</param> /// <param name="httpClient">Validating an Activity requires validating the claimset on the security token. This /// validation may require outbound calls for Endorsement validation and other checks. Those calls are made to /// TLS services, which are (latency wise) expensive resources. The httpClient passed in here, if shared by the layers /// above from call to call, enables connection reuse which is a significant performance and resource improvement.</param> /// <returns>Task tracking operation</returns> public static async Task AssertValidActivity(IActivity activity, string authHeader, ICredentialProvider credentials, HttpClient httpClient = null) { if (string.IsNullOrWhiteSpace(authHeader)) { // No auth header was sent. We might be on the anonymous code path. bool isAuthDisabled = await credentials.IsAuthenticationDisabledAsync(); if (isAuthDisabled) { // We are on the anonymous code path. return; } } // Go through the standard authentication path. await JwtTokenValidation.AuthenticateRequest(activity, authHeader, credentials, httpClient ?? _httpClient); }
/// <summary> /// Validates the security tokens required by the Bot Framework Protocol. Throws on any exceptions. /// </summary> /// <param name="activity">The incoming Activity from the Bot Framework or the Emulator</param> /// <param name="authHeader">The Bearer token included as part of the request</param> /// <param name="credentials">The set of valid credentials, such as the Bot Application ID</param> /// <param name="httpClient">Validating an Activity requires validating the claimset on the security token. This /// validation may require outbound calls for Endorsement validation and other checks. Those calls are made to /// TLS services, which are (latency wise) expensive resources. The httpClient passed in here, if shared by the layers /// above from call to call, enables connection reuse which is a significant performance and resource improvement.</param> /// <returns>Nothing</returns> public static async Task AssertValidActivity(Activity activity, string authHeader, ICredentialProvider credentials, HttpClient httpClient) { if (string.IsNullOrWhiteSpace(authHeader)) { // No auth header was sent. We might be on the anonymous code path. bool isAuthDisabled = await credentials.IsAuthenticationDisabledAsync(); if (isAuthDisabled) { // We are on the anonymous code path. return; } } // Go through the standard authentication path. await JwtTokenValidation.ValidateAuthHeader(authHeader, credentials, activity.ServiceUrl, httpClient); // On the standard Auth path, we need to trust the URL that was incoming. MicrosoftAppCredentials.TrustServiceUrl(activity.ServiceUrl); }
private async Task <ClaimsIdentity> AuthenticateAsync(string authHeader) { if (string.IsNullOrWhiteSpace(authHeader)) { var isAuthDisabled = await _credentialProvider.IsAuthenticationDisabledAsync().ConfigureAwait(false); if (isAuthDisabled) { // In the scenario where auth is disabled, we still want to have the // IsAuthenticated flag set in the ClaimsIdentity. To do this requires // adding in an empty claim. return(new ClaimsIdentity(new List <Claim>(), "anonymous")); } // No auth header. Auth is required. Request is not authorized. throw new UnauthorizedAccessException(); } return(await JwtTokenValidation.ValidateAuthHeader(authHeader, _credentialProvider, _channelProvider, "unknown", _authConfiguration).ConfigureAwait(false)); }
/// <summary> /// Helper to authenticate the header. /// </summary> /// <remarks> /// This code is very similar to the code in <see cref="JwtTokenValidation.AuthenticateRequest(IActivity, string, ICredentialProvider, IChannelProvider, AuthenticationConfiguration, HttpClient)"/>, /// we should move this code somewhere in that library when we refactor auth, for now we keep it private to avoid adding more public static /// functions that we will need to deprecate later. /// </remarks> /// <param name="authHeader">The auth header containing JWT token.</param> /// <param name="cancellationToken">A cancellation token.</param> /// <returns>A <see cref="ClaimsIdentity"/> representing the claims associated with given header.</returns> internal override async Task <ClaimsIdentity> AuthenticateAsync(string authHeader, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(authHeader)) { var isAuthDisabled = await _credentialProvider.IsAuthenticationDisabledAsync().ConfigureAwait(false); if (!isAuthDisabled) { // No auth header. Auth is required. Request is not authorized. throw new UnauthorizedAccessException(); } // In the scenario where auth is disabled, we still want to have the // IsAuthenticated flag set in the ClaimsIdentity. // To do this requires adding in an empty claim. // Since ChannelServiceHandler calls are always a skill callback call, we set the skill claim too. return(SkillValidation.CreateAnonymousSkillClaim()); } // Validate the header and extract claims. return(await JwtTokenValidation.ValidateAuthHeader(authHeader, _credentialProvider, ChannelProvider, "unknown", _authConfiguration).ConfigureAwait(false)); }
public async Task ProcessAsync(HttpRequest httpRequest, HttpResponse httpResponse, IBot bot, CancellationToken cancellationToken = default(CancellationToken)) { if (httpRequest == null) { throw new ArgumentNullException(nameof(httpRequest)); } if (httpResponse == null) { throw new ArgumentNullException(nameof(httpResponse)); } if (bot == null) { throw new ArgumentNullException(nameof(bot)); } if (!httpRequest.HttpContext.WebSockets.IsWebSocketRequest) { httpResponse.StatusCode = (int)HttpStatusCode.BadRequest; await httpResponse.WriteAsync("Upgrade to WebSocket required.").ConfigureAwait(false); return; } ClaimsIdentity claims; // only perform auth when it's enabled if (_credentialProvider != null && !await _credentialProvider.IsAuthenticationDisabledAsync().ConfigureAwait(false)) { claims = await _authenticator.AuthenticateAsync(httpRequest, httpResponse).ConfigureAwait(false); } else { claims = new ClaimsIdentity(new List <Claim>(), "anonymous"); } await CreateWebSocketConnectionAsync(claims, httpRequest.HttpContext, bot).ConfigureAwait(false); }
/// <summary> /// Authenticates the request and adds the activity's <see cref="Activity.ServiceUrl"/> /// to the set of trusted URLs. /// </summary> /// <param name="activity">The activity.</param> /// <param name="authHeader">The authentication header.</param> /// <param name="credentials">The bot's credential provider.</param> /// <param name="provider">The bot's channel service provider.</param> /// <param name="authConfig">The optional authentication configuration.</param> /// <param name="httpClient">The HTTP client.</param> /// <returns>A task that represents the work queued to execute.</returns> /// <remarks>If the task completes successfully, the result contains the claims-based /// identity for the request.</remarks> #pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat) public static async Task <ClaimsIdentity> AuthenticateRequest(IActivity activity, string authHeader, ICredentialProvider credentials, IChannelProvider provider, AuthenticationConfiguration authConfig, HttpClient httpClient = null) #pragma warning restore UseAsyncSuffix // Use Async suffix { if (authConfig == null) { throw new ArgumentNullException(nameof(authConfig)); } if (string.IsNullOrWhiteSpace(authHeader)) { var isAuthDisabled = await credentials.IsAuthenticationDisabledAsync().ConfigureAwait(false); if (!isAuthDisabled) { // No Auth Header and Auth is required. Request is not authorized. throw new UnauthorizedAccessException(); } // Check if the activity is for a skill call and is coming from the Emulator. if (activity.ChannelId == Channels.Emulator && activity.Recipient?.Role == RoleTypes.Skill) { // Return an anonymous claim with an anonymous skill AppId return(SkillValidation.CreateAnonymousSkillClaim()); } // In the scenario where Auth is disabled, we still want to have the // IsAuthenticated flag set in the ClaimsIdentity. To do this requires // adding in an empty claim. return(new ClaimsIdentity(new List <Claim>(), AuthenticationConstants.AnonymousAuthType)); } // Validate the header and extract claims. var claimsIdentity = await ValidateAuthHeader(authHeader, credentials, provider, activity.ChannelId, authConfig, activity.ServiceUrl, httpClient ?? _httpClient).ConfigureAwait(false); AppCredentials.TrustServiceUrl(activity.ServiceUrl); return(claimsIdentity); }
/// <summary> /// Process the initial request to establish a long lived connection via a streaming server. /// </summary> /// <param name="onTurnError"> The function to execute on turn errors.</param> /// <param name="middlewareSet">The set of middleware to perform on each turn.</param> /// <param name="httpRequest">The connection request.</param> /// <param name="httpResponse">The response sent on error or connection termination.</param> /// <param name="cancellationToken">Optional cancellation token.</param> /// <returns>Returns on task completion.</returns> internal async Task ProcessAsync(Func <ITurnContext, Exception, Task> onTurnError, List <Builder.IMiddleware> middlewareSet, HttpRequest httpRequest, HttpResponse httpResponse, CancellationToken cancellationToken = default(CancellationToken)) { if (httpRequest == null) { throw new ArgumentNullException(nameof(httpRequest)); } if (httpResponse == null) { throw new ArgumentNullException(nameof(httpResponse)); } if (!httpRequest.HttpContext.WebSockets.IsWebSocketRequest) { httpRequest.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; await httpRequest.HttpContext.Response.WriteAsync("Upgrade to WebSocket is required.").ConfigureAwait(false); return; } try { if (!await _credentialProvider.IsAuthenticationDisabledAsync().ConfigureAwait(false)) { var authHeader = httpRequest.Headers.Where(x => x.Key.ToLower() == AuthHeaderName).FirstOrDefault().Value.FirstOrDefault(); var channelId = httpRequest.Headers.Where(x => x.Key.ToLower() == ChannelIdHeaderName).FirstOrDefault().Value.FirstOrDefault(); if (string.IsNullOrWhiteSpace(authHeader)) { await MissingAuthHeaderHelperAsync(AuthHeaderName, httpRequest).ConfigureAwait(false); return; } if (string.IsNullOrWhiteSpace(channelId)) { await MissingAuthHeaderHelperAsync(ChannelIdHeaderName, httpRequest).ConfigureAwait(false); return; } var claimsIdentity = await JwtTokenValidation.ValidateAuthHeader(authHeader, _credentialProvider, _channelProvider, channelId).ConfigureAwait(false); if (!claimsIdentity.IsAuthenticated) { httpRequest.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; return; } } } catch (Exception ex) { httpRequest.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; await httpRequest.HttpContext.Response.WriteAsync("Error while attempting to authorize connection.").ConfigureAwait(false); throw ex; } await CreateStreamingServerConnectionAsync(onTurnError, middlewareSet, httpRequest.HttpContext).ConfigureAwait(false); }