-
Notifications
You must be signed in to change notification settings - Fork 207
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[C#] feat: add teams sso bot auth sample
- Loading branch information
1 parent
035e49e
commit e803676
Showing
26 changed files
with
1,265 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# TeamsFx files | ||
build | ||
appPackage/build | ||
env/.env.*.user | ||
env/.env.local | ||
appsettings.Development.json | ||
.deployment | ||
|
||
# User-specific files | ||
*.user | ||
|
||
# Build results | ||
[Dd]ebug/ | ||
[Dd]ebugPublic/ | ||
[Rr]elease/ | ||
[Rr]eleases/ | ||
x64/ | ||
x86/ | ||
bld/ | ||
[Bb]in/ | ||
[Oo]bj/ | ||
[Ll]og/ | ||
|
||
# Notification local store | ||
.notification.localstore.json | ||
|
||
# Visual Studio files | ||
.vs/ |
29 changes: 29 additions & 0 deletions
29
dotnet/samples/06.auth.teamsSSO.bot/AdapterWithErrorHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
using Microsoft.Bot.Builder.Integration.AspNet.Core; | ||
using Microsoft.Bot.Builder.TraceExtensions; | ||
using Microsoft.Bot.Connector.Authentication; | ||
|
||
namespace BotAuth | ||
{ | ||
public class AdapterWithErrorHandler : CloudAdapter | ||
{ | ||
public AdapterWithErrorHandler(BotFrameworkAuthentication auth, ILogger<CloudAdapter> logger) | ||
: base(auth, logger) | ||
{ | ||
OnTurnError = async (turnContext, exception) => | ||
{ | ||
// Log any leaked exception from the application. | ||
// NOTE: In production environment, you should consider logging this to | ||
// Azure Application Insights. Visit https://aka.ms/bottelemetry to see how | ||
// to add telemetry capture to your bot. | ||
logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}"); | ||
|
||
// Send a message to the user | ||
await turnContext.SendActivityAsync($"The bot encountered an unhandled error: {exception.Message}"); | ||
await turnContext.SendActivityAsync("To continue to run this bot, please fix the bot source code."); | ||
|
||
// Send a trace activity | ||
await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError"); | ||
}; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
<Project Sdk="Microsoft.NET.Sdk.Web"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectCapability Include="TeamsFx" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<None Remove="build/**/*" /> | ||
<Content Remove="build/**/*" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="AdaptiveCards.Templating" Version="1.3.1" /> | ||
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Version="4.21.1" /> | ||
<PackageReference Include="Microsoft.Identity.Web.TokenCache" Version="2.16.0" /> | ||
<PackageReference Include="Microsoft.Teams.AI" Version="1.0.*-*" /> | ||
</ItemGroup> | ||
|
||
<!-- Exclude Teams Toolkit files from build output, but can still be viewed from Solution Explorer --> | ||
<ItemGroup> | ||
<Content Remove="appPackage/**/*" /> | ||
<None Include="appPackage/**/*" /> | ||
<None Include="env/**/*" /> | ||
<Content Remove="infra/**/*" /> | ||
<None Include="infra/**/*" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Folder Include="assets\" /> | ||
</ItemGroup> | ||
|
||
<!-- Exclude local settings from publish --> | ||
<ItemGroup> | ||
<Content Remove="appsettings.Development.json" /> | ||
<Content Include="appsettings.Development.json"> | ||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
<CopyToPublishDirectory>None</CopyToPublishDirectory> | ||
</Content> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 17 | ||
VisualStudioVersion = 17.7.33906.173 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotAuth", "BotAuth.csproj", "{D045C9A3-F421-4E8B-91D0-33A62C61DCCD}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{D045C9A3-F421-4E8B-91D0-33A62C61DCCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{D045C9A3-F421-4E8B-91D0-33A62C61DCCD}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{D045C9A3-F421-4E8B-91D0-33A62C61DCCD}.Debug|Any CPU.Deploy.0 = Debug|Any CPU | ||
{D045C9A3-F421-4E8B-91D0-33A62C61DCCD}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{D045C9A3-F421-4E8B-91D0-33A62C61DCCD}.Release|Any CPU.Build.0 = Release|Any CPU | ||
{D045C9A3-F421-4E8B-91D0-33A62C61DCCD}.Release|Any CPU.Deploy.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {1A3065E4-A54D-45EE-BDCB-1BADCD6EA7CA} | ||
EndGlobalSection | ||
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
namespace BotAuth | ||
{ | ||
public class ConfigOptions | ||
{ | ||
public string BOT_ID { get; set; } | ||
public string BOT_PASSWORD { get; set; } | ||
public string BOT_DOMAIN { get; set; } | ||
public string AAD_APP_CLIENT_ID { get; set; } | ||
public string AAD_APP_CLIENT_SECRET { get; set; } | ||
public string AAD_APP_TENANT_ID { get; set; } | ||
public string AAD_APP_OAUTH_AUTHORITY_HOST { get; set; } | ||
|
||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
dotnet/samples/06.auth.teamsSSO.bot/Controllers/BotController.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.Bot.Builder; | ||
using Microsoft.Bot.Builder.Integration.AspNet.Core; | ||
|
||
namespace BotAuth.Controllers | ||
{ | ||
[Route("api/messages")] | ||
[ApiController] | ||
public class BotController : ControllerBase | ||
{ | ||
private readonly IBotFrameworkHttpAdapter _adapter; | ||
private readonly IBot _bot; | ||
|
||
public BotController(IBotFrameworkHttpAdapter adapter, IBot bot) | ||
{ | ||
_adapter = adapter; | ||
_bot = bot; | ||
} | ||
|
||
[HttpPost] | ||
public async Task PostAsync(CancellationToken cancellationToken = default) | ||
{ | ||
await _adapter.ProcessAsync | ||
( | ||
Request, | ||
Response, | ||
_bot, | ||
cancellationToken | ||
); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
using Microsoft.Teams.AI.State; | ||
|
||
namespace BotAuth.Model | ||
{ | ||
// Extend the turn state by configuring custom strongly typed state classes. | ||
public class AppState : TurnState | ||
{ | ||
public AppState() : base() | ||
{ | ||
ScopeDefaults[CONVERSATION_SCOPE] = new ConversationState(); | ||
} | ||
|
||
/// <summary> | ||
/// Stores all the conversation-related state. | ||
/// </summary> | ||
public new ConversationState Conversation | ||
{ | ||
get | ||
{ | ||
TurnStateEntry scope = GetScope(CONVERSATION_SCOPE); | ||
if (scope == null) | ||
{ | ||
throw new ArgumentException("TurnState hasn't been loaded. Call LoadStateAsync() first."); | ||
} | ||
|
||
return (ConversationState)scope.Value!; | ||
} | ||
set | ||
{ | ||
TurnStateEntry scope = GetScope(CONVERSATION_SCOPE); | ||
if (scope == null) | ||
{ | ||
throw new ArgumentException("TurnState hasn't been loaded. Call LoadStateAsync() first."); | ||
} | ||
|
||
scope.Replace(value!); | ||
} | ||
} | ||
} | ||
|
||
// This class adds custom properties to the turn state which will be accessible in the activity handler methods. | ||
public class ConversationState : Record | ||
{ | ||
private const string _countKey = "countKey"; | ||
|
||
public int MessageCount | ||
{ | ||
get => Get<int>(_countKey); | ||
set => Set(_countKey, value); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
using Microsoft.Bot.Builder; | ||
using Microsoft.Bot.Builder.Integration.AspNet.Core; | ||
using Microsoft.Bot.Connector.Authentication; | ||
using Microsoft.Bot.Schema; | ||
using Microsoft.Identity.Client; | ||
using Microsoft.Teams.AI; | ||
using Microsoft.Identity.Web; | ||
using BotAuth; | ||
using BotAuth.Model; | ||
|
||
var builder = WebApplication.CreateBuilder(args); | ||
|
||
builder.Services.AddControllers(); | ||
builder.Services.AddHttpClient("WebClient", client => client.Timeout = TimeSpan.FromSeconds(600)); | ||
builder.Services.AddHttpContextAccessor(); | ||
builder.Logging.AddConsole(); | ||
|
||
// Prepare Configuration for ConfigurationBotFrameworkAuthentication | ||
var config = builder.Configuration.Get<ConfigOptions>(); | ||
builder.Configuration["MicrosoftAppType"] = "MultiTenant"; | ||
builder.Configuration["MicrosoftAppId"] = config.BOT_ID; | ||
builder.Configuration["MicrosoftAppPassword"] = config.BOT_PASSWORD; | ||
|
||
// Create the Bot Framework Authentication to be used with the Bot Adapter. | ||
builder.Services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>(); | ||
|
||
// Create the Cloud Adapter with error handling enabled. | ||
// Note: some classes expect a BotAdapter and some expect a BotFrameworkHttpAdapter, so | ||
// register the same adapter instance for both types. | ||
builder.Services.AddSingleton<CloudAdapter, AdapterWithErrorHandler>(); | ||
builder.Services.AddSingleton<IBotFrameworkHttpAdapter>(sp => sp.GetService<CloudAdapter>()); | ||
|
||
// Create the storage to persist turn state | ||
builder.Services.AddSingleton<IStorage, MemoryStorage>(); | ||
|
||
builder.Services.AddSingleton<IConfidentialClientApplication>(sp => | ||
{ | ||
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(config.AAD_APP_CLIENT_ID) | ||
.WithClientSecret(config.AAD_APP_CLIENT_SECRET) | ||
.WithTenantId(config.AAD_APP_TENANT_ID) | ||
.WithLegacyCacheCompatibility(false) | ||
.Build(); | ||
app.AddInMemoryTokenCache(); // For development purpose only, use distributed cache in production environment | ||
return app; | ||
}); | ||
|
||
// Create the bot as a transient. In this case the ASP Controller is expecting an IBot. | ||
builder.Services.AddTransient<IBot>(sp => | ||
{ | ||
IStorage storage = sp.GetService<IStorage>(); | ||
IConfidentialClientApplication msal = sp.GetService<IConfidentialClientApplication>(); | ||
string signInLink = $"https://{config.BOT_DOMAIN}/auth-start.html"; | ||
ApplicationOptions<AppState> applicationOptions = new() | ||
{ | ||
Storage = storage, | ||
TurnStateFactory = () => | ||
{ | ||
return new AppState(); | ||
}, | ||
Authentication = new AuthenticationOptions<AppState>( | ||
new Dictionary<string, IAuthentication<AppState>>() | ||
{ | ||
{ "graph", new TeamsSsoAuthentication<AppState>(new TeamsSsoSettings(new string[]{"User.Read"}, signInLink, msal)) } | ||
} | ||
) | ||
}; | ||
|
||
Application<AppState> app = new(applicationOptions); | ||
|
||
// Listen for user to say "/reset" and then delete conversation state | ||
app.OnMessage("/reset", async (turnContext, turnState, cancellationToken) => | ||
{ | ||
turnState.DeleteConversationState(); | ||
await turnContext.SendActivityAsync("Ok I've deleted the current conversation state", cancellationToken: cancellationToken); | ||
}); | ||
|
||
// Listen for user to say "/sigout" and then delete cached token | ||
app.OnMessage("/signout", async (context, state, cancellationToken) => | ||
{ | ||
await app.Authentication.SignOutUserAsync(context, state, cancellationToken: cancellationToken); | ||
|
||
await context.SendActivityAsync("You have signed out"); | ||
}); | ||
|
||
// Listen for ANY message to be received. MUST BE AFTER ANY OTHER MESSAGE HANDLERS | ||
app.OnActivity(ActivityTypes.Message, async (turnContext, turnState, cancellationToken) => | ||
{ | ||
int count = turnState.Conversation.MessageCount; | ||
|
||
// Increment count state. | ||
turnState.Conversation.MessageCount = ++count; | ||
|
||
await turnContext.SendActivityAsync($"[{count}] you said: {turnContext.Activity.Text}", cancellationToken: cancellationToken); | ||
}); | ||
|
||
app.Authentication.Get("graph").OnUserSignInSuccess(async (context, state) => | ||
{ | ||
// Successfully logged in | ||
await context.SendActivityAsync("Successfully logged in"); | ||
await context.SendActivityAsync($"Token string length: {state.Temp.AuthTokens["graph"].Length}"); | ||
await context.SendActivityAsync($"This is what you said before the AuthFlow started: {context.Activity.Text}"); | ||
}); | ||
|
||
app.Authentication.Get("graph").OnUserSignInFailure(async (context, state, ex) => | ||
{ | ||
// Failed to login | ||
await context.SendActivityAsync("Failed to login"); | ||
await context.SendActivityAsync($"Error message: {ex.Message}"); | ||
}); | ||
|
||
return app; | ||
}); | ||
|
||
var app = builder.Build(); | ||
|
||
if (app.Environment.IsDevelopment()) | ||
{ | ||
app.UseDeveloperExceptionPage(); | ||
} | ||
|
||
app.UseStaticFiles(); | ||
app.UseRouting(); | ||
app.UseEndpoints(endpoints => | ||
{ | ||
endpoints.MapControllers(); | ||
}); | ||
|
||
app.Run(); |
25 changes: 25 additions & 0 deletions
25
dotnet/samples/06.auth.teamsSSO.bot/Properties/launchSettings.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"profiles": { | ||
"Microsoft Teams (browser)": { | ||
"commandName": "Project", | ||
"launchBrowser": true, | ||
"launchUrl": "https://teams.microsoft.com/", | ||
"environmentVariables": { | ||
"ASPNETCORE_ENVIRONMENT": "Development" | ||
}, | ||
"dotnetRunMessages": true, | ||
"applicationUrl": "http://localhost:5130", | ||
"hotReloadProfile": "aspnetcore" | ||
}, | ||
"WSL": { | ||
"commandName": "WSL2", | ||
"launchBrowser": true, | ||
"launchUrl": "https://teams.microsoft.com/", | ||
"environmentVariables": { | ||
"ASPNETCORE_ENVIRONMENT": "Development", | ||
"ASPNETCORE_URLS": "http://localhost:5130" | ||
}, | ||
"distributionName": "" | ||
} | ||
} | ||
} |
Oops, something went wrong.