diff --git a/samples/apps/copilot-chat-app/importdocument/Program.cs b/samples/apps/copilot-chat-app/importdocument/Program.cs
index 1f98db7d3fcb..cd3e12f7ad15 100644
--- a/samples/apps/copilot-chat-app/importdocument/Program.cs
+++ b/samples/apps/copilot-chat-app/importdocument/Program.cs
@@ -115,10 +115,8 @@ private static async Task UploadFileAsync(FileInfo file, Config config, Guid cha
{
Console.WriteLine($"Successfully acquired User ID. Continuing...");
using var chatScopeContent = new StringContent("Chat");
- using var userIdContent = new StringContent(userId);
using var chatCollectionIdContent = new StringContent(chatCollectionId.ToString());
formContent.Add(chatScopeContent, "documentScope");
- formContent.Add(userIdContent, "userId");
formContent.Add(chatCollectionIdContent, "chatId");
// Calling UploadAsync here to make sure disposable objects are still in scope.
diff --git a/samples/apps/copilot-chat-app/webapi/Auth/ApiKeyAuthenticationHandler.cs b/samples/apps/copilot-chat-app/webapi/Auth/ApiKeyAuthenticationHandler.cs
deleted file mode 100644
index 550f862b1b0e..000000000000
--- a/samples/apps/copilot-chat-app/webapi/Auth/ApiKeyAuthenticationHandler.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-using System.Security.Claims;
-using System.Text.Encodings.Web;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using Microsoft.Extensions.Primitives;
-
-namespace SemanticKernel.Service.Auth;
-
-///
-/// Class implementing API key authentication.
-///
-public class ApiKeyAuthenticationHandler : AuthenticationHandler
-{
- public const string AuthenticationScheme = "ApiKey";
- public const string ApiKeyHeaderName = "x-sk-api-key";
-
- ///
- /// Constructor
- ///
- public ApiKeyAuthenticationHandler(
- IOptionsMonitor options,
- ILoggerFactory loggerFactory,
- UrlEncoder encoder,
- ISystemClock clock) : base(options, loggerFactory, encoder, clock)
- {
- }
-
- protected override Task HandleAuthenticateAsync()
- {
- this.Logger.LogInformation("Checking API key");
-
- if (string.IsNullOrWhiteSpace(this.Options.ApiKey))
- {
- const string ErrorMessage = "API key not configured on server";
-
- this.Logger.LogError(ErrorMessage);
-
- return Task.FromResult(AuthenticateResult.Fail(ErrorMessage));
- }
-
- if (!this.Request.Headers.TryGetValue(ApiKeyHeaderName, out StringValues apiKeyFromHeader))
- {
- const string WarningMessage = "No API key provided";
-
- this.Logger.LogWarning(WarningMessage);
-
- return Task.FromResult(AuthenticateResult.Fail(WarningMessage));
- }
-
- if (!string.Equals(apiKeyFromHeader, this.Options.ApiKey, StringComparison.Ordinal))
- {
- const string WarningMessage = "Incorrect API key";
-
- this.Logger.LogWarning(WarningMessage);
-
- return Task.FromResult(AuthenticateResult.Fail(WarningMessage));
- }
-
- var principal = new ClaimsPrincipal(new ClaimsIdentity(AuthenticationScheme));
- var ticket = new AuthenticationTicket(principal, this.Scheme.Name);
-
- this.Logger.LogInformation("Request authorized by API key");
-
- return Task.FromResult(AuthenticateResult.Success(ticket));
- }
-}
diff --git a/samples/apps/copilot-chat-app/webapi/Auth/ApiKeyAuthenticationSchemeOptions.cs b/samples/apps/copilot-chat-app/webapi/Auth/ApiKeyAuthenticationSchemeOptions.cs
deleted file mode 100644
index 33e8943f2a0a..000000000000
--- a/samples/apps/copilot-chat-app/webapi/Auth/ApiKeyAuthenticationSchemeOptions.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using Microsoft.AspNetCore.Authentication;
-
-namespace SemanticKernel.Service.Auth;
-
-///
-/// Options for API key authentication.
-///
-public class ApiKeyAuthenticationSchemeOptions : AuthenticationSchemeOptions
-{
- ///
- /// The API key against which to authenticate.
- ///
- public string? ApiKey { get; set; }
-}
diff --git a/samples/apps/copilot-chat-app/webapi/Auth/AuthInfo.cs b/samples/apps/copilot-chat-app/webapi/Auth/AuthInfo.cs
new file mode 100644
index 000000000000..ef4323638b12
--- /dev/null
+++ b/samples/apps/copilot-chat-app/webapi/Auth/AuthInfo.cs
@@ -0,0 +1,61 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using Azure.Identity;
+using Microsoft.AspNetCore.Http;
+using Microsoft.IdentityModel.JsonWebTokens;
+
+namespace SemanticKernel.Service.Auth;
+
+///
+/// Class which provides validated security information for use in controllers.
+///
+public class AuthInfo : IAuthInfo
+{
+ private record struct AuthData(
+ string UserId,
+ string UserName);
+
+ private readonly Lazy _data;
+
+ public AuthInfo(IHttpContextAccessor httpContextAccessor)
+ {
+ this._data = new Lazy(() =>
+ {
+ var user = httpContextAccessor.HttpContext?.User;
+ if (user is null)
+ {
+ throw new InvalidOperationException("HttpContext must be present to inspect auth info.");
+ }
+ var userIdClaim = user.FindFirst("oid")
+ ?? user.FindFirst(JwtRegisteredClaimNames.Sub);
+
+ if (userIdClaim is null)
+ {
+ throw new CredentialUnavailableException("User Id was not present in the request token.");
+ }
+
+ var userNameClaim = user.FindFirst(JwtRegisteredClaimNames.Name);
+ if (userNameClaim is null)
+ {
+ throw new CredentialUnavailableException("User name was not present in the request token.");
+ }
+
+ return new AuthData
+ {
+ UserId = userIdClaim.Value,
+ UserName = userNameClaim.Value,
+ };
+ }, isThreadSafe: false);
+ }
+
+ ///
+ /// The authenticated user's unique ID.
+ ///
+ public string UserId => this._data.Value.UserId;
+
+ ///
+ /// The authenticated user's name.
+ ///
+ public string Name => this._data.Value.UserName;
+}
diff --git a/samples/apps/copilot-chat-app/webapi/Auth/IAuthInfo.cs b/samples/apps/copilot-chat-app/webapi/Auth/IAuthInfo.cs
new file mode 100644
index 000000000000..7af794a8843a
--- /dev/null
+++ b/samples/apps/copilot-chat-app/webapi/Auth/IAuthInfo.cs
@@ -0,0 +1,16 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+namespace SemanticKernel.Service.Auth;
+
+public interface IAuthInfo
+{
+ ///
+ /// The authenticated user's unique ID.
+ ///
+ public string UserId { get; }
+
+ ///
+ /// The authenticated user's name.
+ ///
+ public string Name { get; }
+}
diff --git a/samples/apps/copilot-chat-app/webapi/Auth/PassThroughAuthenticationHandler.cs b/samples/apps/copilot-chat-app/webapi/Auth/PassThroughAuthenticationHandler.cs
index e12320b898ec..b2f37dfcc8d1 100644
--- a/samples/apps/copilot-chat-app/webapi/Auth/PassThroughAuthenticationHandler.cs
+++ b/samples/apps/copilot-chat-app/webapi/Auth/PassThroughAuthenticationHandler.cs
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
+using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
@@ -15,6 +16,8 @@ namespace SemanticKernel.Service.Auth;
public class PassThroughAuthenticationHandler : AuthenticationHandler
{
public const string AuthenticationScheme = "PassThrough";
+ private const string DefaultUserId = "c05c61eb-65e4-4223-915a-fe72b0c9ece1";
+ private const string DefaultUserName = "Default User";
///
/// Constructor
@@ -30,9 +33,11 @@ public PassThroughAuthenticationHandler(
protected override Task HandleAuthenticateAsync()
{
this.Logger.LogInformation("Allowing request to pass through");
-
- var principal = new ClaimsPrincipal(new ClaimsIdentity(AuthenticationScheme));
- var ticket = new AuthenticationTicket(principal, this.Scheme.Name);
+ Claim userIdClaim = new(JwtRegisteredClaimNames.Sub, DefaultUserId);
+ Claim nameClaim = new(JwtRegisteredClaimNames.Name, DefaultUserName);
+ ClaimsIdentity identity = new(new Claim[] { userIdClaim, nameClaim }, AuthenticationScheme);
+ ClaimsPrincipal principal = new(identity);
+ AuthenticationTicket ticket = new(principal, this.Scheme.Name);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
diff --git a/samples/apps/copilot-chat-app/webapi/Controllers/ProbeController.cs b/samples/apps/copilot-chat-app/webapi/Controllers/ProbeController.cs
deleted file mode 100644
index 2346e353ba0a..000000000000
--- a/samples/apps/copilot-chat-app/webapi/Controllers/ProbeController.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-// TODO: replace this controller with a better health check:
-// https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-7.0
-
-using Microsoft.AspNetCore.Mvc;
-
-namespace SemanticKernel.Service.Controllers;
-
-[Route("[controller]")]
-[ApiController]
-public class ProbeController : ControllerBase
-{
- [HttpGet]
- public ActionResult Get()
- {
- return "Semantic Kernel service up and running";
- }
-}
diff --git a/samples/apps/copilot-chat-app/webapi/Controllers/SemanticKernelController.cs b/samples/apps/copilot-chat-app/webapi/Controllers/SemanticKernelController.cs
index b56f08f4a785..63c0a1350143 100644
--- a/samples/apps/copilot-chat-app/webapi/Controllers/SemanticKernelController.cs
+++ b/samples/apps/copilot-chat-app/webapi/Controllers/SemanticKernelController.cs
@@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
@@ -11,7 +10,10 @@
using Microsoft.SemanticKernel.AI;
using Microsoft.SemanticKernel.Orchestration;
using Microsoft.SemanticKernel.SkillDefinition;
+using SemanticKernel.Service.Auth;
+using SemanticKernel.Service.CopilotChat.Storage;
using SemanticKernel.Service.Models;
+using SemanticKernel.Service.Utilities;
namespace SemanticKernel.Service.Controllers;
@@ -34,11 +36,13 @@ public SemanticKernelController(ILogger logger)
/// and attempt to invoke the function with the given name.
///
/// Semantic kernel obtained through dependency injection
+ /// Converter to use for converting Asks.
+ /// Storage for chat sessions.
+ /// Authenticated info about the user for the current request.
/// Prompt along with its parameters
/// Skill in which function to invoke resides
/// Name of function to invoke
/// Results consisting of text generated by invoked function along with the variable in the Semantic Kernel that generated it
- [Authorize]
[Route("skills/{skillName}/functions/{functionName}/invoke")]
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
@@ -46,22 +50,31 @@ public SemanticKernelController(ILogger logger)
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task> InvokeFunctionAsync(
[FromServices] IKernel kernel,
+ [FromServices] AskConverter askConverter,
+ [FromServices] ChatSessionRepository chatSessionRepository,
+ [FromServices] IAuthInfo authInfo,
[FromBody] Ask ask,
string skillName, string functionName)
{
this._logger.LogDebug("Received call to invoke {SkillName}/{FunctionName}", skillName, functionName);
- if (string.IsNullOrWhiteSpace(ask.Input))
+ const string chatIdKey = "chatId";
+ var chatIdFromContext = ask.Variables.FirstOrDefault(x => x.Key == chatIdKey);
+ if (chatIdFromContext.Key is chatIdKey)
{
- return this.BadRequest("Input is required.");
+ var chat = await chatSessionRepository.FindByIdAsync(chatIdFromContext.Value);
+ if (chat == null)
+ {
+ return this.NotFound("Failed to find chat session for the chatId specified in variables.");
+ }
+ if (chat.UserId != authInfo.UserId)
+ {
+ return this.Unauthorized("User does not have access to the chatId specified in variables.");
+ }
}
// Put ask's variables in the context we will use.
- var contextVariables = new ContextVariables(ask.Input);
- foreach (var input in ask.Variables)
- {
- contextVariables.Set(input.Key, input.Value);
- }
+ var contextVariables = askConverter.GetContextVariables(ask);
// Get the function to invoke
ISKFunction? function = null;
diff --git a/samples/apps/copilot-chat-app/webapi/CopilotChat/Auth/AuthPolicyName.cs b/samples/apps/copilot-chat-app/webapi/CopilotChat/Auth/AuthPolicyName.cs
new file mode 100644
index 000000000000..751b4a782d00
--- /dev/null
+++ b/samples/apps/copilot-chat-app/webapi/CopilotChat/Auth/AuthPolicyName.cs
@@ -0,0 +1,11 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+namespace SemanticKernel.Service.CopilotChat.Auth;
+
+///
+/// Holds the policy names for custom authorization policies.
+///
+public static class AuthPolicyName
+{
+ public const string RequireChatOwner = "RequireChatOwner";
+}
diff --git a/samples/apps/copilot-chat-app/webapi/CopilotChat/Auth/ChatOwnerAuthorizationHandler.cs b/samples/apps/copilot-chat-app/webapi/CopilotChat/Auth/ChatOwnerAuthorizationHandler.cs
new file mode 100644
index 000000000000..0ecbc64c8d07
--- /dev/null
+++ b/samples/apps/copilot-chat-app/webapi/CopilotChat/Auth/ChatOwnerAuthorizationHandler.cs
@@ -0,0 +1,57 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using SemanticKernel.Service.Auth;
+using SemanticKernel.Service.CopilotChat.Storage;
+
+namespace SemanticKernel.Service.CopilotChat.Auth;
+
+///
+/// Class implementing "authorization" that validates the user has access to a chat.
+///
+public class ChatOwnerAuthorizationHandler : AuthorizationHandler
+{
+ private readonly IAuthInfo _authInfo;
+ private readonly ChatSessionRepository _chatSessionRepository;
+
+ ///
+ /// Constructor
+ ///
+ public ChatOwnerAuthorizationHandler(
+ IAuthInfo authInfo,
+ ChatSessionRepository chatSessionRepository) : base()
+ {
+ this._authInfo = authInfo;
+ this._chatSessionRepository = chatSessionRepository;
+ }
+
+ protected override async Task HandleRequirementAsync(
+ AuthorizationHandlerContext context,
+ ChatOwnerRequirement requirement,
+ HttpContext resource)
+ {
+ string? chatId = resource.GetRouteValue("chatId")?.ToString();
+ if (chatId == null)
+ {
+ // delegate to downstream validation
+ context.Succeed(requirement);
+ return;
+ }
+
+ var session = await this._chatSessionRepository.FindByIdAsync(chatId);
+ if (session == null)
+ {
+ // delegate to downstream validation
+ context.Succeed(requirement);
+ return;
+ }
+
+ if (session.UserId != this._authInfo.UserId)
+ {
+ context.Fail(new AuthorizationFailureReason(this, "User does not have access to the requested chat."));
+ }
+ }
+}
diff --git a/samples/apps/copilot-chat-app/webapi/CopilotChat/Auth/ChatOwnerRequirement.cs b/samples/apps/copilot-chat-app/webapi/CopilotChat/Auth/ChatOwnerRequirement.cs
new file mode 100644
index 000000000000..6d931c8baebf
--- /dev/null
+++ b/samples/apps/copilot-chat-app/webapi/CopilotChat/Auth/ChatOwnerRequirement.cs
@@ -0,0 +1,12 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.AspNetCore.Authorization;
+
+namespace SemanticKernel.Service.CopilotChat.Auth;
+
+///
+/// Used to require the chat to be owned by the authenticated user.
+///
+public class ChatOwnerRequirement : IAuthorizationRequirement
+{
+}
diff --git a/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/BotController.cs b/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/BotController.cs
index e08b2d4cf214..bd9c8fb98765 100644
--- a/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/BotController.cs
+++ b/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/BotController.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
@@ -13,6 +12,8 @@
using Microsoft.Extensions.Options;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Memory;
+using SemanticKernel.Service.Auth;
+using SemanticKernel.Service.CopilotChat.Auth;
using SemanticKernel.Service.CopilotChat.Models;
using SemanticKernel.Service.CopilotChat.Options;
using SemanticKernel.Service.CopilotChat.Storage;
@@ -71,11 +72,10 @@ public BotController(
/// Upload a bot.
///
/// The Semantic Kernel instance.
- /// The user id.
+ /// The auth info instance.
/// The bot object from the message body
/// The cancellation token.
/// The HTTP action result with new chat session object.
- [Authorize]
[HttpPost]
[Route("bot/upload")]
[ProducesResponseType(StatusCodes.Status201Created)]
@@ -83,11 +83,10 @@ public BotController(
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task> UploadAsync(
[FromServices] IKernel kernel,
- [FromQuery] string userId,
+ [FromServices] IAuthInfo authInfo,
[FromBody] Bot bot,
CancellationToken cancellationToken)
{
- // TODO: We should get userId from server context instead of from request for privacy/security reasons when support multiple users.
this._logger.LogDebug("Received call to upload a bot");
if (!IsBotCompatible(
@@ -110,7 +109,7 @@ public async Task> UploadAsync(
// Upload chat history into chat repository and embeddings into memory.
// 1. Create a new chat and get the chat id.
- newChat = new ChatSession(userId, chatTitle);
+ newChat = new ChatSession(authInfo.UserId, chatTitle);
await this._chatRepository.CreateAsync(newChat);
chatId = newChat.Id;
@@ -144,21 +143,21 @@ public async Task> UploadAsync(
/// The Semantic Kernel instance.
/// The chat id to be downloaded.
/// The serialized Bot object of the chat id.
- [Authorize]
[HttpGet]
[ActionName("DownloadAsync")]
[Route("bot/download/{chatId:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task> DownloadAsync(
+ [Authorize(Policy = AuthPolicyName.RequireChatOwner)]
+ public async Task> DownloadAsync(
[FromServices] IKernel kernel,
Guid chatId)
{
this._logger.LogDebug("Received call to download a bot");
var memory = await this.CreateBotAsync(kernel: kernel, chatId: chatId);
- return JsonSerializer.Serialize(memory);
+ return this.Ok(memory);
}
///
diff --git a/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/ChatController.cs b/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/ChatController.cs
index 55767805d2ed..e16631a6dd46 100644
--- a/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/ChatController.cs
+++ b/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/ChatController.cs
@@ -7,7 +7,6 @@
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
-using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
@@ -21,9 +20,12 @@
using Microsoft.SemanticKernel.Skills.MsGraph.Connectors;
using Microsoft.SemanticKernel.Skills.MsGraph.Connectors.Client;
using Microsoft.SemanticKernel.Skills.OpenAPI.Authentication;
+using SemanticKernel.Service.Auth;
using SemanticKernel.Service.CopilotChat.Models;
using SemanticKernel.Service.CopilotChat.Skills.ChatSkills;
+using SemanticKernel.Service.CopilotChat.Storage;
using SemanticKernel.Service.Models;
+using SemanticKernel.Service.Utilities;
namespace SemanticKernel.Service.CopilotChat.Controllers;
@@ -50,10 +52,12 @@ public ChatController(ILogger logger)
/// Semantic kernel obtained through dependency injection.
/// Planner to use to create function sequences.
/// Options for the planner.
+ /// Converter to use for converting Asks.
+ /// Storage for chat sessions.
+ /// Authenticated info about the user for the current request.
/// Prompt along with its parameters.
/// Authentication headers to connect to OpenAPI Skills.
/// Results containing the response from the model.
- [Authorize]
[Route("chat")]
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
@@ -62,18 +66,31 @@ public ChatController(ILogger logger)
public async Task ChatAsync(
[FromServices] IKernel kernel,
[FromServices] CopilotChatPlanner planner,
- [FromBody] Ask ask,
+ [FromServices] AskConverter askConverter,
+ [FromServices] ChatSessionRepository chatSessionRepository,
+ [FromServices] IAuthInfo authInfo,
+ [FromBody] ChatAsk ask,
[FromHeader] OpenApiSkillsAuthHeaders openApiSkillsAuthHeaders)
{
this._logger.LogDebug("Chat request received.");
-
- // Put ask's variables in the context we will use.
- var contextVariables = new ContextVariables(ask.Input);
- foreach (var input in ask.Variables)
+ const string chatIdKey = "chatId";
+ var chatIdFromContext = ask.Variables.FirstOrDefault(x => x.Key == chatIdKey);
+ if (chatIdFromContext.Key is chatIdKey)
{
- contextVariables.Set(input.Key, input.Value);
+ var chat = await chatSessionRepository.FindByIdAsync(chatIdFromContext.Value);
+ if (chat == null)
+ {
+ return this.NotFound("Failed to find chat session for the chatId specified in variables.");
+ }
+ if (chat.UserId != authInfo.UserId)
+ {
+ return this.Unauthorized("User does not have access to the chatId specified in variables.");
+ }
}
+ // Put ask's variables in the context we will use.
+ var contextVariables = askConverter.GetContextVariables(ask);
+
// Register plugins that have been enabled
await this.RegisterPlannerSkillsAsync(planner, openApiSkillsAuthHeaders, contextVariables);
diff --git a/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/ChatHistoryController.cs b/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/ChatHistoryController.cs
index 7730311856f8..078022c4ffbb 100644
--- a/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/ChatHistoryController.cs
+++ b/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/ChatHistoryController.cs
@@ -9,6 +9,8 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
+using SemanticKernel.Service.Auth;
+using SemanticKernel.Service.CopilotChat.Auth;
using SemanticKernel.Service.CopilotChat.Models;
using SemanticKernel.Service.CopilotChat.Options;
using SemanticKernel.Service.CopilotChat.Storage;
@@ -21,12 +23,12 @@ namespace SemanticKernel.Service.CopilotChat.Controllers;
/// retrieving chat messages, and editing chat sessions.
///
[ApiController]
-[Authorize]
public class ChatHistoryController : ControllerBase
{
private readonly ILogger _logger;
private readonly ChatSessionRepository _chatSessionRepository;
private readonly ChatMessageRepository _chatMessageRepository;
+ private readonly IAuthInfo _authInfo;
private readonly PromptsOptions _promptOptions;
///
@@ -36,15 +38,18 @@ public class ChatHistoryController : ControllerBase
/// The chat session repository.
/// The chat message repository.
/// The prompts options.
+ /// The auth info for the current request.
public ChatHistoryController(
ILogger logger,
ChatSessionRepository chatSessionRepository,
ChatMessageRepository chatMessageRepository,
- IOptions promptsOptions)
+ IOptions promptsOptions,
+ IAuthInfo authInfo)
{
this._logger = logger;
this._chatSessionRepository = chatSessionRepository;
this._chatMessageRepository = chatMessageRepository;
+ this._authInfo = authInfo;
this._promptOptions = promptsOptions.Value;
}
@@ -54,14 +59,14 @@ public ChatHistoryController(
/// Object that contains the parameters to create a new chat.
/// The HTTP action result.
[HttpPost]
- [Route("chatSession/create")]
+ [Route("chatSessions")]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task CreateChatSessionAsync(
- [FromBody] ChatSession chatParameters)
+ [FromBody] ChatSessionCreationOptions chatParameters)
{
- var userId = chatParameters.UserId;
+ var userId = this._authInfo.UserId;
var title = chatParameters.Title;
var newChat = new ChatSession(userId, title);
@@ -80,10 +85,11 @@ public async Task CreateChatSessionAsync(
/// The chat id.
[HttpGet]
[ActionName("GetChatSessionByIdAsync")]
- [Route("chatSession/getChat/{chatId:guid}")]
+ [Route("chatSessions/{chatId:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
+ [Authorize(Policy = AuthPolicyName.RequireChatOwner)]
public async Task GetChatSessionByIdAsync(Guid chatId)
{
var chat = await this._chatSessionRepository.FindByIdAsync(chatId.ToString());
@@ -96,19 +102,16 @@ public async Task GetChatSessionByIdAsync(Guid chatId)
}
///
- /// Get all chat sessions associated with a user. Return an empty list if no chats are found.
- /// The regex pattern that is used to match the user id will match the following format:
- /// - 2 period separated groups of one or more hyphen-delimited alphanumeric strings.
- /// The pattern matches two GUIDs in canonical textual representation separated by a period.
+ /// Get all chat sessions associated with the logged in user. Return an empty list if no chats are found.
///
- /// The user id.
[HttpGet]
- [Route("chatSession/getAllChats/{userId:regex(([[a-z0-9]]+-)+[[a-z0-9]]+\\.([[a-z0-9]]+-)+[[a-z0-9]]+)}")]
+ [Route("chatSessions")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task GetAllChatSessionsAsync(string userId)
+ public async Task GetAllChatSessionsAsync()
{
+ var userId = this._authInfo.UserId;
var chats = await this._chatSessionRepository.FindByUserIdAsync(userId);
if (chats == null)
{
@@ -128,10 +131,11 @@ public async Task GetAllChatSessionsAsync(string userId)
/// The number of messages to return. -1 will return all messages starting from startIdx.
/// [Authorize]
[HttpGet]
- [Route("chatSession/getChatMessages/{chatId:guid}")]
+ [Route("chatSessions/{chatId:guid}/messages")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
+ [Authorize(Policy = AuthPolicyName.RequireChatOwner)]
public async Task GetChatMessagesAsync(
Guid chatId,
[FromQuery] int startIdx = 0,
@@ -153,20 +157,20 @@ public async Task GetChatMessagesAsync(
///
/// Edit a chat session.
///
+ /// Chat id from the url.
/// Object that contains the parameters to edit the chat.
- [HttpPost]
- [Route("chatSession/edit")]
+ [HttpPatch]
+ [Route("chatSessions/{chatId:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task EditChatSessionAsync([FromBody] ChatSession chatParameters)
+ [Authorize(Policy = AuthPolicyName.RequireChatOwner)]
+ public async Task EditChatSessionAsync(Guid chatId, [FromBody] ChatSessionEditOptions chatParameters)
{
- string chatId = chatParameters.Id;
-
- ChatSession? chat = await this._chatSessionRepository.FindByIdAsync(chatId);
+ ChatSession? chat = await this._chatSessionRepository.FindByIdAsync(chatId.ToString());
if (chat == null)
{
- return this.NotFound($"Chat of id {chatId} not found.");
+ return this.NotFound($"Chat with id {chatId} not found.");
}
chat.Title = chatParameters.Title;
diff --git a/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/DocumentImportController.cs b/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/DocumentImportController.cs
index be040d484bdc..93b8125aa351 100644
--- a/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/DocumentImportController.cs
+++ b/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/DocumentImportController.cs
@@ -2,15 +2,14 @@
using System;
using System.IO;
-using System.Linq;
using System.Threading.Tasks;
-using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Text;
+using SemanticKernel.Service.Auth;
using SemanticKernel.Service.CopilotChat.Models;
using SemanticKernel.Service.CopilotChat.Options;
using SemanticKernel.Service.CopilotChat.Storage;
@@ -61,13 +60,13 @@ public DocumentImportController(
///
/// Service API for importing a document.
///
- [Authorize]
[Route("importDocument")]
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task ImportDocumentAsync(
[FromServices] IKernel kernel,
+ [FromServices] IAuthInfo authInfo,
[FromForm] DocumentImportForm documentImportForm)
{
var formFile = documentImportForm.FormFile;
@@ -86,10 +85,18 @@ public async Task ImportDocumentAsync(
return this.BadRequest("File size exceeds the limit.");
}
- if (documentImportForm.DocumentScope == DocumentImportForm.DocumentScopes.Chat
- && !(await this.UserHasAccessToChatAsync(documentImportForm.UserId, documentImportForm.ChatId)))
+ if (documentImportForm.DocumentScope == DocumentImportForm.DocumentScopes.Chat)
{
- return this.BadRequest("User does not have access to the chat session.");
+ var chatSession = await this._chatSessionRepository.FindByIdAsync(documentImportForm.ChatId.ToString());
+ if (chatSession == null)
+ {
+ return this.NotFound("Session does not exist.");
+ }
+
+ if (authInfo.UserId != chatSession.UserId)
+ {
+ return this.Unauthorized("User does not have access to the chat session.");
+ }
}
this._logger.LogInformation("Importing document {0}", formFile.FileName);
@@ -201,16 +208,4 @@ await kernel.Memory.SaveInformationAsync(
Path.GetFileName(documentImportForm.FormFile?.FileName)
);
}
-
- ///
- /// Check if the user has access to the chat session.
- ///
- /// The user ID.
- /// The chat session ID.
- /// A boolean indicating whether the user has access to the chat session.
- private async Task UserHasAccessToChatAsync(string userId, Guid chatId)
- {
- var chatSessions = await this._chatSessionRepository.FindByUserIdAsync(userId);
- return chatSessions.Any(c => c.Id == chatId.ToString());
- }
}
diff --git a/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/SpeechTokenController.cs b/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/SpeechTokenController.cs
index 8dea7b3b90f4..9a5c0021e063 100644
--- a/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/SpeechTokenController.cs
+++ b/samples/apps/copilot-chat-app/webapi/CopilotChat/Controllers/SpeechTokenController.cs
@@ -4,7 +4,6 @@
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
-using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
@@ -14,7 +13,6 @@
namespace SemanticKernel.Service.CopilotChat.Controllers;
-[Authorize]
[ApiController]
public class SpeechTokenController : ControllerBase
{
diff --git a/samples/apps/copilot-chat-app/webapi/CopilotChat/Extensions/ServiceExtensions.cs b/samples/apps/copilot-chat-app/webapi/CopilotChat/Extensions/ServiceExtensions.cs
index 95139381540e..3e6a983410ff 100644
--- a/samples/apps/copilot-chat-app/webapi/CopilotChat/Extensions/ServiceExtensions.cs
+++ b/samples/apps/copilot-chat-app/webapi/CopilotChat/Extensions/ServiceExtensions.cs
@@ -4,9 +4,15 @@
using System.Collections.Generic;
using System.IO;
using System.Reflection;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
+using Microsoft.Identity.Web;
+using SemanticKernel.Service.Auth;
+using SemanticKernel.Service.CopilotChat.Auth;
using SemanticKernel.Service.CopilotChat.Models;
using SemanticKernel.Service.CopilotChat.Options;
using SemanticKernel.Service.CopilotChat.Storage;
@@ -32,6 +38,13 @@ public static IServiceCollection AddCopilotChatOptions(this IServiceCollection s
.ValidateOnStart()
.PostConfigure(TrimStringProperties);
+ // Authorization configuration
+ services.AddOptions()
+ .Bind(configuration.GetSection(ChatAuthenticationOptions.PropertyName))
+ .ValidateOnStart()
+ .ValidateDataAnnotations()
+ .PostConfigure(TrimStringProperties);
+
// Chat log storage configuration
services.AddOptions()
.Bind(configuration.GetSection(ChatStoreOptions.PropertyName))
@@ -65,10 +78,30 @@ public static IServiceCollection AddCopilotChatOptions(this IServiceCollection s
return services;
}
+ ///
+ /// Add authorization services for the copilot chat.
+ ///
+ public static IServiceCollection AddCopilotChatAuthorization(this IServiceCollection services)
+ {
+ return services.AddScoped()
+ .AddAuthorizationCore(options =>
+ {
+ options.DefaultPolicy = new AuthorizationPolicyBuilder()
+ .RequireAuthenticatedUser()
+ .Build();
+
+ options.AddPolicy(AuthPolicyName.RequireChatOwner, builder =>
+ {
+ builder.RequireAuthenticatedUser()
+ .AddRequirements(new ChatOwnerRequirement());
+ });
+ });
+ }
+
///
/// Add persistent chat store services.
///
- public static void AddPersistentChatStore(this IServiceCollection services)
+ public static IServiceCollection AddPersistentChatStore(this IServiceCollection services)
{
IStorageContext chatSessionInMemoryContext;
IStorageContext chatMessageInMemoryContext;
@@ -124,7 +157,35 @@ public static void AddPersistentChatStore(this IServiceCollection services)
}
services.AddSingleton(new ChatSessionRepository(chatSessionInMemoryContext));
- services.AddSingleton(new ChatMessageRepository(chatMessageInMemoryContext));
+ return services.AddSingleton(new ChatMessageRepository(chatMessageInMemoryContext));
+ }
+
+ ///
+ /// Add authentication services
+ ///
+ public static IServiceCollection AddCopilotChatAuthentication(this IServiceCollection services, IConfiguration configuration)
+ {
+ services.AddScoped();
+ var config = services.BuildServiceProvider().GetRequiredService>().Value;
+ switch (config.Type)
+ {
+ case ChatAuthenticationOptions.AuthenticationType.AzureAd:
+ services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
+ .AddMicrosoftIdentityWebApi(configuration.GetSection($"{ChatAuthenticationOptions.PropertyName}:AzureAd"));
+ break;
+
+ case ChatAuthenticationOptions.AuthenticationType.None:
+ services.AddAuthentication(PassThroughAuthenticationHandler.AuthenticationScheme)
+ .AddScheme(
+ authenticationScheme: PassThroughAuthenticationHandler.AuthenticationScheme,
+ configureOptions: null);
+ break;
+
+ default:
+ throw new InvalidOperationException($"Invalid authentication type '{config.Type}'.");
+ }
+
+ return services;
}
///
diff --git a/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/ChatAsk.cs b/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/ChatAsk.cs
new file mode 100644
index 000000000000..bf459334ad5f
--- /dev/null
+++ b/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/ChatAsk.cs
@@ -0,0 +1,13 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.ComponentModel.DataAnnotations;
+using SemanticKernel.Service.Models;
+using SemanticKernel.Service.Options;
+
+namespace SemanticKernel.Service.CopilotChat.Models;
+
+public class ChatAsk : Ask
+{
+ [Required, NotEmptyOrWhitespace]
+ public override string Input { get => base.Input; set => base.Input = value; }
+}
diff --git a/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/ChatMessage.cs b/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/ChatMessage.cs
index 7e67abdf2011..669117a5e285 100644
--- a/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/ChatMessage.cs
+++ b/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/ChatMessage.cs
@@ -3,7 +3,6 @@
using System;
using System.Globalization;
using System.Text.Json;
-using System.Text.Json.Serialization;
using SemanticKernel.Service.CopilotChat.Storage;
namespace SemanticKernel.Service.CopilotChat.Models;
@@ -13,6 +12,8 @@ namespace SemanticKernel.Service.CopilotChat.Models;
///
public class ChatMessage : IStorageEntity
{
+ private static readonly JsonSerializerOptions SerializerSettings = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
+
///
/// Role of the author of a chat message.
///
@@ -37,43 +38,36 @@ public enum AuthorRoles
///
/// Timestamp of the message.
///
- [JsonPropertyName("timestamp")]
public DateTimeOffset Timestamp { get; set; }
///
/// Id of the user who sent this message.
///
- [JsonPropertyName("userId")]
public string UserId { get; set; }
///
/// Name of the user who sent this message.
///
- [JsonPropertyName("userName")]
public string UserName { get; set; }
///
/// Id of the chat this message belongs to.
///
- [JsonPropertyName("chatId")]
public string ChatId { get; set; }
///
/// Content of the message.
///
- [JsonPropertyName("content")]
public string Content { get; set; }
///
/// Id of the message.
///
- [JsonPropertyName("id")]
public string Id { get; set; }
///
/// Role of the author of the message.
///
- [JsonPropertyName("authorRole")]
public AuthorRoles AuthorRole { get; set; }
///
@@ -120,7 +114,7 @@ public string ToFormattedString()
/// A serialized json string
public override string ToString()
{
- return JsonSerializer.Serialize(this);
+ return JsonSerializer.Serialize(this, SerializerSettings);
}
///
@@ -130,6 +124,6 @@ public override string ToString()
/// A ChatMessage object
public static ChatMessage? FromString(string json)
{
- return JsonSerializer.Deserialize(json);
+ return JsonSerializer.Deserialize(json, SerializerSettings);
}
}
diff --git a/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/ChatSession.cs b/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/ChatSession.cs
index 29d4d9476c91..9d42e95c267b 100644
--- a/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/ChatSession.cs
+++ b/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/ChatSession.cs
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
-using System.Text.Json.Serialization;
using SemanticKernel.Service.CopilotChat.Storage;
namespace SemanticKernel.Service.CopilotChat.Models;
@@ -14,25 +13,21 @@ public class ChatSession : IStorageEntity
///
/// Chat ID that is persistent and unique.
///
- [JsonPropertyName("id")]
public string Id { get; set; }
///
/// User ID that is persistent and unique.
///
- [JsonPropertyName("userId")]
public string UserId { get; set; }
///
/// Title of the chat.
///
- [JsonPropertyName("title")]
public string Title { get; set; }
///
/// Timestamp of the chat creation.
///
- [JsonPropertyName("createdOn")]
public DateTimeOffset CreatedOn { get; set; }
public ChatSession(string userId, string title)
diff --git a/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/ChatSessionCreationOptions.cs b/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/ChatSessionCreationOptions.cs
new file mode 100644
index 000000000000..f3573af80130
--- /dev/null
+++ b/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/ChatSessionCreationOptions.cs
@@ -0,0 +1,10 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.ComponentModel.DataAnnotations;
+
+namespace SemanticKernel.Service.CopilotChat.Models;
+
+///
+/// A chat session creation option.
+///
+public record struct ChatSessionCreationOptions([Required] string Title);
diff --git a/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/ChatSessionEditOptions.cs b/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/ChatSessionEditOptions.cs
new file mode 100644
index 000000000000..c3f29c7bdee3
--- /dev/null
+++ b/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/ChatSessionEditOptions.cs
@@ -0,0 +1,10 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.ComponentModel.DataAnnotations;
+
+namespace SemanticKernel.Service.CopilotChat.Models;
+
+///
+/// A chat session edit option.
+///
+public record struct ChatSessionEditOptions([Required] string Title);
diff --git a/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/DocumentImportForm.cs b/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/DocumentImportForm.cs
index 109702e0738f..f4168b1936c4 100644
--- a/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/DocumentImportForm.cs
+++ b/samples/apps/copilot-chat-app/webapi/CopilotChat/Models/DocumentImportForm.cs
@@ -36,10 +36,4 @@ public enum DocumentScopes
/// If the document scope is set to global, this value is ignored.
///
public Guid ChatId { get; set; } = Guid.Empty;
-
- ///
- /// The ID of the user who is importing the document to a chat session.
- /// Will be use to validate if the user has access to the chat session.
- ///
- public string UserId { get; set; } = string.Empty;
}
diff --git a/samples/apps/copilot-chat-app/webapi/Options/AuthorizationOptions.cs b/samples/apps/copilot-chat-app/webapi/CopilotChat/Options/ChatAuthenticationOptions.cs
similarity index 63%
rename from samples/apps/copilot-chat-app/webapi/Options/AuthorizationOptions.cs
rename to samples/apps/copilot-chat-app/webapi/CopilotChat/Options/ChatAuthenticationOptions.cs
index d8ee6f70271e..7198f2e67df4 100644
--- a/samples/apps/copilot-chat-app/webapi/Options/AuthorizationOptions.cs
+++ b/samples/apps/copilot-chat-app/webapi/CopilotChat/Options/ChatAuthenticationOptions.cs
@@ -1,20 +1,20 @@
// Copyright (c) Microsoft. All rights reserved.
using System.ComponentModel.DataAnnotations;
+using SemanticKernel.Service.Options;
-namespace SemanticKernel.Service.Options;
+namespace SemanticKernel.Service.CopilotChat.Options;
///
/// Configuration options for authorizing to the service.
///
-public class AuthorizationOptions
+public class ChatAuthenticationOptions
{
- public const string PropertyName = "Authorization";
+ public const string PropertyName = "Authentication";
- public enum AuthorizationType
+ public enum AuthenticationType
{
None,
- ApiKey,
AzureAd
}
@@ -22,18 +22,12 @@ public enum AuthorizationType
/// Type of authorization.
///
[Required]
- public AuthorizationType Type { get; set; } = AuthorizationType.None;
+ public AuthenticationType Type { get; set; } = AuthenticationType.None;
///
- /// When is , this is the API key to use.
+ /// When is , these are the Azure AD options to use.
///
- [RequiredOnPropertyValue(nameof(Type), AuthorizationType.ApiKey, notEmptyOrWhitespace: true)]
- public string ApiKey { get; set; } = string.Empty;
-
- ///
- /// When is , these are the Azure AD options to use.
- ///
- [RequiredOnPropertyValue(nameof(Type), AuthorizationType.AzureAd)]
+ [RequiredOnPropertyValue(nameof(Type), AuthenticationType.AzureAd)]
public AzureAdOptions? AzureAd { get; set; }
///
diff --git a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK-Existing-AzureOpenAI.ps1 b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK-Existing-AzureOpenAI.ps1
index 1722f04c9d2a..77c412aba207 100644
--- a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK-Existing-AzureOpenAI.ps1
+++ b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK-Existing-AzureOpenAI.ps1
@@ -52,10 +52,6 @@ param(
# SKU for the Azure App Service plan
$AppServiceSku = "B1",
- [string]
- # API key to access Semantic Kernel server's endpoints
- $SemanticKernelApiKey = "$([guid]::NewGuid())",
-
[switch]
# Don't deploy Qdrant for memory storage - Use volatile memory instead
$NoQdrant,
@@ -83,7 +79,6 @@ $jsonConfig = "
`\`"plannerModel`\`": { `\`"value`\`": `\`"$PlannerModel`\`" },
`\`"packageUri`\`": { `\`"value`\`": `\`"$PackageUri`\`" },
`\`"appServiceSku`\`": { `\`"value`\`": `\`"$AppServiceSku`\`" },
- `\`"semanticKernelApiKey`\`": { `\`"value`\`": `\`"$SemanticKernelApiKey`\`" },
`\`"deployQdrant`\`": { `\`"value`\`": $(If (!($NoQdrant)) {"true"} Else {"false"}) },
`\`"deployCosmosDB`\`": { `\`"value`\`": $(If (!($NoSpeechServices)) {"true"} Else {"false"}) },
`\`"deploySpeechServices`\`": { `\`"value`\`": $(If (!($NoSpeechServices)) {"true"} Else {"false"}) }
diff --git a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK-Existing-AzureOpenAI.sh b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK-Existing-AzureOpenAI.sh
index 2724522d3a4b..22c7b8ecdb07 100644
--- a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK-Existing-AzureOpenAI.sh
+++ b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK-Existing-AzureOpenAI.sh
@@ -16,7 +16,6 @@ usage() {
echo " -r, --region REGION Region to which to make the deployment (default: \"South Central US\")"
echo " -p, --package-uri PACKAGE_URI Package to deploy to web service (default: 'https://skaasdeploy.blob.core.windows.net/api/semantickernelapi.zip')"
echo " -a, --app-service-sku APP_SERVICE_SKU SKU for the Azure App Service plan (default: \"B1\")"
- echo " -k, --semker-server-api-key SEMKER_SERVER_API_KEY API key to access Semantic Kernel server's endpoints (default: random UUID)"
echo " -cm, --completion-model COMPLETION_MODEL Completion model to use (default: \"gpt-35-turbo\")"
echo " -em, --embedding-model EMBEDDING_MODEL Embedding model to use (default: \"text-embedding-ada-002\")"
echo " -pm, --planner-model PLANNER_MODEL Planner model to use (default: \"gpt-35-turbo\")"
@@ -70,11 +69,6 @@ while [[ $# -gt 0 ]]; do
shift
shift
;;
- -k|--semker-server-api-key)
- SEMKER_SERVER_API_KEY="$2"
- shift
- shift
- ;;
-cm|--completion-model)
COMPLETION_MODEL="$2"
shift
@@ -133,7 +127,6 @@ az account set -s "$SUBSCRIPTION"
: "${REGION:="South Central US"}"
: "${PACKAGE_URI:="https://skaasdeploy.blob.core.windows.net/api/semantickernelapi.zip"}"
: "${APP_SERVICE_SKU:="B1"}"
-: "${SEMKER_SERVER_API_KEY:="$(uuidgen)"}"
: "${NO_QDRANT:=false}"
: "${NO_COSMOS_DB:=false}"
: "${NO_SPEECH_SERVICES:=false}"
@@ -152,7 +145,6 @@ JSON_CONFIG=$(cat << EOF
"plannerModel": { "value": "$PLANNER_MODEL" },
"packageUri": { "value": "$PACKAGE_URI" },
"appServiceSku": { "value": "$APP_SERVICE_SKU" },
- "semanticKernelApiKey": { "value": "$SEMKER_SERVER_API_KEY" },
"deployQdrant": { "value": $([ "$NO_QDRANT" = true ] && echo "false" || echo "true") },
"deployCosmosDB": { "value": $([ "$NO_COSMOS_DB" = true ] && echo "false" || echo "true") },
"deploySpeechServices": { "value": $([ "$NO_SPEECH_SERVICES" = true ] && echo "false" || echo "true") }
diff --git a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK-Existing-OpenAI.ps1 b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK-Existing-OpenAI.ps1
index 3ff5adf05f5a..abd08bbcbcae 100644
--- a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK-Existing-OpenAI.ps1
+++ b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK-Existing-OpenAI.ps1
@@ -47,10 +47,6 @@ param(
# SKU for the Azure App Service plan
$AppServiceSku = "B1",
- [string]
- # API key to access Semantic Kernel server's endpoints
- $SemanticKernelApiKey = "$([guid]::NewGuid())",
-
[switch]
# Don't deploy Qdrant for memory storage - Use volatile memory instead
$NoQdrant,
@@ -77,7 +73,6 @@ $jsonConfig = "
`\`"plannerModel`\`": { `\`"value`\`": `\`"$PlannerModel`\`" },
`\`"packageUri`\`": { `\`"value`\`": `\`"$PackageUri`\`" },
`\`"appServiceSku`\`": { `\`"value`\`": `\`"$AppServiceSku`\`" },
- `\`"semanticKernelApiKey`\`": { `\`"value`\`": `\`"$SemanticKernelApiKey`\`" },
`\`"deployQdrant`\`": { `\`"value`\`": $(If (!($NoQdrant)) {"true"} Else {"false"}) },
`\`"deployCosmosDB`\`": { `\`"value`\`": $(If (!($NoSpeechServices)) {"true"} Else {"false"}) },
`\`"deploySpeechServices`\`": { `\`"value`\`": $(If (!($NoSpeechServices)) {"true"} Else {"false"}) }
diff --git a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK-Existing-OpenAI.sh b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK-Existing-OpenAI.sh
index 2a70c9740636..51d3a8d599cb 100644
--- a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK-Existing-OpenAI.sh
+++ b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK-Existing-OpenAI.sh
@@ -15,7 +15,6 @@ usage() {
echo " -r, --region REGION Region to which to make the deployment (default: \"South Central US\")"
echo " -p, --package-uri PACKAGE_URI Package to deploy to web service (default: 'https://skaasdeploy.blob.core.windows.net/api/semantickernelapi.zip')"
echo " -a, --app-service-sku APP_SERVICE_SKU SKU for the Azure App Service plan (default: \"B1\")"
- echo " -k, --semker-server-api-key SEMKER_SERVER_API_KEY API key to access Semantic Kernel server's endpoints (default: random UUID)"
echo " -cm, --completion-model COMPLETION_MODEL Completion model to use (default: \"gpt-3.5-turbo\")"
echo " -em, --embedding-model EMBEDDING_MODEL Embedding model to use (default: \"text-embedding-ada-002\")"
echo " -pm, --planner-model PLANNER_MODEL Planner model to use (default: \"gpt-3.5-turbo\")"
@@ -64,11 +63,6 @@ while [[ $# -gt 0 ]]; do
shift
shift
;;
- -k|--semker-server-api-key)
- SEMKER_SERVER_API_KEY="$2"
- shift
- shift
- ;;
-cm|--completion-model)
COMPLETION_MODEL="$2"
shift
@@ -127,7 +121,6 @@ az account set -s "$SUBSCRIPTION"
: "${REGION:="South Central US"}"
: "${PACKAGE_URI:="https://skaasdeploy.blob.core.windows.net/api/semantickernelapi.zip"}"
: "${APP_SERVICE_SKU:="B1"}"
-: "${SEMKER_SERVER_API_KEY:="$(uuidgen)"}"
: "${NO_QDRANT:=false}"
: "${NO_COSMOS_DB:=false}"
: "${NO_SPEECH_SERVICES:=false}"
@@ -145,7 +138,6 @@ JSON_CONFIG=$(cat << EOF
"plannerModel": { "value": "$PLANNER_MODEL" },
"packageUri": { "value": "$PACKAGE_URI" },
"appServiceSku": { "value": "$APP_SERVICE_SKU" },
- "semanticKernelApiKey": { "value": "$SEMKER_SERVER_API_KEY" },
"deployQdrant": { "value": $([ "$NO_QDRANT" = true ] && echo "false" || echo "true") },
"deployCosmosDB": { "value": $([ "$NO_COSMOS_DB" = true ] && echo "false" || echo "true") },
"deploySpeechServices": { "value": $([ "$NO_SPEECH_SERVICES" = true ] && echo "false" || echo "true") }
diff --git a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK.ps1 b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK.ps1
index d964a059dea0..796b159f2341 100644
--- a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK.ps1
+++ b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK.ps1
@@ -30,10 +30,6 @@ param(
# SKU for the Azure App Service plan
$AppServiceSku = "B1",
- [string]
- # API key to access Semantic Kernel server's endpoints
- $SemanticKernelApiKey = "$([guid]::NewGuid())",
-
[switch]
# Don't deploy Qdrant for memory storage - Use volatile memory instead
$NoQdrant,
@@ -56,7 +52,6 @@ $jsonConfig = "
`\`"name`\`": { `\`"value`\`": `\`"$DeploymentName`\`" },
`\`"packageUri`\`": { `\`"value`\`": `\`"$PackageUri`\`" },
`\`"appServiceSku`\`": { `\`"value`\`": `\`"$AppServiceSku`\`" },
- `\`"semanticKernelApiKey`\`": { `\`"value`\`": `\`"$SemanticKernelApiKey`\`" },
`\`"deployQdrant`\`": { `\`"value`\`": $(If (!($NoQdrant)) {"true"} Else {"false"}) },
`\`"deployCosmosDB`\`": { `\`"value`\`": $(If (!($NoSpeechServices)) {"true"} Else {"false"}) },
`\`"deploySpeechServices`\`": { `\`"value`\`": $(If (!($NoSpeechServices)) {"true"} Else {"false"}) }
diff --git a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK.sh b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK.sh
index 552299aa386f..be790f171b1c 100644
--- a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK.sh
+++ b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/DeploySK.sh
@@ -14,7 +14,6 @@ usage() {
echo " -r, --region REGION Region to which to make the deployment (default: \"South Central US\")"
echo " -p, --package-uri PACKAGE_URI Package to deploy to web service (default: 'https://skaasdeploy.blob.core.windows.net/api/semantickernelapi.zip')"
echo " -a, --app-service-sku APP_SERVICE_SKU SKU for the Azure App Service plan (default: \"B1\")"
- echo " -k, --semker-server-api-key SEMKER_SERVER_API_KEY API key to access Semantic Kernel server's endpoints (default: random UUID)"
echo " -nq, --no-qdrant Don't deploy Qdrant for memory storage - Use volatile memory instead"
echo " -nc, --no-cosmos-db Don't deploy Cosmos DB for chat storage - Use volatile memory instead"
echo " -ns, --no-speech-services Don't deploy Speech Services to enable speech as chat input"
@@ -55,11 +54,6 @@ while [[ $# -gt 0 ]]; do
shift
shift
;;
- -k|--semker-server-api-key)
- SEMKER_SERVER_API_KEY="$2"
- shift
- shift
- ;;
-nq|--no-qdrant)
NO_QDRANT=true
shift
@@ -103,7 +97,6 @@ az account set -s "$SUBSCRIPTION"
: "${REGION:="South Central US"}"
: "${PACKAGE_URI:="https://skaasdeploy.blob.core.windows.net/api/semantickernelapi.zip"}"
: "${APP_SERVICE_SKU:="B1"}"
-: "${SEMKER_SERVER_API_KEY:="$(uuidgen)"}"
: "${NO_QDRANT:=false}"
: "${NO_COSMOS_DB:=false}"
: "${NO_SPEECH_SERVICES:=false}"
@@ -114,7 +107,6 @@ JSON_CONFIG=$(cat << EOF
"name": { "value": "$DEPLOYMENT_NAME" },
"packageUri": { "value": "$PACKAGE_URI" },
"appServiceSku": { "value": "$APP_SERVICE_SKU" },
- "semanticKernelApiKey": { "value": "$SEMKER_SERVER_API_KEY" },
"deployQdrant": { "value": $([ "$NO_QDRANT" = true ] && echo "false" || echo "true") },
"deployCosmosDB": { "value": $([ "$NO_COSMOS_DB" = true ] && echo "false" || echo "true") },
"deploySpeechServices": { "value": $([ "$NO_SPEECH_SERVICES" = true ] && echo "false" || echo "true") }
diff --git a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/README.md b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/README.md
index 5282d3883d4f..093b81592762 100644
--- a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/README.md
+++ b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/README.md
@@ -1,9 +1,10 @@
# Deploying Semantic Kernel to Azure in a web app service
## Things to know
-- Access to Azure OpenAI is currently limited as we navigate high demand, upcoming product improvements, and Microsoft’s commitment to responsible AI.
- For more details and information on applying for access, go [here](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/overview?ocid=AID3051475#how-do-i-get-access-to-azure-openai).
- For region availability of Azure OpenAI, see the [availability map](https://azure.microsoft.com/en-us/explore/global-infrastructure/products-by-region/?products=cognitive-services).
+
+- Access to Azure OpenAI is currently limited as we navigate high demand, upcoming product improvements, and Microsoft’s commitment to responsible AI.
+ For more details and information on applying for access, go [here](https://learn.microsoft.com/azure/cognitive-services/openai/overview?ocid=AID3051475#how-do-i-get-access-to-azure-openai).
+ For region availability of Azure OpenAI, see the [availability map](https://azure.microsoft.com/explore/global-infrastructure/products-by-region/?products=cognitive-services).
- Due to the limited availability of Azure OpenAI, consider using the same Azure OpenAI instance for multiple deployments of the Semantic Kernel web api and CopilotChat:
- [Deploying with an existing Azure OpenAI account](#deploying-with-an-existing-azure-openai-account)
@@ -11,53 +12,66 @@
- F1 and D1 SKUs for the App Service Plans are not currently supported for this deployment.
-
# Deploying with a new Azure OpenAI instance
+
You can deploy an instance of Semantic Kernel in a web app service within a resource group that bears the name YOUR_DEPLOYMENT_NAME preceded by the "rg-" prefix using any of the following methods.
## PowerShell
+
Use the [DeploySK.ps1](DeploySK.ps1) file found in this folder:
+
```powershell
.\DeploySK.ps1 -DeploymentName YOUR_DEPLOYMENT_NAME -Subscription YOUR_SUBSCRIPTION_ID
```
+
For additional deployment options, see the deployment script.
## Bash
+
Use the [DeploySK.sh](DeploySK.sh) file found in this folder:
+
```bash
chmod +x ./DeploySK.sh
./DeploySK.sh -d DEPLOYMENT_NAME -s SUBSCRIPTION_ID
```
## Azure Portal
+
You can also deploy by clicking on:
[](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fmicrosoft%2Fsemantic-kernel%2Fmain%2Fsamples%2Fapps%2Fcopilot-chat-app%2Fwebapi%2FDeploymentTemplates%2Fsk-new.json)
-
# Deploying with an existing Azure OpenAI account
+
## PowerShell
+
Use the [DeploySK-Existing-AzureOpenAI.ps1](DeploySK-Existing-AzureOpenAI.ps1) file found in this folder:
+
```powershell
.\DeploySK-Existing-AzureOpenAI.ps1 -DeploymentName YOUR_DEPLOYMENT_NAME -Subscription YOUR_SUBSCRIPTION_ID -Endpoint YOUR_AZURE_OPENAI_ENDPOINT -ApiKey YOUR_AZURE_OPENAI_API_KEY
```
## Bash
+
Use the [DeploySK-Existing-AzureOpenAI.sh](DeploySK-Existing-AzureOpenAI.sh) file found in this folder:
+
```bash
chmod +x ./DeploySK-Existing-AzureOpenAI.sh
./DeploySK-Existing-AzureOpenAI.sh -d YOUR_DEPLOYMENT_NAME -s YOUR_SUBSCRIPTION_ID -e YOUR_AZURE_OPENAI_ENDPOINT -o YOUR_AZURE_OPENAI_API_KEY
```
## Azure Portal
+
You can also deploy by clicking on:
[](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fmicrosoft%2Fsemantic-kernel%2Fmain%2Fsamples%2Fapps%2Fcopilot-chat-app%2Fwebapi%2FDeploymentTemplates%2Fsk-existing-azureopenai.json)
-
# Deploying with an existing OpenAI account
+
## PowerShell
+
Use the [DeploySK-Existing-OpenAI.ps1](DeploySK-Existing-OpenAI.ps1) file found in this folder:
+
```powershell
.\DeploySK-Existing-OpenAI.ps1 -DeploymentName YOUR_DEPLOYMENT_NAME -Subscription YOUR_SUBSCRIPTION_ID
```
@@ -65,6 +79,7 @@ Use the [DeploySK-Existing-OpenAI.ps1](DeploySK-Existing-OpenAI.ps1) file found
After entering the command above, you will be prompted to enter your OpenAI API key. (You can also pass in the API key using the -ApiKey parameter)
## Bash
+
After ensuring DeploySK-Existing-OpenAI.sh file found in this folder is executable, enter the following command:
```bash
@@ -72,20 +87,21 @@ After ensuring DeploySK-Existing-OpenAI.sh file found in this folder is executab
```
## Azure Portal
+
You can also deploy by clicking on:
[](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fmicrosoft%2Fsemantic-kernel%2Fmain%2Fsamples%2Fapps%2Fcopilot-chat-app%2Fwebapi%2FDeploymentTemplates%2Fsk-existing-openai.json)
-
# Verifying the deployment
-To make sure your web app service is running, go to https://YOUR_INSTANCE_NAME.azurewebsites.net/probe
+
+To make sure your web app service is running, go to
To get your instance's URL, click on the "Go to resource group" button you see at the end of your deployment. Then click on the resource whose name starts with "app-".
This will bring you to the Overview page on your web service. Your instance's URL is the value that appears next to the "Default domain" field.
-
# Changing your configuration, monitoring your deployment and troubleshooting
+
From the page just mentioned in the section above, you can change your configuration by clicking on the "Configuration" item in the "Settings" section of the left pane.
Scrolling down in that same pane to the "Monitoring" section gives you access to a multitude of ways to monitor your deployment.
@@ -94,34 +110,33 @@ In addition to this, the "Diagnose and "solve problems" item near the top of the
If the service itself if functioning properly but you keep getting errors (perhaps reported as 400 HTTP errors) when making calls to the Semantic Kernel,
check that you have correctly entered the values for the following settings:
+
- AIService:AzureOpenAI
- AIService:Endpoint
Both Completion:Endpoint and Embedding:Endpoint are ignored for OpenAI instances from [openai.com](https://openai.com) but MUST be properly populated when using Azure OpenAI instances.
# Authorization
-All of the server's endpoints other than the /probe one require authorization to access.
-By default, the deployment templates set up the server so that an API key is required to access its endpoints.
-AAD authentication and authorization can also be set up manually after the automated deployment is done.
+All of the server's endpoints other than the /healthz one require authorization to access when using Azure AD.
+By default, the deployment templates set up the server so that no authentication is required to access its endpoints.
-To view the API key required by your instance, access the page for your Semantic Kernel app service in the Azure portal.
-From that page, click on the "Configuration" item in the "Settings" section of the left pane. Then click on the text that reads "Hidden value.
-Click to show value" next to the "Authorization:ApiKey" setting.
-
-To authorize requests with the API key, it must be added as the value of an "x-sk-api-key" header added to the requests.
+AAD authentication and authorization should be set up manually after the automated deployment is done.
# Using web frontends to access your deployment
+
Make sure to include your frontend's URL as an allowed origin in your deployment's CORS settings. Otherwise, web browsers will refuse to let JavaScript make calls to your deployment.
To do this, go on the Azure portal, select your Semantic Kernel App Service, then click on "CORS" under the "API" section of the resource menu on the left of the page.
This will get you to the CORS page where you can add your allowed hosts.
# Deploying your custom version of Semantic Kernel
+
You can build and upload a customized version of the Semantic Kernel service.
To do so, clone the code from this repo then modify it to your needs (for example, by adding your own skills). Once that is done, go into the ../semantic-kernel/samples/apps/copilot-chat-app/webapi
directory and enter the following command:
+
```powershell
dotnet publish CopilotChatWebApi.csproj --configuration Release --arch x64 --os win
```
@@ -135,10 +150,11 @@ Put its URI in the "Package Uri" field in the web deployment page you access thr
Your deployment will then use your customized deployment package.
-
## Cleaning up
+
Once you are done with your resources, you can delete them from the Azure portal. You can also simply delete the resource group in which they are from the portal or through the
-following [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/) command:
+following [Azure CLI](https://learn.microsoft.com/cli/azure/) command:
+
```powershell
az group delete --name YOUR_RESOURCE_GROUP
-```
\ No newline at end of file
+```
diff --git a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/main.bicep b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/main.bicep
index ad5d68dee28d..a43cf01d2fbb 100644
--- a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/main.bicep
+++ b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/main.bicep
@@ -39,9 +39,6 @@ param endpoint string = ''
@description('Azure OpenAI or OpenAI API key')
param apiKey string = ''
-@description('Semantic Kernel server API key - Generated GUID by default (Provide empty string to disable API key auth)')
-param semanticKernelApiKey string = newGuid()
-
@description('Whether to deploy a new Azure OpenAI instance')
param deployNewAzureOpenAI bool = true
@@ -174,12 +171,8 @@ resource appServiceWebConfig 'Microsoft.Web/sites/config@2022-09-01' = {
value: plannerModel
}
{
- name: 'Authorization:Type'
- value: empty(semanticKernelApiKey) ? 'None' : 'ApiKey'
- }
- {
- name: 'Authorization:ApiKey'
- value: semanticKernelApiKey
+ name: 'Authentication:Type'
+ value: 'None'
}
{
name: 'ChatStore:Type'
diff --git a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-existing-azureopenai.bicep b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-existing-azureopenai.bicep
index f5369caceea3..6c52385f8c3c 100644
--- a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-existing-azureopenai.bicep
+++ b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-existing-azureopenai.bicep
@@ -32,9 +32,6 @@ param endpoint string
@description('Azure OpenAI API key')
param apiKey string
-@description('Semantic Kernel server API key - Generated GUID by default (Provide empty string to disable API key auth)')
-param semanticKernelApiKey string = newGuid()
-
@description('Whether to deploy Cosmos DB for chat storage')
param deployCosmosDB bool = true
@@ -57,7 +54,6 @@ module semanticKernel 'main.bicep' = {
plannerModel: plannerModel
endpoint: endpoint
apiKey: apiKey
- semanticKernelApiKey: semanticKernelApiKey
deployCosmosDB: deployCosmosDB
deployQdrant: deployQdrant
deploySpeechServices: deploySpeechServices
diff --git a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-existing-azureopenai.json b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-existing-azureopenai.json
index 4a19356247a9..4eb76c6db9fe 100644
--- a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-existing-azureopenai.json
+++ b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-existing-azureopenai.json
@@ -73,13 +73,6 @@
"description": "Azure OpenAI API key"
}
},
- "semanticKernelApiKey": {
- "type": "string",
- "defaultValue": "[newGuid()]",
- "metadata": {
- "description": "Semantic Kernel server API key - Generated GUID by default (Provide empty string to disable API key auth)"
- }
- },
"deployCosmosDB": {
"type": "bool",
"defaultValue": true,
@@ -140,9 +133,6 @@
"apiKey": {
"value": "[parameters('apiKey')]"
},
- "semanticKernelApiKey": {
- "value": "[parameters('semanticKernelApiKey')]"
- },
"deployCosmosDB": {
"value": "[parameters('deployCosmosDB')]"
},
@@ -244,13 +234,6 @@
"description": "Azure OpenAI or OpenAI API key"
}
},
- "semanticKernelApiKey": {
- "type": "string",
- "defaultValue": "[newGuid()]",
- "metadata": {
- "description": "Semantic Kernel server API key - Generated GUID by default (Provide empty string to disable API key auth)"
- }
- },
"deployNewAzureOpenAI": {
"type": "bool",
"defaultValue": true,
@@ -416,12 +399,8 @@
"value": "[parameters('plannerModel')]"
},
{
- "name": "Authorization:Type",
- "value": "[if(empty(parameters('semanticKernelApiKey')), 'None', 'ApiKey')]"
- },
- {
- "name": "Authorization:ApiKey",
- "value": "[parameters('semanticKernelApiKey')]"
+ "name": "Authentication:Type",
+ "value": "None"
},
{
"name": "ChatStore:Type",
diff --git a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-existing-openai.bicep b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-existing-openai.bicep
index fc64edec9280..9b8478a29b90 100644
--- a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-existing-openai.bicep
+++ b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-existing-openai.bicep
@@ -29,9 +29,6 @@ param plannerModel string = 'gpt-3.5-turbo'
@description('OpenAI API key')
param apiKey string = ''
-@description('Semantic Kernel server API key - Generated GUID by default (Provide empty string to disable API key auth)')
-param semanticKernelApiKey string = newGuid()
-
@description('Whether to deploy Cosmos DB for chat storage')
param deployCosmosDB bool = true
@@ -54,7 +51,6 @@ module semanticKernel 'main.bicep' = {
plannerModel: plannerModel
endpoint: 'not-used'
apiKey: apiKey
- semanticKernelApiKey: semanticKernelApiKey
deployCosmosDB: deployCosmosDB
deployQdrant: deployQdrant
deploySpeechServices: deploySpeechServices
diff --git a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-existing-openai.json b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-existing-openai.json
index 53797934e9ee..3826d260ad64 100644
--- a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-existing-openai.json
+++ b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-existing-openai.json
@@ -68,13 +68,6 @@
"description": "OpenAI API key"
}
},
- "semanticKernelApiKey": {
- "type": "string",
- "defaultValue": "[newGuid()]",
- "metadata": {
- "description": "Semantic Kernel server API key - Generated GUID by default (Provide empty string to disable API key auth)"
- }
- },
"deployCosmosDB": {
"type": "bool",
"defaultValue": true,
@@ -135,9 +128,6 @@
"apiKey": {
"value": "[parameters('apiKey')]"
},
- "semanticKernelApiKey": {
- "value": "[parameters('semanticKernelApiKey')]"
- },
"deployCosmosDB": {
"value": "[parameters('deployCosmosDB')]"
},
@@ -239,13 +229,6 @@
"description": "Azure OpenAI or OpenAI API key"
}
},
- "semanticKernelApiKey": {
- "type": "string",
- "defaultValue": "[newGuid()]",
- "metadata": {
- "description": "Semantic Kernel server API key - Generated GUID by default (Provide empty string to disable API key auth)"
- }
- },
"deployNewAzureOpenAI": {
"type": "bool",
"defaultValue": true,
@@ -411,12 +394,8 @@
"value": "[parameters('plannerModel')]"
},
{
- "name": "Authorization:Type",
- "value": "[if(empty(parameters('semanticKernelApiKey')), 'None', 'ApiKey')]"
- },
- {
- "name": "Authorization:ApiKey",
- "value": "[parameters('semanticKernelApiKey')]"
+ "name": "Authentication:Type",
+ "value": "None"
},
{
"name": "ChatStore:Type",
diff --git a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-new.bicep b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-new.bicep
index 81373a3e4551..1de0bbb77ac1 100644
--- a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-new.bicep
+++ b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-new.bicep
@@ -25,9 +25,6 @@ param embeddingModel string = 'text-embedding-ada-002'
@description('Completion model the task planner should use')
param plannerModel string = 'gpt-35-turbo'
-@description('Semantic Kernel server API key - Generated GUID by default (Provide empty string to disable API key auth)')
-param semanticKernelApiKey string = newGuid()
-
@description('Whether to deploy Cosmos DB for chat storage')
param deployCosmosDB bool = true
@@ -48,7 +45,6 @@ module semanticKernel 'main.bicep' = {
completionModel: completionModel
embeddingModel: embeddingModel
plannerModel: plannerModel
- semanticKernelApiKey: semanticKernelApiKey
deployCosmosDB: deployCosmosDB
deployQdrant: deployQdrant
deploySpeechServices: deploySpeechServices
diff --git a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-new.json b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-new.json
index 46902eac9e90..18ee3a397e9c 100644
--- a/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-new.json
+++ b/samples/apps/copilot-chat-app/webapi/DeploymentTemplates/sk-new.json
@@ -61,13 +61,6 @@
"description": "Completion model the task planner should use"
}
},
- "semanticKernelApiKey": {
- "type": "string",
- "defaultValue": "[newGuid()]",
- "metadata": {
- "description": "Semantic Kernel server API key - Generated GUID by default (Provide empty string to disable API key auth)"
- }
- },
"deployCosmosDB": {
"type": "bool",
"defaultValue": true,
@@ -122,9 +115,6 @@
"plannerModel": {
"value": "[parameters('plannerModel')]"
},
- "semanticKernelApiKey": {
- "value": "[parameters('semanticKernelApiKey')]"
- },
"deployCosmosDB": {
"value": "[parameters('deployCosmosDB')]"
},
@@ -226,13 +216,6 @@
"description": "Azure OpenAI or OpenAI API key"
}
},
- "semanticKernelApiKey": {
- "type": "string",
- "defaultValue": "[newGuid()]",
- "metadata": {
- "description": "Semantic Kernel server API key - Generated GUID by default (Provide empty string to disable API key auth)"
- }
- },
"deployNewAzureOpenAI": {
"type": "bool",
"defaultValue": true,
@@ -398,12 +381,8 @@
"value": "[parameters('plannerModel')]"
},
{
- "name": "Authorization:Type",
- "value": "[if(empty(parameters('semanticKernelApiKey')), 'None', 'ApiKey')]"
- },
- {
- "name": "Authorization:ApiKey",
- "value": "[parameters('semanticKernelApiKey')]"
+ "name": "Authentication:Type",
+ "value": "None"
},
{
"name": "ChatStore:Type",
diff --git a/samples/apps/copilot-chat-app/webapi/Models/Ask.cs b/samples/apps/copilot-chat-app/webapi/Models/Ask.cs
index d72f95b3025e..2406dceb906c 100644
--- a/samples/apps/copilot-chat-app/webapi/Models/Ask.cs
+++ b/samples/apps/copilot-chat-app/webapi/Models/Ask.cs
@@ -7,7 +7,7 @@ namespace SemanticKernel.Service.Models;
public class Ask
{
- public string Input { get; set; } = string.Empty;
+ public virtual string Input { get; set; } = string.Empty;
public IEnumerable> Variables { get; set; } = Enumerable.Empty>();
}
diff --git a/samples/apps/copilot-chat-app/webapi/Program.cs b/samples/apps/copilot-chat-app/webapi/Program.cs
index a796e1373bc4..6529571d77a6 100644
--- a/samples/apps/copilot-chat-app/webapi/Program.cs
+++ b/samples/apps/copilot-chat-app/webapi/Program.cs
@@ -2,12 +2,14 @@
using System;
using System.Linq;
+using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using SemanticKernel.Service.CopilotChat.Extensions;
@@ -42,24 +44,34 @@ public static async Task Main(string[] args)
builder.Services
.AddCopilotChatOptions(builder.Configuration)
.AddCopilotChatPlannerServices()
- .AddPersistentChatStore();
+ .AddPersistentChatStore()
+ .AddUtilities()
+ .AddCopilotChatAuthentication(builder.Configuration)
+ .AddCopilotChatAuthorization();
// Add in the rest of the services.
builder.Services
.AddApplicationInsightsTelemetry()
.AddLogging(logBuilder => logBuilder.AddApplicationInsights())
- .AddAuthorization(builder.Configuration)
.AddEndpointsApiExplorer()
.AddSwaggerGen()
.AddCors()
- .AddControllers();
+ .AddControllers()
+ .AddJsonOptions(options =>
+ {
+ options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
+ });
+ builder.Services.AddHealthChecks().AddCheck("Default",
+ () => HealthCheckResult.Healthy("Semantic Kernel service up and running"));
// Configure middleware and endpoints
WebApplication app = builder.Build();
app.UseCors();
+ app.MapHealthChecks("/healthz");
app.UseAuthentication();
app.UseAuthorization();
- app.MapControllers();
+ app.MapControllers()
+ .RequireAuthorization();
// Enable Swagger for development environments.
if (app.Environment.IsDevelopment())
@@ -75,7 +87,7 @@ public static async Task Main(string[] args)
try
{
string? address = app.Services.GetRequiredService().Features.Get()?.Addresses.FirstOrDefault();
- app.Services.GetRequiredService().LogInformation("Health probe: {0}/probe", address);
+ app.Services.GetRequiredService().LogInformation("Health probe: {0}/heaalthz", address);
}
catch (ObjectDisposedException)
{
diff --git a/samples/apps/copilot-chat-app/webapi/README.md b/samples/apps/copilot-chat-app/webapi/README.md
index d4701084ce7b..d3ed7d75048f 100644
--- a/samples/apps/copilot-chat-app/webapi/README.md
+++ b/samples/apps/copilot-chat-app/webapi/README.md
@@ -38,11 +38,11 @@ You can start the WebApi service using the command-line, Visual Studio Code, or
```
dotnet run
```
-1. Early in the startup, the service will provide a probe endpoint you can use in a web browser to verify
+1. Early in the startup, the service will provide a healthz endpoint you can use in a web browser to verify
the service is running.
```
info: Microsoft.SemanticKernel.Kernel[0]
- Health probe: https://localhost:40443/probe
+ Health probe: https://localhost:40443/healthz
```
## Visual Studio Code
diff --git a/samples/apps/copilot-chat-app/webapi/ServiceExtensions.cs b/samples/apps/copilot-chat-app/webapi/ServiceExtensions.cs
index 1b8768e01d11..613ea66f83e7 100644
--- a/samples/apps/copilot-chat-app/webapi/ServiceExtensions.cs
+++ b/samples/apps/copilot-chat-app/webapi/ServiceExtensions.cs
@@ -3,14 +3,11 @@
using System;
using System.Collections.Generic;
using System.Reflection;
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
-using Microsoft.Identity.Web;
-using SemanticKernel.Service.Auth;
using SemanticKernel.Service.Options;
+using SemanticKernel.Service.Utilities;
namespace SemanticKernel.Service;
@@ -37,13 +34,6 @@ internal static IServiceCollection AddOptions(this IServiceCollection services,
var foo = services.BuildServiceProvider().GetService>();
- // Authorization configuration
- services.AddOptions()
- .Bind(configuration.GetSection(AuthorizationOptions.PropertyName))
- .ValidateOnStart()
- .ValidateDataAnnotations()
- .PostConfigure(TrimStringProperties);
-
// Memory store configuration
services.AddOptions()
.Bind(configuration.GetSection(MemoriesStoreOptions.PropertyName))
@@ -54,6 +44,11 @@ internal static IServiceCollection AddOptions(this IServiceCollection services,
return services;
}
+ internal static IServiceCollection AddUtilities(this IServiceCollection services)
+ {
+ return services.AddScoped();
+ }
+
///
/// Add CORS settings.
///
@@ -77,40 +72,6 @@ internal static IServiceCollection AddCors(this IServiceCollection services)
return services;
}
- ///
- /// Add authorization services
- ///
- internal static IServiceCollection AddAuthorization(this IServiceCollection services, IConfiguration configuration)
- {
- AuthorizationOptions config = services.BuildServiceProvider().GetRequiredService>().Value;
- switch (config.Type)
- {
- case AuthorizationOptions.AuthorizationType.AzureAd:
- services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
- .AddMicrosoftIdentityWebApi(configuration.GetSection($"{AuthorizationOptions.PropertyName}:AzureAd"));
- break;
-
- case AuthorizationOptions.AuthorizationType.ApiKey:
- services.AddAuthentication(ApiKeyAuthenticationHandler.AuthenticationScheme)
- .AddScheme(
- ApiKeyAuthenticationHandler.AuthenticationScheme,
- options => options.ApiKey = config.ApiKey);
- break;
-
- case AuthorizationOptions.AuthorizationType.None:
- services.AddAuthentication(PassThroughAuthenticationHandler.AuthenticationScheme)
- .AddScheme(
- authenticationScheme: PassThroughAuthenticationHandler.AuthenticationScheme,
- configureOptions: null);
- break;
-
- default:
- throw new InvalidOperationException($"Invalid authorization type '{config.Type}'.");
- }
-
- return services;
- }
-
///
/// Trim all string properties, recursively.
///
diff --git a/samples/apps/copilot-chat-app/webapi/Utilities/AskConverter.cs b/samples/apps/copilot-chat-app/webapi/Utilities/AskConverter.cs
new file mode 100644
index 000000000000..f5e38871dc9f
--- /dev/null
+++ b/samples/apps/copilot-chat-app/webapi/Utilities/AskConverter.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.SemanticKernel.Orchestration;
+using SemanticKernel.Service.Auth;
+using SemanticKernel.Service.Models;
+
+namespace SemanticKernel.Service.Utilities;
+
+///
+/// Converts variables to , inserting some system variables along the way.
+///
+public class AskConverter
+{
+ private readonly IAuthInfo _authInfo;
+
+ public AskConverter(IAuthInfo authInfo)
+ {
+ this._authInfo = authInfo;
+ }
+
+ ///
+ /// Converts variables to , inserting some system variables along the way.
+ ///
+ public ContextVariables GetContextVariables(Ask ask)
+ {
+ const string userIdKey = "userId";
+ const string userNameKey = "userName";
+ var contextVariables = new ContextVariables(ask.Input);
+ foreach (var input in ask.Variables)
+ {
+ if (input.Key != userIdKey && input.Key != userNameKey)
+ {
+ contextVariables.Set(input.Key, input.Value);
+ }
+ }
+
+ contextVariables.Set(userIdKey, this._authInfo.UserId);
+ contextVariables.Set(userNameKey, this._authInfo.Name);
+ return contextVariables;
+ }
+}
diff --git a/samples/apps/copilot-chat-app/webapi/appsettings.json b/samples/apps/copilot-chat-app/webapi/appsettings.json
index 3cdd6aa4f6f6..622a3bfcd4db 100644
--- a/samples/apps/copilot-chat-app/webapi/appsettings.json
+++ b/samples/apps/copilot-chat-app/webapi/appsettings.json
@@ -60,14 +60,11 @@
},
//
- // Authorization configuration to gate access to the service.
- // - Supported Types are "None", "ApiKey", or "AzureAd".
- // - Set ApiKey using dotnet's user secrets (see above)
- // (i.e. dotnet user-secret set "Authorization:ApiKey" "MY_API_KEY")
+ // Authentication configuration to gate access to the service.
+ // - Supported Types are "None", or "AzureAd".
//
- "Authorization": {
+ "Authentication": {
"Type": "None",
- "ApiKey": "",
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "",
diff --git a/samples/apps/copilot-chat-app/webapp/src/components/chat/ChatInput.tsx b/samples/apps/copilot-chat-app/webapp/src/components/chat/ChatInput.tsx
index 5b49b09b121b..d70f6b410b86 100644
--- a/samples/apps/copilot-chat-app/webapp/src/components/chat/ChatInput.tsx
+++ b/samples/apps/copilot-chat-app/webapp/src/components/chat/ChatInput.tsx
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
-import { useAccount, useMsal } from '@azure/msal-react';
+import { useMsal } from '@azure/msal-react';
import { Button, Spinner, Textarea, makeStyles, shorthands, tokens } from '@fluentui/react-components';
import { AttachRegular, MicRegular, SendRegular } from '@fluentui/react-icons';
import debug from 'debug';
@@ -63,8 +63,7 @@ interface ChatInputProps {
export const ChatInput: React.FC = (props) => {
const { isTyping, onSubmit } = props;
const classes = useClasses();
- const { instance, accounts, inProgress } = useMsal();
- const account = useAccount(accounts[0] || {});
+ const { instance, inProgress } = useMsal();
const dispatch = useAppDispatch();
const [value, setValue] = React.useState('');
const [previousValue, setPreviousValue] = React.useState('');
@@ -112,7 +111,6 @@ export const ChatInput: React.FC = (props) => {
try {
SetDocumentImporting(true);
await documentImportService.importDocumentAsync(
- account!.homeAccountId!,
selectedId,
documentFile,
await AuthHelper.getSKaaSAccessToken(instance, inProgress),
diff --git a/samples/apps/copilot-chat-app/webapp/src/components/views/BackendProbe.tsx b/samples/apps/copilot-chat-app/webapp/src/components/views/BackendProbe.tsx
index e70411301112..3e45ca93fe01 100644
--- a/samples/apps/copilot-chat-app/webapp/src/components/views/BackendProbe.tsx
+++ b/samples/apps/copilot-chat-app/webapp/src/components/views/BackendProbe.tsx
@@ -10,7 +10,7 @@ interface IData {
const BackendProbe: FC = ({ uri, onBackendFound }) => {
useEffect(() => {
- const requestUrl = new URL('probe', uri);
+ const requestUrl = new URL('healthz', uri);
const fetchAsync = async () => {
try {
var result = await fetch(requestUrl);
diff --git a/samples/apps/copilot-chat-app/webapp/src/libs/services/BotService.ts b/samples/apps/copilot-chat-app/webapp/src/libs/services/BotService.ts
index e06d045bb9da..f93f36c87405 100644
--- a/samples/apps/copilot-chat-app/webapp/src/libs/services/BotService.ts
+++ b/samples/apps/copilot-chat-app/webapp/src/libs/services/BotService.ts
@@ -19,11 +19,11 @@ export class BotService extends BaseService {
return result;
};
- public uploadAsync = async (bot: Bot, userId: string, accessToken: string): Promise => {
+ public uploadAsync = async (bot: Bot, accessToken: string): Promise => {
// TODO: return type
const result = await this.getResponseAsync(
{
- commandPath: `bot/upload?userId=${userId}`,
+ commandPath: `bot/upload`,
method: 'Post',
body: bot,
},
diff --git a/samples/apps/copilot-chat-app/webapp/src/libs/services/ChatService.ts b/samples/apps/copilot-chat-app/webapp/src/libs/services/ChatService.ts
index c172dfbbaae9..bd888152b5ee 100644
--- a/samples/apps/copilot-chat-app/webapp/src/libs/services/ChatService.ts
+++ b/samples/apps/copilot-chat-app/webapp/src/libs/services/ChatService.ts
@@ -9,22 +9,18 @@ import { BaseService } from './BaseService';
export class ChatService extends BaseService {
public createChatAsync = async (
- userId: string,
- userName: string,
title: string,
accessToken: string,
): Promise => {
const body = {
- userId: userId,
- userName: userName,
- title: title,
+ title,
};
const result = await this.getResponseAsync(
{
- commandPath: 'chatSession/create',
+ commandPath: 'chatSessions',
method: 'POST',
- body: body,
+ body,
},
accessToken,
);
@@ -35,7 +31,7 @@ export class ChatService extends BaseService {
public getChatAsync = async (chatId: string, accessToken: string): Promise => {
const result = await this.getResponseAsync(
{
- commandPath: `chatSession/getChat/${chatId}`,
+ commandPath: `chatSessions/${chatId}`,
method: 'GET',
},
accessToken,
@@ -44,10 +40,10 @@ export class ChatService extends BaseService {
return result;
};
- public getAllChatsAsync = async (userId: string, accessToken: string): Promise => {
+ public getAllChatsAsync = async (accessToken: string): Promise => {
const result = await this.getResponseAsync(
{
- commandPath: `chatSession/getAllChats/${userId}`,
+ commandPath: `chatSessions`,
method: 'GET',
},
accessToken,
@@ -63,7 +59,7 @@ export class ChatService extends BaseService {
): Promise => {
const result = await this.getResponseAsync(
{
- commandPath: `chatSession/getChatMessages/${chatId}?startIdx=${startIdx}&count=${count}`,
+ commandPath: `chatSessions/${chatId}/messages?startIdx=${startIdx}&count=${count}`,
method: 'GET',
},
accessToken,
@@ -75,17 +71,15 @@ export class ChatService extends BaseService {
};
public editChatAsync = async (chatId: string, title: string, accessToken: string): Promise => {
- const body: IChatSession = {
- id: chatId,
- userId: '',
- title: title,
+ const body: Partial = {
+ title,
};
const result = await this.getResponseAsync(
{
- commandPath: `chatSession/edit`,
- method: 'POST',
- body: body,
+ commandPath: `chatSessions/${chatId}`,
+ method: 'PATCH',
+ body,
},
accessToken,
);
diff --git a/samples/apps/copilot-chat-app/webapp/src/libs/services/DocumentImportService.ts b/samples/apps/copilot-chat-app/webapp/src/libs/services/DocumentImportService.ts
index b983bf87ecd6..9e5954e9b99c 100644
--- a/samples/apps/copilot-chat-app/webapp/src/libs/services/DocumentImportService.ts
+++ b/samples/apps/copilot-chat-app/webapp/src/libs/services/DocumentImportService.ts
@@ -3,9 +3,13 @@
import { BaseService } from './BaseService';
export class DocumentImportService extends BaseService {
- public importDocumentAsync = async (userId: string, chatId: string, document: File, accessToken: string) => {
+
+ importDocumentAsync = async (
+ chatId: string,
+ document: File,
+ accessToken: string,
+ ) => {
const formData = new FormData();
- formData.append('userId', userId);
formData.append('chatId', chatId);
formData.append('documentScope', 'Chat');
formData.append('formFile', document);
diff --git a/samples/apps/copilot-chat-app/webapp/src/libs/useChat.ts b/samples/apps/copilot-chat-app/webapp/src/libs/useChat.ts
index 7f8bac59113e..fb59290c5efd 100644
--- a/samples/apps/copilot-chat-app/webapp/src/libs/useChat.ts
+++ b/samples/apps/copilot-chat-app/webapp/src/libs/useChat.ts
@@ -61,8 +61,6 @@ export const useChat = () => {
try {
await chatService
.createChatAsync(
- account?.homeAccountId!,
- account?.name ?? account?.username!,
chatTitle,
await AuthHelper.getSKaaSAccessToken(instance, inProgress),
)
@@ -101,14 +99,6 @@ export const useChat = () => {
const ask = {
input: value,
variables: [
- {
- key: 'userId',
- value: account?.homeAccountId!,
- },
- {
- key: 'userName',
- value: account?.name ?? account?.username!,
- },
{
key: 'chatId',
value: chatId,
@@ -162,7 +152,6 @@ export const useChat = () => {
const loadChats = async () => {
try {
const chatSessions = await chatService.getAllChatsAsync(
- account?.homeAccountId!,
await AuthHelper.getSKaaSAccessToken(instance, inProgress),
);
@@ -213,7 +202,7 @@ export const useChat = () => {
const uploadBot = async (bot: Bot) => {
botService
- .uploadAsync(bot, account?.homeAccountId || '', await AuthHelper.getSKaaSAccessToken(instance, inProgress))
+ .uploadAsync(bot, await AuthHelper.getSKaaSAccessToken(instance, inProgress))
.then(async (chatSession: IChatSession) => {
const chatMessages = await chatService.getChatMessagesAsync(
chatSession.id,