├── .dockerignore ├── .gitignore ├── .idea └── .idea.ConfTool │ └── .idea │ ├── .gitignore │ ├── .name │ ├── indexLayout.xml │ └── vcs.xml ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── Client ├── App.razor ├── Authentication │ ├── Authentication.razor │ ├── LoginDisplay.razor │ └── RedirectToLogin.razor ├── ClientCodeMap.dgml ├── Common │ ├── Collapsible.razor │ ├── MainLayout.razor │ ├── MainLayout.razor.css │ ├── NavMenu.razor │ └── NavMenu.razor.css ├── ConfTool.Client.csproj ├── Counter │ └── Counter.razor ├── Index.razor ├── Infrastructure │ ├── ClientLoggerInterceptor.cs │ ├── Configuration.cs │ └── pwa │ │ ├── commands.txt │ │ └── workbox-config.js ├── Program.cs ├── Properties │ └── launchSettings.json ├── Time │ └── Time.razor ├── Webcam │ ├── IWebcamService.cs │ ├── Webcam.razor │ ├── Webcam.razor.css │ ├── WebcamOptions.cs │ ├── WebcamService.cs │ └── WebcamServiceCollectionExtensions.cs ├── _Imports.razor └── wwwroot │ ├── appsettings.json │ ├── css │ ├── app.css │ ├── bootstrap │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ └── open-iconic │ │ ├── FONT-LICENSE │ │ ├── ICON-LICENSE │ │ ├── README.md │ │ └── font │ │ ├── css │ │ └── open-iconic-bootstrap.min.css │ │ └── fonts │ │ ├── open-iconic.eot │ │ ├── open-iconic.otf │ │ ├── open-iconic.svg │ │ ├── open-iconic.ttf │ │ └── open-iconic.woff │ ├── favicon.ico │ ├── icon-192.png │ ├── icon-512.png │ ├── index.html │ ├── jsinterop │ └── webcam.js │ ├── manifest.json │ ├── sw.js │ ├── sw.js.map │ ├── workbox-1a734021.js │ └── workbox-1a734021.js.map ├── ClientModules ├── ConferencesModule │ ├── Components │ │ ├── ConferenceComponentDisplayModes.cs │ │ └── ConferenceHeader.razor │ ├── ConfTool.ClientModules.Conferences.csproj │ ├── ConferencesModuleServiceCollectionExtensions.cs │ ├── Configuration.cs │ ├── Pages │ │ ├── Conference.razor │ │ ├── Conference.razor.cs │ │ └── Conferences.razor │ ├── Services │ │ ├── ConferencesServiceClientGrpc.cs │ │ ├── ConferencesServiceClientHttp.cs │ │ ├── CountriesServiceClient.cs │ │ ├── Dialog │ │ │ ├── DialogService.cs │ │ │ ├── DialogServiceCollectionExtensions.cs │ │ │ └── IDialogService.cs │ │ └── IConferencesServiceClient.cs │ ├── _Imports.razor │ └── wwwroot │ │ └── jsinterop │ │ └── dialog.js └── StatisticsModule │ ├── ConfTool.ClientModules.Statistics.csproj │ ├── Configuration.cs │ ├── Pages │ └── Statistics.razor │ ├── Services │ └── StatisticsServiceClient.cs │ ├── StatisticsModuleServiceCollectionExtensions.cs │ ├── _Imports.razor │ └── wwwroot │ ├── jsinterop │ └── wcpiechart.js │ └── libs │ └── wc-piechart │ ├── angular-scripts.js │ ├── polyfills.js │ └── wc-piechart.js ├── ConfTool.sln ├── IdentityServer4Demo ├── Api │ └── TestController.cs ├── Config.cs ├── DemoCorsPolicy.cs ├── DemoRedirectValidator.cs ├── Dockerfile ├── IdentityServer4Demo.csproj ├── IdentityServer4Demo.sln ├── Program.cs ├── Properties │ └── launchSettings.json ├── Quickstart │ ├── Account │ │ ├── AccountController.cs │ │ ├── AccountOptions.cs │ │ ├── ExternalController.cs │ │ ├── ExternalProvider.cs │ │ ├── LoggedOutViewModel.cs │ │ ├── LoginInputModel.cs │ │ ├── LoginViewModel.cs │ │ ├── LogoutInputModel.cs │ │ ├── LogoutViewModel.cs │ │ └── RedirectViewModel.cs │ ├── Consent │ │ ├── ConsentController.cs │ │ ├── ConsentInputModel.cs │ │ ├── ConsentOptions.cs │ │ ├── ConsentViewModel.cs │ │ ├── ProcessConsentResult.cs │ │ └── ScopeViewModel.cs │ ├── Device │ │ ├── DeviceAuthorizationInputModel.cs │ │ ├── DeviceAuthorizationViewModel.cs │ │ └── DeviceController.cs │ ├── Diagnostics │ │ ├── DiagnosticsController.cs │ │ └── DiagnosticsViewModel.cs │ ├── Extensions.cs │ ├── Grants │ │ ├── GrantsController.cs │ │ └── GrantsViewModel.cs │ ├── Home │ │ ├── ErrorViewModel.cs │ │ └── HomeController.cs │ ├── SecurityHeadersAttribute.cs │ └── TestUsers.cs ├── SameSiteHandlingExtensions.cs ├── Startup.cs ├── Views │ ├── Account │ │ ├── AccessDenied.cshtml │ │ ├── LoggedOut.cshtml │ │ ├── Login.cshtml │ │ └── Logout.cshtml │ ├── Consent │ │ └── Index.cshtml │ ├── Device │ │ ├── Success.cshtml │ │ ├── UserCodeCapture.cshtml │ │ └── UserCodeConfirmation.cshtml │ ├── Diagnostics │ │ └── Index.cshtml │ ├── Grants │ │ └── Index.cshtml │ ├── Home │ │ └── Index.cshtml │ ├── Shared │ │ ├── Error.cshtml │ │ ├── Redirect.cshtml │ │ ├── _Layout.cshtml │ │ ├── _Nav.cshtml │ │ ├── _ScopeListItem.cshtml │ │ └── _ValidationSummary.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml ├── appsettings.json ├── web.config └── wwwroot │ ├── css │ ├── site.css │ ├── site.min.css │ └── site.scss │ ├── favicon.ico │ ├── icon.jpg │ ├── icon.png │ ├── js │ ├── signin-redirect.js │ └── signout-redirect.js │ └── lib │ ├── bootstrap │ ├── LICENSE │ ├── README.md │ ├── dist │ │ ├── css │ │ │ ├── bootstrap-grid.css │ │ │ ├── bootstrap-grid.css.map │ │ │ ├── bootstrap-grid.min.css │ │ │ ├── bootstrap-grid.min.css.map │ │ │ ├── bootstrap-reboot.css │ │ │ ├── bootstrap-reboot.css.map │ │ │ ├── bootstrap-reboot.min.css │ │ │ ├── bootstrap-reboot.min.css.map │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ └── js │ │ │ ├── bootstrap.bundle.js │ │ │ ├── bootstrap.bundle.js.map │ │ │ ├── bootstrap.bundle.min.js │ │ │ ├── bootstrap.bundle.min.js.map │ │ │ ├── bootstrap.js │ │ │ ├── bootstrap.js.map │ │ │ ├── bootstrap.min.js │ │ │ └── bootstrap.min.js.map │ └── scss │ │ ├── _alert.scss │ │ ├── _badge.scss │ │ ├── _breadcrumb.scss │ │ ├── _button-group.scss │ │ ├── _buttons.scss │ │ ├── _card.scss │ │ ├── _carousel.scss │ │ ├── _close.scss │ │ ├── _code.scss │ │ ├── _custom-forms.scss │ │ ├── _dropdown.scss │ │ ├── _forms.scss │ │ ├── _functions.scss │ │ ├── _grid.scss │ │ ├── _images.scss │ │ ├── _input-group.scss │ │ ├── _jumbotron.scss │ │ ├── _list-group.scss │ │ ├── _media.scss │ │ ├── _mixins.scss │ │ ├── _modal.scss │ │ ├── _nav.scss │ │ ├── _navbar.scss │ │ ├── _pagination.scss │ │ ├── _popover.scss │ │ ├── _print.scss │ │ ├── _progress.scss │ │ ├── _reboot.scss │ │ ├── _root.scss │ │ ├── _spinners.scss │ │ ├── _tables.scss │ │ ├── _toasts.scss │ │ ├── _tooltip.scss │ │ ├── _transitions.scss │ │ ├── _type.scss │ │ ├── _utilities.scss │ │ ├── _variables.scss │ │ ├── bootstrap-grid.scss │ │ ├── bootstrap-reboot.scss │ │ ├── bootstrap.scss │ │ ├── mixins │ │ ├── _alert.scss │ │ ├── _background-variant.scss │ │ ├── _badge.scss │ │ ├── _border-radius.scss │ │ ├── _box-shadow.scss │ │ ├── _breakpoints.scss │ │ ├── _buttons.scss │ │ ├── _caret.scss │ │ ├── _clearfix.scss │ │ ├── _deprecate.scss │ │ ├── _float.scss │ │ ├── _forms.scss │ │ ├── _gradients.scss │ │ ├── _grid-framework.scss │ │ ├── _grid.scss │ │ ├── _hover.scss │ │ ├── _image.scss │ │ ├── _list-group.scss │ │ ├── _lists.scss │ │ ├── _nav-divider.scss │ │ ├── _pagination.scss │ │ ├── _reset-text.scss │ │ ├── _resize.scss │ │ ├── _screen-reader.scss │ │ ├── _size.scss │ │ ├── _table-row.scss │ │ ├── _text-emphasis.scss │ │ ├── _text-hide.scss │ │ ├── _text-truncate.scss │ │ ├── _transition.scss │ │ └── _visibility.scss │ │ ├── utilities │ │ ├── _align.scss │ │ ├── _background.scss │ │ ├── _borders.scss │ │ ├── _clearfix.scss │ │ ├── _display.scss │ │ ├── _embed.scss │ │ ├── _flex.scss │ │ ├── _float.scss │ │ ├── _overflow.scss │ │ ├── _position.scss │ │ ├── _screenreaders.scss │ │ ├── _shadows.scss │ │ ├── _sizing.scss │ │ ├── _spacing.scss │ │ ├── _stretched-link.scss │ │ ├── _text.scss │ │ └── _visibility.scss │ │ └── vendor │ │ └── _rfs.scss │ └── jquery │ ├── LICENSE.txt │ ├── README.md │ └── dist │ ├── jquery.js │ ├── jquery.min.js │ ├── jquery.min.map │ ├── jquery.slim.js │ ├── jquery.slim.min.js │ └── jquery.slim.min.map ├── README.md ├── Server ├── ConfTool.Server.csproj ├── Controllers │ ├── ConferencesController.cs │ ├── CountriesController.cs │ └── StatisticsController.cs ├── Dockerfile ├── GrpcServices │ ├── ConferencesServiceCodeFirst.cs │ ├── ConferencesServiceContractFirst.cs │ ├── CounterService.cs │ ├── GreeterService.cs │ └── TimeService.cs ├── Hubs │ └── ConferencesHub.cs ├── Model │ ├── AutoMapperProfile.cs │ ├── Conference.cs │ ├── ConferencesDbContext.cs │ └── DataGenerator.cs ├── Pages │ ├── Error.cshtml │ ├── Error.cshtml.cs │ └── Shared │ │ └── _Layout.cshtml ├── Program.cs ├── Properties │ └── launchSettings.json ├── ServerCodeMap.dgml ├── Startup.cs ├── appsettings.Development.json └── appsettings.json ├── Shared ├── ConfTool.Shared.csproj ├── Contracts │ ├── IConferencesService.cs │ └── ITimeService.cs ├── DTO │ ├── ConferenceDetails.cs │ ├── ConferenceDetailsRequest.cs │ └── ConferenceOverview.cs ├── Protos │ ├── ConferencesService.proto │ ├── CounterService.proto │ └── GreeterService.proto └── Validation │ └── ConferenceDetailsValidator.cs ├── Tests ├── CollapsibleTest.cs └── ConfTool.Tests.csproj ├── Tools ├── ConfTool.Tools.csproj └── Program.cs ├── nuget.config └── tye.yaml /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/bin 16 | **/charts 17 | **/docker-compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md -------------------------------------------------------------------------------- /.idea/.idea.ConfTool/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Rider ignored files 5 | /contentModel.xml 6 | /projectSettingsUpdater.xml 7 | /modules.xml 8 | /.idea.ConfTool.iml 9 | # Editor-based HTTP Client requests 10 | /httpRequests/ 11 | # Datasource local storage ignored files 12 | /dataSources/ 13 | /dataSources.local.xml 14 | -------------------------------------------------------------------------------- /.idea/.idea.ConfTool/.idea/.name: -------------------------------------------------------------------------------- 1 | ConfTool -------------------------------------------------------------------------------- /.idea/.idea.ConfTool/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/.idea.ConfTool/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch and Debug Hosted Blazor WebAssembly App", 9 | "type": "blazorwasm", 10 | "request": "launch", 11 | "hosted": true, 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/Server/bin/Debug/netcoreapp3.1/ConfTool.Server.dll", 14 | "cwd": "${workspaceFolder}/Server" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "testExplorer.addToEditorContextMenu": true 3 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/Server/ConfTool.Server.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/Server/ConfTool.Server.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/Server/ConfTool.Server.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /Client/App.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Authorization 2 | @using Microsoft.AspNetCore.Components.Routing 3 | 4 | @using ConfTool.Client.Authentication 5 | @using ConfTool.Client.Common 6 | 7 | 8 | 11 | 12 | 13 | 14 | @if (!context.User.Identity.IsAuthenticated) 15 | { 16 | 17 | } 18 | else 19 | { 20 |

You are not authorized to access this resource.

21 | } 22 |
23 |
24 |
25 | 26 | 27 |

Sorry, there's nothing at this address.

28 |
29 |
30 |
31 |
32 | -------------------------------------------------------------------------------- /Client/Authentication/Authentication.razor: -------------------------------------------------------------------------------- 1 | @page "/authentication/{action}" 2 | 3 | @using Microsoft.AspNetCore.Components.WebAssembly.Authentication 4 | 5 | 6 | 7 | @code{ 8 | [Parameter] public string Action { get; set; } 9 | } 10 | -------------------------------------------------------------------------------- /Client/Authentication/LoginDisplay.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Authorization 2 | @using Microsoft.AspNetCore.Components.WebAssembly.Authentication 3 | @using Microsoft.AspNetCore.Components.Web 4 | 5 | @inject NavigationManager _navigation 6 | @inject SignOutSessionStateManager _signOutManager 7 | 8 | 9 | 10 | Hello, @context.User.Identity.Name! 11 | 12 | 13 | 14 | Log in 15 | 16 | 17 | 18 | @code{ 19 | private async Task BeginSignOut(MouseEventArgs args) 20 | { 21 | await _signOutManager.SetSignOutState(); 22 | 23 | _navigation.NavigateTo("authentication/logout"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Client/Authentication/RedirectToLogin.razor: -------------------------------------------------------------------------------- 1 | @inject NavigationManager _navigation 2 | 3 | @code { 4 | protected override void OnInitialized() 5 | { 6 | _navigation.NavigateTo($"authentication/login?returnUrl={Uri.EscapeDataString(_navigation.Uri)}"); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Client/Common/Collapsible.razor: -------------------------------------------------------------------------------- 1 | 
2 | @ActionText 3 | 4 | @if (!Collapsed) 5 | { 6 |
7 | @ChildContent 8 |
9 | } 10 |
11 | 12 | @code { 13 | [Parameter] 14 | public RenderFragment ChildContent { get; set; } 15 | 16 | [Parameter] 17 | public bool Collapsed { get; set; } 18 | 19 | private string ActionText { get => Collapsed ? "Expand" : "Collapse"; } 20 | 21 | private void Toggle() 22 | { 23 | Collapsed = !Collapsed; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Client/Common/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 | @using ConfTool.Client.Authentication 4 | @using Blazored.Toast 5 | 6 | 7 | 8 |
9 | 12 | 13 |
14 |
15 | 16 | About 17 |
18 | 19 |
20 | @Body 21 |
22 |
23 |
24 | -------------------------------------------------------------------------------- /Client/Common/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | .main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-color: darkslategrey; 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | } 28 | 29 | .top-row a:first-child { 30 | overflow: hidden; 31 | text-overflow: ellipsis; 32 | } 33 | 34 | @media (max-width: 767.98px) { 35 | .top-row:not(.auth) { 36 | display: none; 37 | } 38 | 39 | .top-row.auth { 40 | justify-content: space-between; 41 | } 42 | 43 | .top-row a, .top-row .btn-link { 44 | margin-left: 0; 45 | } 46 | } 47 | 48 | @media (min-width: 768px) { 49 | .page { 50 | flex-direction: row; 51 | } 52 | 53 | .sidebar { 54 | width: 250px; 55 | height: 100vh; 56 | position: sticky; 57 | top: 0; 58 | } 59 | 60 | .top-row { 61 | position: sticky; 62 | top: 0; 63 | z-index: 1; 64 | } 65 | 66 | .main > div { 67 | padding-left: 2rem !important; 68 | padding-right: 1.5rem !important; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Client/Common/NavMenu.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Routing 2 | 3 | 9 | 10 |
11 | 43 |
44 | 45 | @code { 46 | private bool collapseNavMenu = true; 47 | 48 | private string NavMenuCssClass => collapseNavMenu ? "collapse" : null; 49 | 50 | private void ToggleNavMenu() 51 | { 52 | collapseNavMenu = !collapseNavMenu; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Client/Common/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | background-color: rgba(255, 255, 255, 0.1); 3 | } 4 | 5 | .top-row { 6 | height: 3.5rem; 7 | background-color: rgba(0,0,0,0.4); 8 | } 9 | 10 | .navbar-brand { 11 | font-size: 1.1rem; 12 | } 13 | 14 | .oi { 15 | width: 2rem; 16 | font-size: 1.1rem; 17 | vertical-align: text-top; 18 | top: -2px; 19 | } 20 | 21 | .nav-item { 22 | font-size: 0.9rem; 23 | padding-bottom: 0.5rem; 24 | } 25 | 26 | .nav-item:first-of-type { 27 | padding-top: 1rem; 28 | } 29 | 30 | .nav-item:last-of-type { 31 | padding-bottom: 1rem; 32 | } 33 | 34 | .nav-item ::deep a { 35 | color: #d7d7d7; 36 | border-radius: 4px; 37 | height: 3rem; 38 | display: flex; 39 | align-items: center; 40 | line-height: 3rem; 41 | } 42 | 43 | .nav-item ::deep a.active { 44 | background-color: rgba(255,255,255,0.25); 45 | color: white; 46 | } 47 | 48 | .nav-item ::deep a:hover { 49 | background-color: rgba(255,255,255,0.1); 50 | color: white; 51 | } 52 | 53 | @media (min-width: 768px) { 54 | .navbar-toggler { 55 | display: none; 56 | } 57 | 58 | .collapse { 59 | /* Never collapse the sidebar for wide screens */ 60 | display: block; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Client/ConfTool.Client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | Release;Debug 6 | 7 | 8 | 9 | TRACE;LIVESHARP_DISABLE 10 | 11 | 12 | 13 | true 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | all 28 | runtime; build; native; contentfiles; analyzers 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Client/Counter/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | 3 | @using Grpc.Core 4 | @using Grpc.Net.Client 5 | @using Microsoft.AspNetCore.Authorization 6 | @using System.Threading 7 | 8 | @using Count 9 | 10 | @inject GrpcChannel _channel 11 | 12 | @attribute [Authorize] 13 | 14 |

Streaming Counter

15 | 16 |

Current value: @_currentCount

17 | 18 | 19 | 20 | 21 | @code { 22 | private int _currentCount = 0; 23 | private CancellationTokenSource _cts; 24 | 25 | private async Task IncrementCount() 26 | { 27 | _cts = new CancellationTokenSource(); 28 | 29 | var client = new Count.Counter.CounterClient(_channel); 30 | var call = client.StartCounter(new CounterRequest() { Start = _currentCount }, cancellationToken: _cts.Token); 31 | 32 | try 33 | { 34 | await foreach (var message in call.ResponseStream.ReadAllAsync()) 35 | { 36 | _currentCount = message.Count; 37 | StateHasChanged(); 38 | } 39 | } 40 | catch (RpcException ex) when (ex.StatusCode == StatusCode.Cancelled) 41 | { 42 | // Ignore exception from cancellation 43 | } 44 | } 45 | 46 | private void StopCount() 47 | { 48 | _cts?.Cancel(); 49 | _cts = null; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Client/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | @using ConfTool.Client.Common 4 | 5 |

Blazor Demos

6 | 7 | 8 |

ConfTool PoC

9 |
10 | -------------------------------------------------------------------------------- /Client/Infrastructure/Configuration.cs: -------------------------------------------------------------------------------- 1 | namespace ConfTool.Client.Infrastructure 2 | { 3 | internal static class Configuration 4 | { 5 | public const string BackendUrlKey = "Client:BackendUrl"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Client/Infrastructure/pwa/commands.txt: -------------------------------------------------------------------------------- 1 | cd ../.. 2 | dotnet publish -c Debug 3 | cd Infrastructure/pwa 4 | workbox generateSW workbox-config.js 5 | -------------------------------------------------------------------------------- /Client/Infrastructure/pwa/workbox-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | maximumFileSizeToCacheInBytes: 10485760, 3 | globDirectory: "../../bin/Debug/net5.0/publish/wwwroot", 4 | globPatterns: [ 5 | '**/*.{html,json,js,css,png,ico,json,wasm,dll,pdb,eot,otf,woff,svg,ttf}' 6 | ], 7 | swDest: "../../bin/Debug/net5.0/publish/wwwroot/sw.js", 8 | navigateFallback: "/index.html", 9 | clientsClaim: true, 10 | runtimeCaching: [{ 11 | urlPattern: "https://localhost:5003/api/conferences/", 12 | handler: "NetworkFirst", 13 | options: { 14 | cacheName: "conferencesApi", 15 | expiration: { 16 | maxAgeSeconds: 1000, 17 | }, 18 | }, 19 | }], 20 | }; -------------------------------------------------------------------------------- /Client/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using System.Threading.Tasks; 4 | using ConfTool.Client.Infrastructure; 5 | using ConfTool.Client.Webcam; 6 | using ConfTool.ClientModules.Conferences; 7 | using ConfTool.ClientModules.Statistics; 8 | using Grpc.Core; 9 | using Grpc.Core.Interceptors; 10 | using Grpc.Net.Client; 11 | using Grpc.Net.Client.Web; 12 | using Microsoft.AspNetCore.Components.WebAssembly.Authentication; 13 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 14 | using Microsoft.Extensions.Configuration; 15 | using Microsoft.Extensions.DependencyInjection; 16 | 17 | namespace ConfTool.Client 18 | { 19 | public class Program 20 | { 21 | public static async Task Main(string[] args) 22 | { 23 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 24 | builder.RootComponents.Add("#app"); 25 | 26 | builder.Services.AddConferencesModule(builder.Configuration); 27 | builder.Services.AddStatisticsModule(builder.Configuration); 28 | 29 | builder.Services.AddWebcam(); 30 | 31 | builder.Services.AddScoped(services => 32 | { 33 | var channel = BuildGrpcChannel(services, builder); 34 | 35 | return channel; 36 | }); 37 | 38 | builder.Services.AddScoped(services => 39 | { 40 | var channel = BuildGrpcChannel(services, builder); 41 | var invoker = channel.Intercept(new ClientLoggerInterceptor()); 42 | 43 | return invoker; 44 | }); 45 | 46 | builder.Services.AddOidcAuthentication(options => 47 | { 48 | builder.Configuration.Bind("Oidc", options.ProviderOptions); 49 | }); 50 | 51 | await builder.Build().RunAsync(); 52 | } 53 | 54 | private static GrpcChannel BuildGrpcChannel(IServiceProvider services, WebAssemblyHostBuilder builder) 55 | { 56 | var baseAddressMessageHandler = services.GetRequiredService(); 57 | baseAddressMessageHandler.InnerHandler = new HttpClientHandler(); 58 | var grpcWebHandler = new GrpcWebHandler(GrpcWebMode.GrpcWeb, baseAddressMessageHandler); 59 | 60 | var channel = GrpcChannel.ForAddress(builder.Configuration[Configuration.BackendUrlKey], new GrpcChannelOptions { HttpHandler = grpcWebHandler }); 61 | 62 | return channel; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Client/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:30107", 7 | "sslPort": 44380 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | } 18 | }, 19 | "ConfTool": { 20 | "commandName": "Project", 21 | "dotnetRunMessages": true, 22 | "launchBrowser": true, 23 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 24 | "applicationUrl": "https://localhost:5003;http://localhost:5002", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Client/Time/Time.razor: -------------------------------------------------------------------------------- 1 | @page "/time" 2 | 3 | @using Grpc.Core 4 | @using Grpc.Net.Client 5 | @using Microsoft.AspNetCore.Authorization 6 | @using ProtoBuf.Grpc.Client; 7 | @using ProtoBuf.Grpc; 8 | @using System.Threading 9 | @using ConfTool.Shared.Contracts 10 | 11 | @inject GrpcChannel _channel 12 | 13 | @attribute [Authorize] 14 | 15 |

Server Time

16 | 17 |

Current time: @_currentTimeFromServer

18 | 19 | 20 | 21 | 22 | @code { 23 | private DateTime _currentTimeFromServer; 24 | private CancellationTokenSource _cts; 25 | 26 | private async Task StartTime() 27 | { 28 | var clock = _channel.CreateGrpcService(); 29 | _cts = new CancellationTokenSource(); 30 | var options = new CallOptions(cancellationToken: _cts.Token); 31 | 32 | Console.WriteLine($"Current local time: {DateTime.UtcNow}"); 33 | 34 | try 35 | { 36 | await foreach (var time in clock.SubscribeAsync(new CallContext(options))) 37 | { 38 | _currentTimeFromServer = time.Time; 39 | StateHasChanged(); 40 | Console.WriteLine($"Time from server: {time.Time}"); 41 | } 42 | } 43 | catch (RpcException) { } 44 | catch (OperationCanceledException) { } 45 | } 46 | 47 | private void StopTime() 48 | { 49 | _cts?.Cancel(); 50 | _cts = null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Client/Webcam/IWebcamService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace ConfTool.Client.Webcam 4 | { 5 | public interface IWebcamService 6 | { 7 | Task StartVideoAsync(WebcamOptions options); 8 | Task TakePictureAsync(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Client/Webcam/Webcam.razor: -------------------------------------------------------------------------------- 1 | @page "/webcam" 2 | 3 | @inject IWebcamService _webcam 4 | 5 |

Webcam

6 | 7 |
8 | 10 |
11 | 12 |
13 | 15 |
16 | 17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | @code { 26 | private readonly WebcamOptions _options = new WebcamOptions() 27 | { 28 | CanvasId = "canvas", 29 | VideoId = "video", 30 | PhotoId = "photo" 31 | }; 32 | 33 | protected override void OnInitialized() 34 | { 35 | _options.Width = 320; 36 | } 37 | 38 | private async Task StartVideoAsync() 39 | { 40 | await _webcam.StartVideoAsync(_options); 41 | } 42 | 43 | private async Task TakePictureAsync() 44 | { 45 | await _webcam.TakePictureAsync(); 46 | } 47 | } -------------------------------------------------------------------------------- /Client/Webcam/Webcam.razor.css: -------------------------------------------------------------------------------- 1 | #video { 2 | border: 1px solid black; 3 | box-shadow: 2px 2px 3px black; 4 | width: 320px; 5 | height: 240px; 6 | background-color: black; 7 | } 8 | 9 | #photo { 10 | border: 1px solid black; 11 | box-shadow: 2px 2px 3px black; 12 | width: 320px; 13 | height: 240px; 14 | } 15 | 16 | #canvas { 17 | display: none; 18 | } 19 | 20 | .camera { 21 | width: 340px; 22 | display: inline-block; 23 | } 24 | 25 | .output { 26 | width: 340px; 27 | display: inline-block; 28 | } 29 | -------------------------------------------------------------------------------- /Client/Webcam/WebcamOptions.cs: -------------------------------------------------------------------------------- 1 | namespace ConfTool.Client.Webcam 2 | { 3 | public class WebcamOptions 4 | { 5 | public int Width { get; set; } 6 | public string VideoId { get; set; } 7 | public string CanvasId { get; set; } 8 | public string PhotoId { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Client/Webcam/WebcamService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System; 3 | using System.Threading.Tasks; 4 | 5 | namespace ConfTool.Client.Webcam 6 | { 7 | public class WebcamService : IWebcamService, IAsyncDisposable 8 | { 9 | private readonly Lazy> _moduleTask; 10 | 11 | public WebcamService(IJSRuntime jsRuntime) 12 | { 13 | _moduleTask = new(() => jsRuntime.InvokeAsync( 14 | "import", "./jsinterop/webcam.js").AsTask()); 15 | } 16 | 17 | public async Task StartVideoAsync(WebcamOptions options) 18 | { 19 | var module = await _moduleTask.Value; 20 | await module.InvokeVoidAsync("startVideo", options); 21 | } 22 | 23 | public async Task TakePictureAsync() 24 | { 25 | var module = await _moduleTask.Value; 26 | await module.InvokeVoidAsync("takePicture"); 27 | } 28 | 29 | public async ValueTask DisposeAsync() 30 | { 31 | if (_moduleTask.IsValueCreated) 32 | { 33 | var module = await _moduleTask.Value; 34 | await module.DisposeAsync(); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Client/Webcam/WebcamServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace ConfTool.Client.Webcam 4 | { 5 | public static class WebcamServiceCollectionExtensions 6 | { 7 | public static IServiceCollection AddWebcam(this IServiceCollection services) 8 | { 9 | services.AddSingleton(); 10 | 11 | return services; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Client/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web -------------------------------------------------------------------------------- /Client/wwwroot/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Client": { 3 | "BackendUrl": "https://localhost:5003" 4 | }, 5 | "Conferences": { 6 | "BackendUrl": "https://localhost:5003" 7 | }, 8 | "Statistics": { 9 | "BackendUrl": "https://localhost:5003" 10 | }, 11 | "Oidc": { 12 | "Authority": "https://localhost:5006", 13 | "MetadataUrl": "https://localhost:5006/.well-known/openid-configuration", 14 | "ClientId": "interactive.public", 15 | "DefaultScopes": [ 16 | "email", 17 | "api" 18 | ], 19 | "ResponseType": "code" 20 | }, 21 | "ConnectionStrings": { 22 | "DefaultConnection": "DataSource=app.db" 23 | }, 24 | "Logging": { 25 | "LogLevel": { 26 | "Default": "Warning" 27 | } 28 | }, 29 | "AllowedHosts": "*" 30 | } 31 | -------------------------------------------------------------------------------- /Client/wwwroot/css/app.css: -------------------------------------------------------------------------------- 1 | @import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); 2 | 3 | html, body { 4 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 5 | } 6 | 7 | a, .btn-link { 8 | color: #0366d6; 9 | } 10 | 11 | .btn-primary { 12 | color: #fff; 13 | background-color: #1b6ec2; 14 | border-color: #1861ac; 15 | } 16 | 17 | .content { 18 | padding-top: 1.1rem; 19 | } 20 | 21 | .valid.modified:not([type=checkbox]) { 22 | outline: 1px solid #26b050; 23 | } 24 | 25 | .invalid { 26 | outline: 1px solid red; 27 | } 28 | 29 | .validation-message { 30 | color: red; 31 | } 32 | 33 | #blazor-error-ui { 34 | background: lightyellow; 35 | bottom: 0; 36 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 37 | display: none; 38 | left: 0; 39 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 40 | position: fixed; 41 | width: 100%; 42 | z-index: 1000; 43 | } 44 | 45 | #blazor-error-ui .dismiss { 46 | cursor: pointer; 47 | position: absolute; 48 | right: 0.75rem; 49 | top: 0.5rem; 50 | } 51 | 52 | .spinner { 53 | width: 40px; 54 | height: 40px; 55 | background-color: #333; 56 | margin: 100px auto; 57 | -webkit-animation: sk-rotateplane 1.2s infinite ease-in-out; 58 | animation: sk-rotateplane 1.2s infinite ease-in-out; 59 | } 60 | 61 | @-webkit-keyframes sk-rotateplane { 62 | 0% { 63 | -webkit-transform: perspective(120px) 64 | } 65 | 66 | 50% { 67 | -webkit-transform: perspective(120px) rotateY(180deg) 68 | } 69 | 70 | 100% { 71 | -webkit-transform: perspective(120px) rotateY(180deg) rotateX(180deg) 72 | } 73 | } 74 | 75 | @keyframes sk-rotateplane { 76 | 0% { 77 | transform: perspective(120px) rotateX(0deg) rotateY(0deg); 78 | -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg) 79 | } 80 | 81 | 50% { 82 | transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg); 83 | -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) 84 | } 85 | 86 | 100% { 87 | transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); 88 | -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Client/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture/blazor-webassembly-demo/6ae30b2b2d877d1169ec02bc3d5ca05c74b490d6/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture/blazor-webassembly-demo/6ae30b2b2d877d1169ec02bc3d5ca05c74b490d6/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture/blazor-webassembly-demo/6ae30b2b2d877d1169ec02bc3d5ca05c74b490d6/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture/blazor-webassembly-demo/6ae30b2b2d877d1169ec02bc3d5ca05c74b490d6/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /Client/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture/blazor-webassembly-demo/6ae30b2b2d877d1169ec02bc3d5ca05c74b490d6/Client/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Client/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture/blazor-webassembly-demo/6ae30b2b2d877d1169ec02bc3d5ca05c74b490d6/Client/wwwroot/icon-192.png -------------------------------------------------------------------------------- /Client/wwwroot/icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture/blazor-webassembly-demo/6ae30b2b2d877d1169ec02bc3d5ca05c74b490d6/Client/wwwroot/icon-512.png -------------------------------------------------------------------------------- /Client/wwwroot/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | ConfTool 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
...
29 |
30 | 31 |
32 | An unhandled error has occurred. 33 | Reload 34 | X 35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Client/wwwroot/jsinterop/webcam.js: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/mdn/samples-server/blob/master/s/webrtc-capturestill/capture.js 2 | 3 | var interopJS = interopJS || {} 4 | 5 | let video = null; 6 | let canvas = null; 7 | let context = null; 8 | let photo = null; 9 | let streaming = false; 10 | 11 | let width = 100; // We will scale the photo width to this 12 | let height = 0; // This will be computed based on the input stream 13 | 14 | export function startVideo(options) { 15 | video = document.getElementById(options.videoId); 16 | canvas = document.getElementById(options.canvasId); 17 | photo = document.getElementById(options.photoId); 18 | context = canvas.getContext('2d'); 19 | width = options.width; 20 | 21 | navigator.mediaDevices.getUserMedia({ video: true, audio: false }) 22 | .then(function (stream) { 23 | video.srcObject = stream; 24 | video.play(); 25 | }) 26 | .catch(function (err) { 27 | console.log("An error occurred: " + err); 28 | }); 29 | 30 | video.addEventListener('canplay', function () { 31 | if (!streaming) { 32 | height = video.videoHeight / (video.videoWidth / width); 33 | 34 | if (isNaN(height)) { 35 | height = width / (4 / 3); 36 | } 37 | 38 | video.setAttribute('width', width); 39 | video.setAttribute('height', height); 40 | canvas.setAttribute('width', width); 41 | canvas.setAttribute('height', height); 42 | 43 | streaming = true; 44 | } 45 | }, false); 46 | 47 | clearPicture(); 48 | } 49 | 50 | export function clearPicture() { 51 | var context = canvas.getContext('2d'); 52 | context.fillStyle = "#FFF"; 53 | context.fillRect(0, 0, canvas.width, canvas.height); 54 | 55 | var data = canvas.toDataURL('image/png'); 56 | photo.setAttribute('src', data); 57 | } 58 | 59 | export function takePicture() { 60 | var context = canvas.getContext('2d'); 61 | 62 | if (width && height) { 63 | canvas.width = width; 64 | canvas.height = height; 65 | 66 | context.drawImage(video, 0, 0, width, height); 67 | 68 | var data = canvas.toDataURL('image/png'); 69 | photo.setAttribute('src', data); 70 | } else { 71 | clearPicture(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Client/wwwroot/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "ConfTool", 3 | "name": "Conferences Tool", 4 | "description": "Franconia dolor ipsum sit amet, Ziebeleskäs fei sacht, bassd scho Halbs Hemmerd Debb Schbodzn Gwaaf. Oggs vo der Fleischbrüggn Boodwanna do schau her groom Abodeng? Grawallschachdl Fleischkäichla Schloudfeecher nächerds! Bridschn, bugglhadd Schdumhogger Barragn Fraa glei fängsd der anne Misdn. Fiesl mal ned dran rum adee Schdrom bassd scho.", 5 | "icons": [ 6 | { 7 | "src": "icon-512.png", 8 | "type": "image/png", 9 | "sizes": "512x512" 10 | } 11 | ], 12 | "start_url": "/", 13 | "background_color": "#1294e0", 14 | "display": "standalone", 15 | "scope": "/", 16 | "theme_color": "darkslategrey" 17 | } 18 | -------------------------------------------------------------------------------- /ClientModules/ConferencesModule/Components/ConferenceComponentDisplayModes.cs: -------------------------------------------------------------------------------- 1 | namespace ConfTool.ClientModules.Conferences.Components 2 | { 3 | public static class ConferenceComponentDisplayModes 4 | { 5 | public const string Show = "show"; 6 | public const string Edit = "edit"; 7 | public const string New = "new"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ClientModules/ConferencesModule/Components/ConferenceHeader.razor: -------------------------------------------------------------------------------- 1 |  2 | @switch (Mode) 3 | { 4 | case ConferenceComponentDisplayModes.Show: 5 |

Conference Details

6 | break; 7 | 8 | case ConferenceComponentDisplayModes.New: 9 |

New Conference

10 | break; 11 | 12 | case ConferenceComponentDisplayModes.Edit: 13 |

Edit Conference

14 | break; 15 | } 16 | 17 | @code { 18 | [Parameter] 19 | public string Mode { get; set; } 20 | } 21 | -------------------------------------------------------------------------------- /ClientModules/ConferencesModule/ConfTool.ClientModules.Conferences.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /ClientModules/ConferencesModule/ConferencesModuleServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Blazored.Toast; 3 | using ConfTool.ClientModules.Conferences.Services; 4 | using Microsoft.AspNetCore.Components.WebAssembly.Authentication; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | 8 | namespace ConfTool.ClientModules.Conferences 9 | { 10 | public static class ConferencesModuleServiceCollectionExtensions 11 | { 12 | public static IServiceCollection AddConferencesModule(this IServiceCollection services, IConfiguration config) 13 | { 14 | services.AddScoped(); 15 | services.AddScoped(); 16 | 17 | services.AddHttpClient("Conferences.ServerAPI", client => 18 | client.BaseAddress = new Uri(config[Configuration.BackendUrlKey])) 19 | .AddHttpMessageHandler(); 20 | 21 | services.AddDialog(); 22 | 23 | services.AddBlazoredToast(); 24 | 25 | return services; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ClientModules/ConferencesModule/Configuration.cs: -------------------------------------------------------------------------------- 1 | namespace ConfTool.ClientModules.Conferences 2 | { 3 | internal static class Configuration 4 | { 5 | public const string BackendUrlKey = "Conferences:BackendUrl"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ClientModules/ConferencesModule/Pages/Conference.razor: -------------------------------------------------------------------------------- 1 | @page "/conferences/{mode}" 2 | @page "/conferences/{mode}/{id:guid}" 3 | 4 | @using Microsoft.AspNetCore.Components.Forms 5 | 6 | @using Blazored.FluentValidation 7 | @using Blazored.Typeahead 8 | 9 | @using ConfTool.ClientModules.Conferences.Components 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 |
21 |
22 | 23 | 24 |
25 |
26 | 27 | @if (Mode == ConferenceComponentDisplayModes.Show) 28 | { 29 | 30 | } 31 | else 32 | { 33 | 41 | 44 | 45 | @country 46 | 47 | 48 | @country 49 | 50 | 51 | } 52 |
53 |
54 | 55 | 56 |
57 |
58 | 59 | 60 |
61 | 62 | 63 | 64 | @if (Mode != ConferenceComponentDisplayModes.Show) 65 | { 66 | 67 | } 68 |
69 | -------------------------------------------------------------------------------- /ClientModules/ConferencesModule/Pages/Conferences.razor: -------------------------------------------------------------------------------- 1 | @page "/conferences" 2 | 3 | @using Microsoft.AspNetCore.Components.Web.Virtualization 4 | @using Microsoft.AspNetCore.Authorization 5 | 6 | @using Blazored.Toast.Services 7 | 8 | @using ConfTool.ClientModules.Conferences.Services 9 | @using ConfTool.Shared.DTO 10 | 11 | @attribute [Authorize] 12 | 13 | @implements IDisposable 14 | 15 | @inject NavigationManager _navigation 16 | @inject IConferencesServiceClient _conferencesService 17 | @inject IToastService _toastService 18 | 19 |

Conferences

20 | 21 | 22 |
23 |
24 | 25 | @if (_conferences == null) 26 | { 27 |

Loading conferences...

28 | } 29 | else 30 | { 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |
Title
@conf.Title
45 | } 46 | @code { 47 | public List _conferences; 48 | 49 | protected override async Task OnInitializedAsync() 50 | { 51 | _conferencesService.ConferenceListChanged += HandleConferenceUpdates; 52 | 53 | await _conferencesService.InitAsync(); 54 | 55 | _conferences = await ListConferences(); 56 | } 57 | 58 | private async void HandleConferenceUpdates(object sender, EventArgs args) 59 | { 60 | _toastService.ShowInfo("Conference list changed."); 61 | Console.WriteLine("###SignalR - NEW conference added!"); 62 | 63 | _conferences = await ListConferences(); 64 | 65 | StateHasChanged(); 66 | } 67 | 68 | private async Task> ListConferences() 69 | { 70 | return await _conferencesService.ListConferencesAsync(); 71 | } 72 | 73 | private void LoadDetails(Guid id) 74 | { 75 | _navigation.NavigateTo("conferences/show/" + id); 76 | } 77 | 78 | private void AddConference(MouseEventArgs e) 79 | { 80 | _navigation.NavigateTo("conferences/new"); 81 | } 82 | 83 | public void Dispose() 84 | { 85 | GC.SuppressFinalize(this); 86 | 87 | _conferencesService.ConferenceListChanged -= HandleConferenceUpdates; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /ClientModules/ConferencesModule/Services/ConferencesServiceClientGrpc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using ConfTool.Shared.Contracts; 6 | using ConfTool.Shared.DTO; 7 | using Grpc.Core; 8 | using Grpc.Net.Client; 9 | using Microsoft.AspNetCore.SignalR.Client; 10 | using Microsoft.Extensions.Configuration; 11 | using Microsoft.Extensions.DependencyInjection; 12 | using ProtoBuf.Grpc.Client; 13 | 14 | namespace ConfTool.ClientModules.Conferences.Services 15 | { 16 | public class ConferencesServiceClientGrpc : IConferencesServiceClient 17 | { 18 | private readonly IConferencesService _client; 19 | //private Conference.Conferences.ConferencesClient _client; 20 | 21 | private readonly string _baseUrl; 22 | private HubConnection _hubConnection; 23 | 24 | public event EventHandler ConferenceListChanged; 25 | 26 | public ConferencesServiceClientGrpc(IConfiguration config, GrpcChannel channel, CallInvoker invoker) 27 | { 28 | _baseUrl = config[Configuration.BackendUrlKey]; 29 | 30 | //_client = new Conference.Conferences.ConferencesClient(channel); 31 | _client = channel.CreateGrpcService(); 32 | //_client = invoker.CreateGrpcService(); 33 | } 34 | 35 | public async Task InitAsync() 36 | { 37 | _hubConnection = new HubConnectionBuilder() 38 | .WithUrl(new Uri(new Uri(_baseUrl), "conferencesHub")) 39 | .WithAutomaticReconnect() 40 | .AddMessagePackProtocol() 41 | .Build(); 42 | 43 | _hubConnection.On("NewConferenceAdded", () => 44 | { 45 | ConferenceListChanged?.Invoke(this, null); 46 | }); 47 | 48 | await _hubConnection.StartAsync(); 49 | } 50 | 51 | public async Task> ListConferencesAsync() 52 | { 53 | var result = await _client.ListConferencesAsync(); 54 | 55 | return result.ToList(); 56 | } 57 | 58 | public async Task GetConferenceDetailsAsync(Guid id) 59 | { 60 | var result = await _client.GetConferenceDetailsAsync(new ConferenceDetailsRequest { ID = id }); 61 | 62 | return result; 63 | } 64 | 65 | public async Task AddConferenceAsync(ConferenceDetails conference) 66 | { 67 | var result = await _client.AddNewConferenceAsync(conference); 68 | 69 | return result; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /ClientModules/ConferencesModule/Services/ConferencesServiceClientHttp.cs: -------------------------------------------------------------------------------- 1 | using ConfTool.Shared.DTO; 2 | using Microsoft.AspNetCore.SignalR.Client; 3 | using Microsoft.Extensions.Configuration; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Net.Http; 7 | using System.Net.Http.Json; 8 | using System.Threading.Tasks; 9 | 10 | namespace ConfTool.ClientModules.Conferences.Services 11 | { 12 | public class ConferencesServiceClientHttp : IConferencesServiceClient 13 | { 14 | private readonly HttpClient _secureHttpClient; 15 | private readonly string _baseUrl; 16 | private readonly string _conferencesUrl; 17 | private HubConnection _hubConnection; 18 | 19 | public event EventHandler ConferenceListChanged; 20 | 21 | public ConferencesServiceClientHttp(IConfiguration config, IHttpClientFactory httpClientFactory) 22 | { 23 | _secureHttpClient = httpClientFactory.CreateClient("Conferences.ServerAPI"); 24 | _baseUrl = config[Configuration.BackendUrlKey]; 25 | _conferencesUrl = new Uri(new Uri(_baseUrl), "api/conferences/").ToString(); 26 | } 27 | 28 | public async Task InitAsync() 29 | { 30 | _hubConnection = new HubConnectionBuilder() 31 | .WithUrl(new Uri(new Uri(_baseUrl), "conferencesHub")) 32 | .Build(); 33 | 34 | _hubConnection.On("NewConferenceAdded", () => 35 | { 36 | ConferenceListChanged?.Invoke(this, null); 37 | }); 38 | 39 | await _hubConnection.StartAsync(); 40 | } 41 | 42 | public async Task> ListConferencesAsync() 43 | { 44 | var result = await _secureHttpClient.GetFromJsonAsync>(_conferencesUrl); 45 | 46 | return result; 47 | } 48 | 49 | public async Task GetConferenceDetailsAsync(Guid id) 50 | { 51 | var result = await _secureHttpClient.GetFromJsonAsync(_conferencesUrl + id); 52 | 53 | return result; 54 | } 55 | 56 | public async Task AddConferenceAsync(ConferenceDetails conference) 57 | { 58 | var result = await (await _secureHttpClient.PostAsJsonAsync(_conferencesUrl, conference)) 59 | .Content.ReadFromJsonAsync(); 60 | 61 | return result; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /ClientModules/ConferencesModule/Services/CountriesServiceClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Net.Http.Json; 5 | using System.Threading.Tasks; 6 | using Microsoft.Extensions.Configuration; 7 | 8 | namespace ConfTool.ClientModules.Conferences.Services 9 | { 10 | public class CountriesServiceClient 11 | { 12 | private readonly HttpClient _anonHttpClient; 13 | private readonly string _countriesUrl; 14 | 15 | public CountriesServiceClient(IConfiguration config, IHttpClientFactory httpClientFactory) 16 | { 17 | _anonHttpClient = httpClientFactory.CreateClient("ConfTool.ServerAPI.Anon"); 18 | var baseUrl = config[Configuration.BackendUrlKey]; 19 | _countriesUrl = new Uri(new Uri(baseUrl), "api/countries/").ToString(); 20 | } 21 | 22 | public async Task> ListCountries() 23 | { 24 | var result = await _anonHttpClient.GetFromJsonAsync>(_countriesUrl); 25 | 26 | return result; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ClientModules/ConferencesModule/Services/Dialog/DialogService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System; 3 | using System.Threading.Tasks; 4 | 5 | namespace ConfTool.ClientModules.Conferences.Services 6 | { 7 | public class DialogService : IDialogService, IAsyncDisposable 8 | { 9 | private readonly Lazy> _moduleTask; 10 | 11 | public DialogService(IJSRuntime jsRuntime) 12 | { 13 | _moduleTask = new(() => jsRuntime.InvokeAsync( 14 | "import", "./_content/ConfTool.ClientModules.Conferences/jsinterop/dialog.js").AsTask()); 15 | } 16 | 17 | public async Task ConfirmAsync(string message) 18 | { 19 | var module = await _moduleTask.Value; 20 | return await module.InvokeAsync("confirm", message); 21 | } 22 | 23 | public async Task AlertAsync(string message) 24 | { 25 | var module = await _moduleTask.Value; 26 | await module.InvokeVoidAsync("alert", message); 27 | } 28 | 29 | public async ValueTask DisposeAsync() 30 | { 31 | if (_moduleTask.IsValueCreated) 32 | { 33 | var module = await _moduleTask.Value; 34 | await module.DisposeAsync(); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ClientModules/ConferencesModule/Services/Dialog/DialogServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace ConfTool.ClientModules.Conferences.Services 4 | { 5 | public static class AlertServiceCollectionExtensions 6 | { 7 | public static IServiceCollection AddDialog(this IServiceCollection services) 8 | { 9 | services.AddSingleton(); 10 | 11 | return services; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ClientModules/ConferencesModule/Services/Dialog/IDialogService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace ConfTool.ClientModules.Conferences.Services 4 | { 5 | public interface IDialogService 6 | { 7 | Task ConfirmAsync(string message); 8 | Task AlertAsync(string message); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ClientModules/ConferencesModule/Services/IConferencesServiceClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using ConfTool.Shared.DTO; 5 | 6 | namespace ConfTool.ClientModules.Conferences.Services 7 | { 8 | public interface IConferencesServiceClient 9 | { 10 | Task InitAsync(); 11 | Task> ListConferencesAsync(); 12 | Task AddConferenceAsync(ConferenceDetails conference); 13 | Task GetConferenceDetailsAsync(Guid id); 14 | 15 | event EventHandler ConferenceListChanged; 16 | } 17 | } -------------------------------------------------------------------------------- /ClientModules/ConferencesModule/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web -------------------------------------------------------------------------------- /ClientModules/ConferencesModule/wwwroot/jsinterop/dialog.js: -------------------------------------------------------------------------------- 1 | export function confirm(message) { 2 | return window.confirm(message); 3 | } 4 | 5 | export function alert(message) { 6 | window.alert(message); 7 | 8 | return true; 9 | } 10 | -------------------------------------------------------------------------------- /ClientModules/StatisticsModule/ConfTool.ClientModules.Statistics.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ClientModules/StatisticsModule/Configuration.cs: -------------------------------------------------------------------------------- 1 | namespace ConfTool.ClientModules.Statistics 2 | { 3 | internal static class Configuration 4 | { 5 | public const string BackendUrlKey = "Statistics:BackendUrl"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ClientModules/StatisticsModule/Pages/Statistics.razor: -------------------------------------------------------------------------------- 1 | @page "/statistics" 2 | 3 | @using Microsoft.JSInterop 4 | @using System.Text.Json 5 | @using ConfTool.ClientModules.Statistics.Services 6 | 7 | @implements IDisposable 8 | 9 | @inject IJSRuntime _js 10 | @inject StatisticsServiceClient _statisticsService 11 | 12 |

Statistics

13 | 14 | 23 | 24 |
25 |
26 | 27 | 28 |
29 |
30 | 31 | 32 |
33 |
34 | 35 | @code { 36 | private string _data; 37 | private string _selectedCountry; 38 | 39 | private DotNetObjectReference _objectReference; 40 | 41 | protected override async Task OnInitializedAsync() 42 | { 43 | var result = await _statisticsService.GetStatisticsAsync(); 44 | _data = JsonSerializer.Serialize(result); 45 | } 46 | 47 | protected override async void OnAfterRender(bool firstRender) 48 | { 49 | if (firstRender) 50 | { 51 | _objectReference = DotNetObjectReference.Create(this); 52 | 53 | await _js.InvokeVoidAsync("interop.charts.register", _objectReference); 54 | await _js.InvokeVoidAsync("interop.charts.init"); 55 | } 56 | } 57 | 58 | [JSInvokable] 59 | public void SetSelectedCountry(JsonElement selectedData) 60 | { 61 | _selectedCountry = selectedData.GetProperty("name").ToString(); 62 | StateHasChanged(); 63 | } 64 | 65 | public void Dispose() 66 | { 67 | GC.SuppressFinalize(this); 68 | 69 | if (_objectReference != null) 70 | { 71 | _objectReference.Dispose(); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /ClientModules/StatisticsModule/Services/StatisticsServiceClient.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using System; 3 | using System.Net.Http; 4 | using System.Net.Http.Json; 5 | using System.Threading.Tasks; 6 | 7 | namespace ConfTool.ClientModules.Statistics.Services 8 | { 9 | public class StatisticsServiceClient 10 | { 11 | private readonly HttpClient _anonHttpClient; 12 | private readonly string _statisticsUrl; 13 | 14 | public StatisticsServiceClient(IConfiguration config, IHttpClientFactory httpClientFactory) 15 | { 16 | _anonHttpClient = httpClientFactory.CreateClient("Statistics.ServerAPI.Anon"); 17 | var baseUrl = config[Configuration.BackendUrlKey]; 18 | _statisticsUrl = new Uri(new Uri(baseUrl), "api/statistics/").ToString(); 19 | } 20 | 21 | public async Task GetStatisticsAsync() 22 | { 23 | var result = await _anonHttpClient.GetFromJsonAsync(_statisticsUrl); 24 | 25 | return result; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ClientModules/StatisticsModule/StatisticsModuleServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ConfTool.ClientModules.Statistics.Services; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace ConfTool.ClientModules.Statistics 7 | { 8 | public static class StatisticsModuleServiceCollectionExtensions 9 | { 10 | public static IServiceCollection AddStatisticsModule(this IServiceCollection services, IConfiguration config) 11 | { 12 | services.AddScoped(); 13 | 14 | services.AddHttpClient("Statistics.ServerAPI.Anon", client => 15 | client.BaseAddress = new Uri(config[Configuration.BackendUrlKey])); 16 | 17 | return services; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ClientModules/StatisticsModule/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web -------------------------------------------------------------------------------- /ClientModules/StatisticsModule/wwwroot/jsinterop/wcpiechart.js: -------------------------------------------------------------------------------- 1 | var interopJS = interopJS || {} 2 | 3 | interopJS.charts = { 4 | dotNet: null, 5 | 6 | init: _ => { 7 | var chart = document.querySelector('wc-pie-chart'); 8 | 9 | chart.addEventListener("selected", function (eventData) { 10 | console.log('### wc-pie-chart event fired!'); 11 | console.log(eventData); 12 | 13 | dotNet.invokeMethodAsync("SetSelectedCountry", eventData.detail) 14 | .then(data => { 15 | console.log("### wc-pie-chart was sent event details to .NET."); 16 | }); 17 | }); 18 | }, 19 | 20 | register: dotNetReference => { 21 | dotNet = dotNetReference; 22 | } 23 | } 24 | 25 | window.interop = interopJS; 26 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Api/TestController.cs: -------------------------------------------------------------------------------- 1 | using IdentityServer4; 2 | using Microsoft.AspNetCore.Authorization; 3 | using Microsoft.AspNetCore.Mvc; 4 | using System.Linq; 5 | 6 | namespace IdentityServer4Demo.Api 7 | { 8 | [Route("/api/test")] 9 | [Authorize(AuthenticationSchemes = IdentityServerConstants.LocalApi.AuthenticationScheme)] 10 | public class TestController : ControllerBase 11 | { 12 | public IActionResult Get() 13 | { 14 | var claims = User.Claims.Select(c => new { c.Type, c.Value }); 15 | return new JsonResult(claims); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /IdentityServer4Demo/DemoCorsPolicy.cs: -------------------------------------------------------------------------------- 1 | using IdentityServer4.Services; 2 | using System.Threading.Tasks; 3 | 4 | namespace IdentityServer4Demo 5 | { 6 | // allows arbitrary CORS origins - only for demo purposes. NEVER USE IN PRODUCTION 7 | public class DemoCorsPolicy : ICorsPolicyService 8 | { 9 | public Task IsOriginAllowedAsync(string origin) 10 | { 11 | return Task.FromResult(true); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /IdentityServer4Demo/DemoRedirectValidator.cs: -------------------------------------------------------------------------------- 1 | using IdentityServer4.Validation; 2 | using System.Threading.Tasks; 3 | using IdentityServer4.Models; 4 | 5 | namespace IdentityServer4Demo 6 | { 7 | // allows arbitrary redirect URIs - only for demo purposes. NEVER USE IN PRODUCTION 8 | public class DemoRedirectValidator : IRedirectUriValidator 9 | { 10 | public Task IsPostLogoutRedirectUriValidAsync(string requestedUri, Client client) 11 | { 12 | return Task.FromResult(true); 13 | } 14 | 15 | public Task IsRedirectUriValidAsync(string requestedUri, Client client) 16 | { 17 | return Task.FromResult(true); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /IdentityServer4Demo/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build 8 | WORKDIR /src 9 | COPY ["nuget.config", "."] 10 | COPY ["IdentityServer4Demo/IdentityServer4Demo.csproj", "IdentityServer4Demo/"] 11 | RUN dotnet restore "IdentityServer4Demo/IdentityServer4Demo.csproj" 12 | COPY . . 13 | WORKDIR "/src/IdentityServer4Demo" 14 | RUN dotnet build "IdentityServer4Demo.csproj" -c Release -o /app/build 15 | 16 | FROM build AS publish 17 | RUN dotnet publish "IdentityServer4Demo.csproj" -c Release -o /app/publish 18 | 19 | FROM base AS final 20 | WORKDIR /app 21 | COPY --from=publish /app/publish . 22 | ENTRYPOINT ["dotnet", "IdentityServer4Demo.dll"] -------------------------------------------------------------------------------- /IdentityServer4Demo/IdentityServer4Demo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | Linux 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /IdentityServer4Demo/IdentityServer4Demo.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.808.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IdentityServer4Demo", "IdentityServer4Demo.csproj", "{9075A92E-4915-45B3-BA8F-E869BFD51A30}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {9075A92E-4915-45B3-BA8F-E869BFD51A30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {9075A92E-4915-45B3-BA8F-E869BFD51A30}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {9075A92E-4915-45B3-BA8F-E869BFD51A30}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {9075A92E-4915-45B3-BA8F-E869BFD51A30}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {206470C4-088D-45F1-B0C7-292842B69D4D} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Azure.KeyVault; 3 | using Microsoft.Azure.Services.AppAuthentication; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.Configuration.AzureKeyVault; 6 | using Microsoft.Extensions.Hosting; 7 | using Serilog; 8 | using Serilog.Events; 9 | using System; 10 | 11 | namespace IdentityServer4Demo 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | Console.Title = "IdentityServer"; 18 | 19 | BuildWebHostBuilder(args).Build().Run(); 20 | } 21 | 22 | public static IHostBuilder BuildWebHostBuilder(string[] args) 23 | { 24 | return Host.CreateDefaultBuilder(args) 25 | .UseSerilog((ctx, config) => 26 | { 27 | config.MinimumLevel.Debug() 28 | .MinimumLevel.Debug() 29 | .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) 30 | .MinimumLevel.Override("System", LogEventLevel.Warning) 31 | .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information) 32 | .Enrich.FromLogContext(); 33 | 34 | if (ctx.HostingEnvironment.IsDevelopment()) 35 | { 36 | config.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}"); 37 | } 38 | else if (ctx.HostingEnvironment.IsProduction()) 39 | { 40 | config.WriteTo.File(@"D:\home\LogFiles\Application\identityserver.txt", 41 | fileSizeLimitBytes: 1_000_000, 42 | rollOnFileSizeLimit: true, 43 | shared: true, 44 | flushToDiskInterval: TimeSpan.FromSeconds(1)); 45 | } 46 | }) 47 | /*.ConfigureAppConfiguration((ctx, builder) => 48 | { 49 | var config = builder.Build(); 50 | var tokenProvider = new AzureServiceTokenProvider(); 51 | var kvClient = new KeyVaultClient((authority, resource, scope) => tokenProvider.KeyVaultTokenCallback(authority, resource, scope)); 52 | 53 | builder.AddAzureKeyVault(config["KeyVault:BaseUrl"], kvClient, new DefaultKeyVaultSecretManager()); 54 | })*/ 55 | .ConfigureWebHostDefaults(webBuilder => 56 | { 57 | webBuilder.UseStartup(); 58 | }); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:24997/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "IdentityServer4Demo": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "https://localhost:5006;http://localhost:5005" 25 | }, 26 | "Docker": { 27 | "commandName": "Docker", 28 | "launchBrowser": true, 29 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 30 | "publishAllPorts": true 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Account/AccountOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using System; 6 | 7 | namespace IdentityServerHost.Quickstart.UI 8 | { 9 | public class AccountOptions 10 | { 11 | public static bool AllowLocalLogin = true; 12 | public static bool AllowRememberLogin = true; 13 | public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30); 14 | 15 | public static bool ShowLogoutPrompt = true; 16 | public static bool AutomaticRedirectAfterSignOut = false; 17 | 18 | public static string InvalidCredentialsErrorMessage = "Invalid username or password"; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Account/ExternalProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | namespace IdentityServerHost.Quickstart.UI 6 | { 7 | public class ExternalProvider 8 | { 9 | public string DisplayName { get; set; } 10 | public string AuthenticationScheme { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Account/LoggedOutViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | namespace IdentityServerHost.Quickstart.UI 6 | { 7 | public class LoggedOutViewModel 8 | { 9 | public string PostLogoutRedirectUri { get; set; } 10 | public string ClientName { get; set; } 11 | public string SignOutIframeUrl { get; set; } 12 | 13 | public bool AutomaticRedirectAfterSignOut { get; set; } 14 | 15 | public string LogoutId { get; set; } 16 | public bool TriggerExternalSignout => ExternalAuthenticationScheme != null; 17 | public string ExternalAuthenticationScheme { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Account/LoginInputModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using System.ComponentModel.DataAnnotations; 6 | 7 | namespace IdentityServerHost.Quickstart.UI 8 | { 9 | public class LoginInputModel 10 | { 11 | [Required] 12 | public string Username { get; set; } 13 | [Required] 14 | public string Password { get; set; } 15 | public bool RememberLogin { get; set; } 16 | public string ReturnUrl { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Account/LoginViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | 9 | namespace IdentityServerHost.Quickstart.UI 10 | { 11 | public class LoginViewModel : LoginInputModel 12 | { 13 | public bool AllowRememberLogin { get; set; } = true; 14 | public bool EnableLocalLogin { get; set; } = true; 15 | 16 | public IEnumerable ExternalProviders { get; set; } = Enumerable.Empty(); 17 | public IEnumerable VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName)); 18 | 19 | public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1; 20 | public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null; 21 | } 22 | } -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Account/LogoutInputModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | namespace IdentityServerHost.Quickstart.UI 6 | { 7 | public class LogoutInputModel 8 | { 9 | public string LogoutId { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Account/LogoutViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | namespace IdentityServerHost.Quickstart.UI 6 | { 7 | public class LogoutViewModel : LogoutInputModel 8 | { 9 | public bool ShowLogoutPrompt { get; set; } = true; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Account/RedirectViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | 6 | namespace IdentityServerHost.Quickstart.UI 7 | { 8 | public class RedirectViewModel 9 | { 10 | public string RedirectUrl { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Consent/ConsentInputModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using System.Collections.Generic; 6 | 7 | namespace IdentityServerHost.Quickstart.UI 8 | { 9 | public class ConsentInputModel 10 | { 11 | public string Button { get; set; } 12 | public IEnumerable ScopesConsented { get; set; } 13 | public bool RememberConsent { get; set; } 14 | public string ReturnUrl { get; set; } 15 | public string Description { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Consent/ConsentOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | namespace IdentityServerHost.Quickstart.UI 6 | { 7 | public class ConsentOptions 8 | { 9 | public static bool EnableOfflineAccess = true; 10 | public static string OfflineAccessDisplayName = "Offline Access"; 11 | public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline"; 12 | 13 | public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission"; 14 | public static readonly string InvalidSelectionErrorMessage = "Invalid selection"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Consent/ConsentViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using System.Collections.Generic; 6 | 7 | namespace IdentityServerHost.Quickstart.UI 8 | { 9 | public class ConsentViewModel : ConsentInputModel 10 | { 11 | public string ClientName { get; set; } 12 | public string ClientUrl { get; set; } 13 | public string ClientLogoUrl { get; set; } 14 | public bool AllowRememberConsent { get; set; } 15 | 16 | public IEnumerable IdentityScopes { get; set; } 17 | public IEnumerable ApiScopes { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Consent/ProcessConsentResult.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using IdentityServer4.Models; 6 | 7 | namespace IdentityServerHost.Quickstart.UI 8 | { 9 | public class ProcessConsentResult 10 | { 11 | public bool IsRedirect => RedirectUri != null; 12 | public string RedirectUri { get; set; } 13 | public Client Client { get; set; } 14 | 15 | public bool ShowView => ViewModel != null; 16 | public ConsentViewModel ViewModel { get; set; } 17 | 18 | public bool HasValidationError => ValidationError != null; 19 | public string ValidationError { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Consent/ScopeViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | namespace IdentityServerHost.Quickstart.UI 6 | { 7 | public class ScopeViewModel 8 | { 9 | public string Value { get; set; } 10 | public string DisplayName { get; set; } 11 | public string Description { get; set; } 12 | public bool Emphasize { get; set; } 13 | public bool Required { get; set; } 14 | public bool Checked { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Device/DeviceAuthorizationInputModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | namespace IdentityServerHost.Quickstart.UI 6 | { 7 | public class DeviceAuthorizationInputModel : ConsentInputModel 8 | { 9 | public string UserCode { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Device/DeviceAuthorizationViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | namespace IdentityServerHost.Quickstart.UI 6 | { 7 | public class DeviceAuthorizationViewModel : ConsentViewModel 8 | { 9 | public string UserCode { get; set; } 10 | public bool ConfirmUserCode { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Diagnostics/DiagnosticsController.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Authentication; 7 | using Microsoft.AspNetCore.Authorization; 8 | using Microsoft.AspNetCore.Mvc; 9 | 10 | namespace IdentityServerHost.Quickstart.UI 11 | { 12 | [SecurityHeaders] 13 | [Authorize] 14 | public class DiagnosticsController : Controller 15 | { 16 | public async Task Index() 17 | { 18 | var model = new DiagnosticsViewModel(await HttpContext.AuthenticateAsync()); 19 | return View(model); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Diagnostics/DiagnosticsViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using IdentityModel; 6 | using Microsoft.AspNetCore.Authentication; 7 | using Newtonsoft.Json; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | 11 | namespace IdentityServerHost.Quickstart.UI 12 | { 13 | public class DiagnosticsViewModel 14 | { 15 | public DiagnosticsViewModel(AuthenticateResult result) 16 | { 17 | AuthenticateResult = result; 18 | 19 | if (result.Properties.Items.ContainsKey("client_list")) 20 | { 21 | var encoded = result.Properties.Items["client_list"]; 22 | var bytes = Base64Url.Decode(encoded); 23 | var value = Encoding.UTF8.GetString(bytes); 24 | 25 | Clients = JsonConvert.DeserializeObject(value); 26 | } 27 | } 28 | 29 | public AuthenticateResult AuthenticateResult { get; } 30 | public IEnumerable Clients { get; } = new List(); 31 | } 32 | } -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using IdentityServer4.Models; 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | namespace IdentityServerHost.Quickstart.UI 6 | { 7 | public static class Extensions 8 | { 9 | /// 10 | /// Checks if the redirect URI is for a native client. 11 | /// 12 | /// 13 | public static bool IsNativeClient(this AuthorizationRequest context) 14 | { 15 | return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal) 16 | && !context.RedirectUri.StartsWith("http", StringComparison.Ordinal); 17 | } 18 | 19 | public static IActionResult LoadingPage(this Controller controller, string viewName, string redirectUri) 20 | { 21 | controller.HttpContext.Response.StatusCode = 200; 22 | controller.HttpContext.Response.Headers["Location"] = ""; 23 | 24 | return controller.View(viewName, new RedirectViewModel { RedirectUrl = redirectUri }); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Grants/GrantsViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | 8 | namespace IdentityServerHost.Quickstart.UI 9 | { 10 | public class GrantsViewModel 11 | { 12 | public IEnumerable Grants { get; set; } 13 | } 14 | 15 | public class GrantViewModel 16 | { 17 | public string ClientId { get; set; } 18 | public string ClientName { get; set; } 19 | public string ClientUrl { get; set; } 20 | public string ClientLogoUrl { get; set; } 21 | public string Description { get; set; } 22 | public DateTime Created { get; set; } 23 | public DateTime? Expires { get; set; } 24 | public IEnumerable IdentityGrantNames { get; set; } 25 | public IEnumerable ApiGrantNames { get; set; } 26 | } 27 | } -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Home/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using IdentityServer4.Models; 6 | 7 | namespace IdentityServerHost.Quickstart.UI 8 | { 9 | public class ErrorViewModel 10 | { 11 | public ErrorViewModel() 12 | { 13 | } 14 | 15 | public ErrorViewModel(string error) 16 | { 17 | Error = new ErrorMessage { Error = error }; 18 | } 19 | 20 | public ErrorMessage Error { get; set; } 21 | } 22 | } -------------------------------------------------------------------------------- /IdentityServer4Demo/Quickstart/Home/HomeController.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using IdentityServer4.Services; 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.Extensions.Logging; 10 | using System.Threading.Tasks; 11 | 12 | namespace IdentityServerHost.Quickstart.UI 13 | { 14 | [SecurityHeaders] 15 | [AllowAnonymous] 16 | public class HomeController : Controller 17 | { 18 | private readonly IIdentityServerInteractionService _interaction; 19 | private readonly IWebHostEnvironment _environment; 20 | private readonly ILogger _logger; 21 | 22 | public HomeController(IIdentityServerInteractionService interaction, IWebHostEnvironment environment, ILogger logger) 23 | { 24 | _interaction = interaction; 25 | _environment = environment; 26 | _logger = logger; 27 | } 28 | 29 | public IActionResult Index() 30 | { 31 | return View(); 32 | } 33 | 34 | /// 35 | /// Shows the error page 36 | /// 37 | public async Task Error(string errorId) 38 | { 39 | var vm = new ErrorViewModel(); 40 | 41 | // retrieve error details from identityserver 42 | var message = await _interaction.GetErrorContextAsync(errorId); 43 | if (message != null) 44 | { 45 | vm.Error = message; 46 | } 47 | 48 | return View("Error", vm); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /IdentityServer4Demo/Views/Account/AccessDenied.cshtml: -------------------------------------------------------------------------------- 1 |  2 |
3 |
4 |

Access Denied

5 |

You do not have access to that resource.

6 |
7 |
-------------------------------------------------------------------------------- /IdentityServer4Demo/Views/Account/LoggedOut.cshtml: -------------------------------------------------------------------------------- 1 | @model LoggedOutViewModel 2 | 3 | @{ 4 | // set this so the layout rendering sees an anonymous user 5 | ViewData["signed-out"] = true; 6 | } 7 | 8 |
9 |

10 | Logout 11 | You are now logged out 12 |

13 | 14 | @if (Model.PostLogoutRedirectUri != null) 15 | { 16 |
17 | Click here to return to the 18 | @Model.ClientName application. 19 |
20 | } 21 | 22 | @if (Model.SignOutIframeUrl != null) 23 | { 24 | 25 | } 26 |
27 | 28 | @section scripts 29 | { 30 | @if (Model.AutomaticRedirectAfterSignOut) 31 | { 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Views/Account/Logout.cshtml: -------------------------------------------------------------------------------- 1 | @model LogoutViewModel 2 | 3 |
4 |
5 |

Logout

6 |

Would you like to logut of IdentityServer?

7 |
8 | 9 |
10 | 11 |
12 | 13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Views/Device/Success.cshtml: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |

Success

5 |

You have successfully authorized the device

6 |
7 |
8 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Views/Device/UserCodeCapture.cshtml: -------------------------------------------------------------------------------- 1 | @model string 2 | 3 |
4 |
5 |

User Code

6 |

Please enter the code displayed on your device.

7 |
8 | 9 | 10 | 11 |
12 |
13 |
14 |
15 | 16 | 17 |
18 | 19 | 20 |
21 |
22 |
23 |
24 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Views/Diagnostics/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model DiagnosticsViewModel 2 | 3 |
4 |
5 |

Authentication Cookie

6 |
7 | 8 |
9 |
10 |
11 |
12 |

Claims

13 |
14 |
15 |
16 | @foreach (var claim in Model.AuthenticateResult.Principal.Claims) 17 | { 18 |
@claim.Type
19 |
@claim.Value
20 | } 21 |
22 |
23 |
24 |
25 | 26 |
27 |
28 |
29 |

Properties

30 |
31 |
32 |
33 | @foreach (var prop in Model.AuthenticateResult.Properties.Items) 34 | { 35 |
@prop.Key
36 |
@prop.Value
37 | } 38 | @if (Model.Clients.Any()) 39 | { 40 |
Clients
41 |
42 | @{ 43 | var clients = Model.Clients.ToArray(); 44 | for(var i = 0; i < clients.Length; i++) 45 | { 46 | @clients[i] 47 | if (i < clients.Length - 1) 48 | { 49 | , 50 | } 51 | } 52 | } 53 |
54 | } 55 |
56 |
57 |
58 |
59 |
60 |
61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | 3 | @{ 4 | var error = Model?.Error?.Error; 5 | var errorDescription = Model?.Error?.ErrorDescription; 6 | var request_id = Model?.Error?.RequestId; 7 | } 8 | 9 |
10 |
11 |

Error

12 |
13 | 14 |
15 |
16 |
17 | Sorry, there was an error 18 | 19 | @if (error != null) 20 | { 21 | 22 | 23 | : @error 24 | 25 | 26 | 27 | if (errorDescription != null) 28 | { 29 |
@errorDescription
30 | } 31 | } 32 |
33 | 34 | @if (request_id != null) 35 | { 36 |
Request Id: @request_id
37 | } 38 |
39 |
40 |
41 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Views/Shared/Redirect.cshtml: -------------------------------------------------------------------------------- 1 | @model RedirectViewModel 2 | 3 |
4 |
5 |

You are now being returned to the application

6 |

Once complete, you may close this tab.

7 |
8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | IdentityServer4 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | @RenderBody() 21 |
22 | 23 | 24 | 25 | 26 | @RenderSection("scripts", required: false) 27 | 28 | 29 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Views/Shared/_Nav.cshtml: -------------------------------------------------------------------------------- 1 | @using IdentityServer4.Extensions 2 | 3 | @{ 4 | string name = null; 5 | if (!true.Equals(ViewData["signed-out"])) 6 | { 7 | name = Context.User?.GetDisplayName(); 8 | } 9 | } 10 | 11 | 34 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Views/Shared/_ScopeListItem.cshtml: -------------------------------------------------------------------------------- 1 | @model ScopeViewModel 2 | 3 |
  • 4 | 24 | @if (Model.Required) 25 | { 26 | (required) 27 | } 28 | @if (Model.Description != null) 29 | { 30 | 33 | } 34 |
  • -------------------------------------------------------------------------------- /IdentityServer4Demo/Views/Shared/_ValidationSummary.cshtml: -------------------------------------------------------------------------------- 1 | @if (ViewContext.ModelState.IsValid == false) 2 | { 3 |
    4 | Error 5 |
    6 |
    7 | } -------------------------------------------------------------------------------- /IdentityServer4Demo/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using IdentityServerHost.Quickstart.UI 2 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 3 | -------------------------------------------------------------------------------- /IdentityServer4Demo/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /IdentityServer4Demo/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "KeyVault": { 3 | "BaseUrl": "https://identityserverdemo.vault.azure.net/" 4 | } 5 | } -------------------------------------------------------------------------------- /IdentityServer4Demo/web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /IdentityServer4Demo/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | .body-container { 2 | margin-top: 60px; 3 | padding-bottom: 40px; } 4 | 5 | .welcome-page li { 6 | list-style: none; 7 | padding: 4px; } 8 | 9 | .logged-out-page iframe { 10 | display: none; 11 | width: 0; 12 | height: 0; } 13 | 14 | .grants-page .card { 15 | margin-top: 20px; 16 | border-bottom: 1px solid lightgray; } 17 | .grants-page .card .card-title { 18 | font-size: 120%; 19 | font-weight: bold; } 20 | .grants-page .card .card-title img { 21 | width: 100px; 22 | height: 100px; } 23 | .grants-page .card label { 24 | font-weight: bold; } 25 | -------------------------------------------------------------------------------- /IdentityServer4Demo/wwwroot/css/site.min.css: -------------------------------------------------------------------------------- 1 | .body-container{margin-top:60px;padding-bottom:40px;}.welcome-page li{list-style:none;padding:4px;}.logged-out-page iframe{display:none;width:0;height:0;}.grants-page .card{margin-top:20px;border-bottom:1px solid #d3d3d3;}.grants-page .card .card-title{font-size:120%;font-weight:bold;}.grants-page .card .card-title img{width:100px;height:100px;}.grants-page .card label{font-weight:bold;} -------------------------------------------------------------------------------- /IdentityServer4Demo/wwwroot/css/site.scss: -------------------------------------------------------------------------------- 1 | .body-container { 2 | margin-top: 60px; 3 | padding-bottom:40px; 4 | } 5 | 6 | .welcome-page { 7 | li { 8 | list-style: none; 9 | padding: 4px; 10 | } 11 | } 12 | 13 | .logged-out-page { 14 | iframe { 15 | display: none; 16 | width: 0; 17 | height: 0; 18 | } 19 | } 20 | 21 | .grants-page { 22 | .card { 23 | margin-top: 20px; 24 | border-bottom: 1px solid lightgray; 25 | 26 | .card-title { 27 | img { 28 | width: 100px; 29 | height: 100px; 30 | } 31 | 32 | font-size: 120%; 33 | font-weight: bold; 34 | } 35 | 36 | label { 37 | font-weight: bold; 38 | } 39 | } 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /IdentityServer4Demo/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture/blazor-webassembly-demo/6ae30b2b2d877d1169ec02bc3d5ca05c74b490d6/IdentityServer4Demo/wwwroot/favicon.ico -------------------------------------------------------------------------------- /IdentityServer4Demo/wwwroot/icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture/blazor-webassembly-demo/6ae30b2b2d877d1169ec02bc3d5ca05c74b490d6/IdentityServer4Demo/wwwroot/icon.jpg -------------------------------------------------------------------------------- /IdentityServer4Demo/wwwroot/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture/blazor-webassembly-demo/6ae30b2b2d877d1169ec02bc3d5ca05c74b490d6/IdentityServer4Demo/wwwroot/icon.png -------------------------------------------------------------------------------- /IdentityServer4Demo/wwwroot/js/signin-redirect.js: -------------------------------------------------------------------------------- 1 | window.location.href = document.querySelector("meta[http-equiv=refresh]").getAttribute("data-url"); 2 | -------------------------------------------------------------------------------- /IdentityServer4Demo/wwwroot/js/signout-redirect.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("load", function () { 2 | var a = document.querySelector("a.PostLogoutRedirectUri"); 3 | if (a) { 4 | window.location = a.href; 5 | } 6 | }); 7 | -------------------------------------------------------------------------------- /IdentityServer4Demo/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2019 Twitter, Inc. 4 | Copyright (c) 2011-2019 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /IdentityServer4Demo/wwwroot/lib/bootstrap/scss/_alert.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Base styles 3 | // 4 | 5 | .alert { 6 | position: relative; 7 | padding: $alert-padding-y $alert-padding-x; 8 | margin-bottom: $alert-margin-bottom; 9 | border: $alert-border-width solid transparent; 10 | @include border-radius($alert-border-radius); 11 | } 12 | 13 | // Headings for larger alerts 14 | .alert-heading { 15 | // Specified to prevent conflicts of changing $headings-color 16 | color: inherit; 17 | } 18 | 19 | // Provide class for links that match alerts 20 | .alert-link { 21 | font-weight: $alert-link-font-weight; 22 | } 23 | 24 | 25 | // Dismissible alerts 26 | // 27 | // Expand the right padding and account for the close button's positioning. 28 | 29 | .alert-dismissible { 30 | padding-right: $close-font-size + $alert-padding-x * 2; 31 | 32 | // Adjust close link position 33 | .close { 34 | position: absolute; 35 | top: 0; 36 | right: 0; 37 | padding: $alert-padding-y $alert-padding-x; 38 | color: inherit; 39 | } 40 | } 41 | 42 | 43 | // Alternate styles 44 | // 45 | // Generate contextual modifier classes for colorizing the alert. 46 | 47 | @each $color, $value in $theme-colors { 48 | .alert-#{$color} { 49 | @include alert-variant(theme-color-level($color, $alert-bg-level), theme-color-level($color, $alert-border-level), theme-color-level($color, $alert-color-level)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /IdentityServer4Demo/wwwroot/lib/bootstrap/scss/_badge.scss: -------------------------------------------------------------------------------- 1 | // Base class 2 | // 3 | // Requires one of the contextual, color modifier classes for `color` and 4 | // `background-color`. 5 | 6 | .badge { 7 | display: inline-block; 8 | padding: $badge-padding-y $badge-padding-x; 9 | @include font-size($badge-font-size); 10 | font-weight: $badge-font-weight; 11 | line-height: 1; 12 | text-align: center; 13 | white-space: nowrap; 14 | vertical-align: baseline; 15 | @include border-radius($badge-border-radius); 16 | @include transition($badge-transition); 17 | 18 | @at-root a#{&} { 19 | @include hover-focus() { 20 | text-decoration: none; 21 | } 22 | } 23 | 24 | // Empty badges collapse automatically 25 | &:empty { 26 | display: none; 27 | } 28 | } 29 | 30 | // Quick fix for badges in buttons 31 | .btn .badge { 32 | position: relative; 33 | top: -1px; 34 | } 35 | 36 | // Pill badges 37 | // 38 | // Make them extra rounded with a modifier to replace v3's badges. 39 | 40 | .badge-pill { 41 | padding-right: $badge-pill-padding-x; 42 | padding-left: $badge-pill-padding-x; 43 | @include border-radius($badge-pill-border-radius); 44 | } 45 | 46 | // Colors 47 | // 48 | // Contextual variations (linked badges get darker on :hover). 49 | 50 | @each $color, $value in $theme-colors { 51 | .badge-#{$color} { 52 | @include badge-variant($value); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /IdentityServer4Demo/wwwroot/lib/bootstrap/scss/_breadcrumb.scss: -------------------------------------------------------------------------------- 1 | .breadcrumb { 2 | display: flex; 3 | flex-wrap: wrap; 4 | padding: $breadcrumb-padding-y $breadcrumb-padding-x; 5 | margin-bottom: $breadcrumb-margin-bottom; 6 | @include font-size($breadcrumb-font-size); 7 | list-style: none; 8 | background-color: $breadcrumb-bg; 9 | @include border-radius($breadcrumb-border-radius); 10 | } 11 | 12 | .breadcrumb-item { 13 | // The separator between breadcrumbs (by default, a forward-slash: "/") 14 | + .breadcrumb-item { 15 | padding-left: $breadcrumb-item-padding; 16 | 17 | &::before { 18 | display: inline-block; // Suppress underlining of the separator in modern browsers 19 | padding-right: $breadcrumb-item-padding; 20 | color: $breadcrumb-divider-color; 21 | content: escape-svg($breadcrumb-divider); 22 | } 23 | } 24 | 25 | // IE9-11 hack to properly handle hyperlink underlines for breadcrumbs built 26 | // without `